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:
Create with defaults:
>>> from doppler.analyzer import Specan
>>> obj = Specan(fs=2.048e6, span=200e3, rbw=500.0, src_center=0.0, center=0.0, offset_db=0.0, full_scale=1.0, bits=0, window="kaiser", navg=1)
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.
Without out=, the returned array is a view into a buffer reused on the next call (see execute_max_out() to size an out= buffer for an independent, alias-free result).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex64]
|
cf32 input block (C-only; the binding passes it). |
required |
out
|
NDArray[float32]
|
Caller-provided output buffer, at least max(execute_max_out(), len(x)) elements. |
...
|
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 samples
True
>>> frame = sa.execute(np.zeros(65536, dtype=np.complex64))
>>> frame.shape, frame.dtype
((801,), dtype('float32'))
execute_max_out
¶
Max output length execute() can produce for the current state. Use to size the out= buffer.
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 |
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.