Python Spectrum Analyzer API¶
doppler.analyzer is a single CPython extension exposing Specan — a
streaming spectrum analyzer driven by the instrument parameters an operator
already knows (center, span, RBW, reference level) instead of the DSP knobs
(window length, Kaiser beta, zero-pad) underneath them.
It is the C-first home for the natural-parameter → DSP mapping: a Specan
composes the DDC tuner/decimator and the
PSD averaging-PSD core,
so the whole chain lives in C exactly once and the
doppler.specan application is a thin display/transport
shell over it.
Specan¶
import numpy as np
from doppler.analyzer import Specan
from doppler.spectral import find_peaks_f32
# 200 kHz span, 500 Hz RBW around DC of a 2.048 MHz cf32 stream
sa = Specan(fs=2.048e6, span=200e3, rbw=500.0, center=0.0)
sa.fs_out, sa.n, sa.nfft, sa.display_size # derived DSP grid
sa.rbw, sa.beta # realised RBW + the Kaiser beta
rng = np.random.default_rng(0) # example cf32 source
iq_stream = [(rng.standard_normal(1 << 16)
+ 1j * rng.standard_normal(1 << 16)).astype(np.complex64)
for _ in range(4)]
for chunk in iq_stream: # any cf32 block size
db = sa.execute(chunk.astype(np.complex64))
if db is None: # not enough samples for a frame
continue
# db is a DC-centred dB display band, length sa.display_size.
# bin i → center + (i − display_size/2)·fs_out/nfft Hz
peaks = find_peaks_f32(db, 5, -60.0) # peaks compose on the trace
sa.retune(50e3) # cheap, seamless LO retune (no rebuild)
sa.destroy()
The constructor maps the instrument parameters to the DSP grid: the span sets
the decimation rate (fs_out = span·1.28), the RBW sets the window length
(coarse) and the Kaiser beta (fine, solved so the window ENBW realises the
requested RBW), and the display is cropped to the central ±span/2 band. Changing
the center is a cheap retune (the same instance keeps its
filter history); changing the span or RBW alters the decimation rate and
window length, so build a new Specan.
fs, span and rbw are required — omitting them raises TypeError
rather than constructing an unusable analyzer. The display reads in dBFS
against the PSD core's 0-dBFS reference — set it with
bits (an ADC depth → 2**(bits-1)) or full_scale, the same single-source
knob the measurement analyzers use. offset_db is an additive offset applied on
top (e.g. a dBm calibration the application computes from a reference level).
navg averages that many segments per emitted frame
(navg=1 is a responsive single periodogram, larger navg trades update rate
for a smoother, lower-variance floor). Peaks are intentionally not computed in
the core — compose find_peaks_f32 on the returned dB band.
Specan
¶
Create a natural-parameter spectrum analyzer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fs
|
float
|
Input sample rate (Hz). Must be > 0. |
required |
span
|
float
|
Display span (Hz). Must be > 0. |
required |
rbw
|
float
|
Resolution bandwidth (Hz). Must be > 0. |
required |
src_center
|
float
|
Source center frequency (Hz); the input band is centred here, so the analyzer mixes (center − src_center) to DC. |
0.0
|
center
|
float
|
Desired display center frequency (Hz). |
0.0
|
offset_db
|
float
|
Additive dB offset on the display spectrum, applied on top of dBFS (e.g. a dBm calibration the application computes from a reference level). |
0.0
|
full_scale
|
float
|
Amplitude that reads 0 dBFS (> 0). Ignored if bits > 0. |
1.0
|
bits
|
int
|
ADC depth: bits>0 sets the 0-dBFS reference to 2^(bits-1) in the shared PSD core (the single source of truth for the dBFS reference). |
0
|
window
|
Literal['hann', 'kaiser']
|
Window index: 0 = Hann, 1 = Kaiser (RBW-trimmable). |
"kaiser"
|
navg
|
int
|
Segments averaged per emitted frame (>= 1). |
1
|
Examples:
>>> from doppler.analyzer import Specan
>>> sa = Specan(fs=2.048e6, span=200e3, rbw=500.0)
>>> sa.fs_out
256000.0
>>> sa.nfft == 2 * sa.n
True
execute
¶
Mix, decimate, average; return one DC-centred dB display frame, or None.
Feeds x through the Ddc, buffers the decimated output, and once
n·navg decimated samples are available windows + FFTs + averages them
into a fresh frame, crops the central ±span/2 band and writes it in dB
(+ ref_db). Returns 0 (writing nothing) until a frame is ready — the
binding maps that to Python None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex64]
|
cf32 input block (C-only; the binding passes it). |
required |
out
|
NDArray[float32] | None
|
Display-spectrum buffer, dB (C-only). |
None
|
Returns:
| Type | Description |
|---|---|
NDArray[float32]
|
Display bins written (disp_n), or 0 if no frame is ready yet. |
Examples:
>>> from doppler.analyzer import Specan
>>> import numpy as np
>>> sa = Specan(fs=2.048e6, span=200e3, rbw=500.0, navg=1)
>>> sa.execute(np.zeros(64, dtype=np.complex64)) is None # too few
True
>>> frame = sa.execute(np.zeros(65536, dtype=np.complex64))
>>> frame.shape, frame.dtype
((801,), dtype('float32'))
execute_max_out
¶
Output capacity hint for specan_execute(); equals disp_n.
Returns:
| Type | Description |
|---|---|
int
|
Output. |
retune
¶
Move the display center frequency (seamless LO retune; no rebuild).
Updates the Ddc LO phase increment (seamless across blocks — no resampler or window reset) and drops pending samples so the next frame reflects only the new tuning. Changing the span or RBW requires a destroy + create (the decimation rate and window length change).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center
|
float
|
New display center frequency (Hz). |
required |
state_bytes
¶
Size in bytes of this object's serialized state.
The exact length get_state returns and set_state requires. It
depends on how the object was constructed (state arrays are sized at
construction), so read it from the instance rather than assuming a
constant.
Raises RuntimeError if the Specan has already been destroyed.
Returns:
| Type | Description |
|---|---|
int
|
Byte length of one serialized state blob. |
get_state
¶
Serialize this object's mutable state to bytes.
Captures exactly the state that evolves as the object runs, so a blob taken now and restored later resumes from this point. Construction parameters are not included: restore into an object built the same way.
The blob is opaque and always state_bytes() long. Its layout is an
implementation detail of the C core and is not a stable format across
builds.
Raises RuntimeError if the Specan has already been destroyed.
Returns:
| Type | Description |
|---|---|
bytes
|
Opaque snapshot, |
set_state
¶
Restore mutable state from a get_state() blob.
Overwrites the live state in place; the object keeps the parameters it
was constructed with. Length is validated against state_bytes()
before the blob is handed to the C core, and the core may reject it as
well.
Raises TypeError if blob is not bytes, ValueError if its
length differs from state_bytes() or the core rejects it, and
RuntimeError if the Specan has already been destroyed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blob
|
bytes
|
A |
required |
destroy
¶
Release the underlying C resources immediately.
Ordinarily unnecessary: the resources are freed when the object is garbage-collected. Call this to release them at a definite point instead, or use the object as a context manager, which calls it on exit.
Idempotent: calling it again on an already-released object does
nothing. Every other method raises RuntimeError once it has run.
__enter__
¶
Enter a context manager, returning this object.
Lets a Specan be used in a with statement so its C resources are
released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
Specan
|
This same object, not a copy. |
__exit__
¶
__exit__(
exc_type: object | None = ...,
exc: object | None = ...,
tb: object | None = ...,
) -> None
Exit a context manager, releasing the Specan.
Equivalent to calling destroy(). Returns None, so an exception
raised inside the with body propagates normally; this never
suppresses one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc_type
|
object | None
|
Exception class, or None. Ignored. |
...
|
exc
|
object | None
|
Exception instance, or None. Ignored. |
...
|
tb
|
object | None
|
Traceback object, or None. Ignored. |
...
|
See also¶
- Power Spectra & Measurements guide — the
time → PSD → measurementspipeline and the natural-parameter section. - Python: spectral API — the
PSDaveraging core aSpecancomposes, plusfind_peaks_f32. - Spectral & Measurement API Map — the whole
time → PSD → measurements/displayre-architecture in one diagram. - Python: DDC — the tuner/decimator front end.
- Spectrum Analyzer app — the
doppler.specandisplay shell built onSpecan.
Related pages¶
Guides — Power Spectra & Measurements Design — API taxonomy: the DSP building-block hierarchy and its naming axis, Spectral & Measurement API Map