Python Correlation & Detection API¶
The doppler.spectral module is a single CPython extension over the C spectral
core. Its FFT engines are documented on the FFT page; this page
covers the rest of the module — correlation (Corr, Corr2D), streaming
detection (CorrDetector, CorrDetector2D), and the spectral helper functions
(windows, magnitude, peak-finding).
For the statistical-detection side (probability of detection, thresholds, dwell
sizing), see the Detection Statistics page. For how
PSD (below) anchors the dBFS reference for every measurement and display
consumer, see the Spectral & Measurement API Map.
Correlation¶
Corr is a 1-D FFT correlator with coherent integrate-and-dump: it pre-computes
conj(FFT(ref)) once at construction, so each execute() costs two FFTs and n
complex multiplies. With dwell == 1 every call dumps; with a larger dwell,
the accumulator coherently integrates that many frames before returning a result
(and returns None in between).
import numpy as np
from doppler.spectral import Corr
ref = np.exp(2j * np.pi * 0.1 * np.arange(1024)).astype(np.complex64)
corr = Corr(ref, dwell=1)
frame = ref + 0.1 * (np.random.randn(1024) + 1j * np.random.randn(1024))
out = corr.execute(frame.astype(np.complex64)) # ndarray on a dump, else None
lag = int(np.argmax(np.abs(out))) # correlation peak position
Corr2D is the 2-D analogue over an ny × nx grid (flat row-major arrays).
Corr
¶
Allocate a 1-D FFT correlator with coherent integrate-and-dump. Pre-computes conj(FFT(ref)) once at construction so each execute() call costs only two FFTs and n complex multiplies. ref may be freed after this returns. With dwell == 1 every call produces output; with larger values the accumulator absorbs dwell frames before dumping.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ref
|
NDArray[complex64]
|
Reference signal, CF32, length n. |
required |
dwell
|
int
|
Integration depth; must be >= 1. Pass 1 for immediate output on every call. |
1
|
nthreads
|
int
|
Accepted for API compatibility; ignored. |
1
|
n_out
|
int
|
Inverse/output length; 0 => native (n). Must be >= n. A larger value zero-pads the cross-spectrum before the inverse, returning the band-limited (Dirichlet) interpolation of the correlation on a finer length-n_out grid — same peak, sub-bin lag resolution. Native is bit-exact and allocates no extra buffer. |
0
|
Examples:
>>> from doppler.spectral import Corr
>>> import numpy as np
>>> ref = np.zeros(4, dtype=np.complex64); ref[0] = 1.0
>>> corr = Corr(ref=ref, dwell=1, nthreads=1)
>>> corr.n, corr.dwell, corr.count
(4, 1, 0)
reset
¶
Zero the accumulator and reset the integration counter to 0. Equivalent to starting a fresh dwell cycle without tearing down the FFT plans. Does NOT recompute ref_spec; use corr_set_ref() to replace the reference.
Examples:
execute
¶
Correlate one frame and optionally dump the coherent accumulator. Runs: forward FFT → pointwise multiply with ref_spec → accumulate the cross-spectrum; on dump, inverse FFT → normalise (÷ n). Accumulating in the frequency domain and inverting once is exactly the per-frame inverse summed, by linearity of the IFFT — valid because the dwell is coherent (a complex sum); a non-coherent (magnitude) integration could not defer the inverse. On the dwell-th call out is written, the accumulator is zeroed, and the counter resets; the function returns n_out. All other calls return 0 and leave out unmodified. In Python, a dump returns an ndarray and a no-dump returns None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex64]
|
Input. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[complex64]
|
n_out on a dump call (or max_out if smaller), 0 otherwise (None in Python). |
Examples:
>>> from doppler.spectral import Corr
>>> import numpy as np
>>> ref = np.zeros(4, dtype=np.complex64); ref[0] = 1.0
>>> corr = Corr(ref=ref, dwell=2)
>>> x = np.ones(4, dtype=np.complex64)
>>> corr.execute(x) is None # frame 1 — no dump yet
True
>>> corr.execute(x).tolist() # frame 2 — dump
[(2+0j), (2+0j), (2+0j), (2+0j)]
execute_max_out
¶
Maximum output samples per execute call (== n_out).
Returns:
| Type | Description |
|---|---|
int
|
Output. |
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 Corr 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 Corr 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 Corr 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 Corr be used in a with statement so its C resources are
released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
Corr
|
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 Corr.
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. |
...
|
Corr2D
¶
Allocate a 2-D FFT correlator with coherent integrate-and-dump. Two-dimensional extension of corr_create(). The reference is a flat row-major ny×nx CF32 array; its conjugate spectrum is pre-computed once so each execute() call costs two 2-D FFTs plus ny*nx complex multiplies. The Python wrapper requires ref to be a 2-D ndarray with shape (ny, nx); it passes a flat view to C.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ref
|
NDArray[complex64]
|
Reference image, 2-D (ny, nx) CF32 ndarray in Python. |
required |
dwell
|
int
|
Integration depth; must be >= 1. |
1
|
nthreads
|
int
|
Accepted for API compatibility; ignored. |
1
|
ny_out
|
int
|
Inverse/output rows; 0 => native (ny). Must be >= ny. A larger output zero-pads the cross-spectrum before the inverse, returning the band-limited (Dirichlet) interpolation of the correlation on a finer (ny_out, nx_out) grid — same peak, sub-bin resolution. Native is bit-exact and allocates no extra buffers. |
0
|
nx_out
|
int
|
Inverse/output columns; 0 => native (nx). Must be >= nx. |
0
|
Examples:
>>> from doppler.spectral import Corr2D
>>> import numpy as np
>>> ref = np.zeros((4, 4), dtype=np.complex64); ref[0, 0] = 1.0
>>> c = Corr2D(ref=ref, dwell=1, nthreads=1)
>>> c.ny, c.nx, c.dwell, c.count
(4, 4, 1, 0)
reset
¶
Zero the accumulator and reset the integration counter to 0. Equivalent to starting a fresh dwell cycle without rebuilding FFT plans or recomputing ref_spec.
Examples:
execute
¶
Correlate one 2-D frame and optionally dump the coherent accumulator. Runs the 2-D pipeline: FFT2 → pointwise multiply with ref_spec → accumulate the cross-spectrum; on dump, IFFT2 → normalise (÷ nynx). Accumulating in the frequency domain and inverting once is exactly the per-frame inverse summed, by linearity of the IFFT — valid because the dwell is coherent (a complex sum); a non-coherent (magnitude) integration could not defer the inverse. The Python wrapper accepts a (ny, nx) CF32 ndarray; a dump returns a flat length-nynx ndarray, a no-dump returns None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex64]
|
Input. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[complex64]
|
ny*nx on a dump (or max_out if smaller), 0 otherwise (None in Python). |
Examples:
>>> from doppler.spectral import Corr2D
>>> import numpy as np
>>> ref = np.zeros((2, 2), dtype=np.complex64); ref[0, 0] = 1.0
>>> c = Corr2D(ref=ref, dwell=2)
>>> x = np.ones((2, 2), dtype=np.complex64)
>>> c.execute(x) is None # frame 1 — no dump
True
>>> c.execute(x).tolist() # frame 2 — dump
[(2+0j), (2+0j), (2+0j), (2+0j)]
execute_max_out
¶
Maximum output samples per execute call (always == ny*nx).
Returns:
| Type | Description |
|---|---|
int
|
Output. |
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 Corr2D 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 Corr2D 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 Corr2D 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 Corr2D be used in a with statement so its C resources are
released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
Corr2D
|
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 Corr2D.
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. |
...
|
Streaming detection¶
CorrDetector wraps a correlator with a double-mapped ring buffer so you can
push arbitrary-sized chunks. After each integrate-and-dump it compares the
peak-to-noise test statistic against threshold and emits a detection result
when it passes (threshold = 0.0 fires on every dump). The ring capacity is
next_pow2(max(n, 512)) complex samples.
import numpy as np
from doppler.spectral import CorrDetector
ref = np.exp(2j * np.pi * 0.1 * np.arange(1024)).astype(np.complex64)
det = CorrDetector(ref, dwell=4, threshold=12.0) # ~12 dB peak-to-noise
def stream_chunks(): # a real CF32 source
for _ in range(4):
yield (np.random.randn(1024)
+ 1j * np.random.randn(1024)).astype(np.complex64)
for chunk in stream_chunks(): # any chunk size
for hit in det.push(chunk.astype(np.complex64)):
print("detection:", hit) # (lag, peak, noise, stat)
CorrDetector2D is the 2-D streaming detector over a grid.
CorrDetector
¶
Allocate a 1-D streaming signal detector backed by an FFT correlator. Combines a corr_state_t with a double-mapped ring buffer so that arbitrary chunk sizes can be pushed. After every int-dump the peak-to-noise test statistic is compared against threshold; a det_result_t is emitted when it passes. Setting threshold to 0.0 unconditionally fires on every dump. The ring capacity is next_pow2(max(n, 512)) complex samples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ref
|
NDArray[complex64]
|
Reference signal, CF32 ndarray of length n. |
required |
dwell
|
int
|
Int-dump depth; must be >= 1. |
1
|
noise_lo
|
int
|
Lower noise bin index (inclusive, 0-based). |
0
|
noise_hi
|
int
|
Upper noise bin index (inclusive, < n). |
n-1
|
noise_mode
|
Literal['mean', 'median', 'min', 'max']
|
Noise aggregation: "mean", "median", "min", or "max". |
"mean"
|
threshold
|
float
|
Test-stat gate; 0.0 = always emit. |
0.0
|
nthreads
|
int
|
Accepted for API compatibility; ignored. |
1
|
Examples:
>>> from doppler.spectral import CorrDetector
>>> import numpy as np
>>> ref = np.zeros(8, dtype=np.complex64); ref[0] = 1.0
>>> det = CorrDetector(ref=ref, dwell=1, noise_lo=1, noise_hi=7,
... noise_mode="mean", threshold=0.0)
>>> det.n, det.dwell, det.ring_cap
(8, 1, 512)
last_corr
property
¶
The correlation vector from the most recent push() that produced a result (None before that). This is a zero-copy view into a buffer owned by the detector and reused every push() -- the next push() (even one that doesn't produce a result) overwrites it in place. Copy the array before the next push() if you need to retain it.
reset
¶
Reset the correlator, ring buffer, and last-corr flag. Discards any partial frame buffered in the ring and zeroes the coherent accumulator. Equivalent to starting fresh from the same reference without rebuilding any internal object.
Examples:
>>> from doppler.spectral import CorrDetector
>>> import numpy as np
>>> ref = np.zeros(8, dtype=np.complex64); ref[0] = 1.0
>>> det = CorrDetector(ref=ref, dwell=1, noise_lo=1, noise_hi=7,
... noise_mode="mean", threshold=0.0)
>>> _ = det.push(np.ones(8, dtype=np.complex64))
>>> det.reset()
>>> det.count
0
push
¶
Stream an arbitrary-length CF32 chunk through the detector pipeline. Writes samples into the ring buffer, drains complete n-sample frames through the correlator, and on every int-dump computes the test statistic peak_mag / noise_est. Detections that pass the threshold are appended to the Python return list as (lag, peak_mag, noise_est, test_stat) tuples. In Python the result is always a list, even when empty.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
complex
|
Input. |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[int, float, float, float]]
|
Number of det_result_t entries written to result. |
Examples:
>>> from doppler.spectral import CorrDetector
>>> import numpy as np
>>> ref = np.zeros(8, dtype=np.complex64); ref[0] = 1.0
>>> det = CorrDetector(ref=ref, dwell=1, noise_lo=1, noise_hi=7,
... noise_mode="mean", threshold=0.0)
>>> results = det.push(np.ones(8, dtype=np.complex64))
>>> len(results)
1
>>> lag, peak, noise, stat = results[0]
>>> lag, round(peak, 4), round(noise, 4), round(stat, 4)
(0, 1.0, 1.0, 1.0)
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 CorrDetector 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 CorrDetector 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 CorrDetector 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 CorrDetector be used in a with statement so its C resources
are released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
CorrDetector
|
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 CorrDetector.
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. |
...
|
CorrDetector2D
¶
Allocate a 2-D streaming signal detector backed by a 2-D correlator. Two-dimensional extension of detector_create(). Input frames are flat row-major CF32 arrays of length ny*nx streamed through a ring buffer. On every int-dump the peak flat index is decomposed into (row, col) and a det_result2d_t is emitted when test_stat > threshold. The Python wrapper accepts a (ny, nx) CF32 ndarray for both ref and the push input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ref
|
NDArray[complex64]
|
2-D reference image, (ny, nx) CF32 ndarray in Python. |
required |
dwell
|
int
|
Int-dump depth; must be >= 1. |
1
|
noise_lo
|
int
|
Lower flat-index noise bin (inclusive, 0-based). |
0
|
noise_hi
|
int
|
Upper flat-index noise bin (inclusive, < ny*nx). |
ny*nx-1
|
noise_mode
|
Literal['mean', 'median', 'min', 'max']
|
Noise aggregation: "mean", "median", "min", or "max". |
"mean"
|
threshold
|
float
|
Test-stat gate; 0.0 = always emit. |
0.0
|
nthreads
|
int
|
Accepted for API compatibility; ignored. |
1
|
Examples:
>>> from doppler.spectral import CorrDetector2D
>>> import numpy as np
>>> ref = np.zeros((4, 4), dtype=np.complex64); ref[0, 0] = 1.0
>>> det = CorrDetector2D(ref=ref, dwell=1, noise_lo=1, noise_hi=15,
... noise_mode="mean", threshold=0.0)
>>> det.ny, det.nx, det.n, det.dwell
(4, 4, 16, 1)
last_corr
property
¶
The correlation vector from the most recent push() that produced a result (None before that). This is a zero-copy view into a buffer owned by the detector and reused every push() -- the next push() (even one that doesn't produce a result) overwrites it in place. Copy the array before the next push() if you need to retain it.
reset
¶
Reset the 2-D correlator, ring buffer, and last-corr flag. Discards any partial frame buffered in the ring and zeroes the coherent accumulator. The reference spectrum and FFT plans are preserved.
Examples:
>>> from doppler.spectral import CorrDetector2D
>>> import numpy as np
>>> ref = np.zeros((4, 4), dtype=np.complex64); ref[0, 0] = 1.0
>>> det = CorrDetector2D(ref=ref, dwell=1, noise_lo=1, noise_hi=15,
... noise_mode="mean", threshold=0.0)
>>> _ = det.push(np.ones((4, 4), dtype=np.complex64))
>>> det.reset()
>>> det.count
0
push
¶
Stream an arbitrary-length CF32 chunk through the 2-D detector. Identical to detector_push() except frames are ny*nx complex samples and each detection event carries (row, col) for the peak location instead of a single lag index. In Python the result is always a list of (row, col, peak_mag, noise_est, test_stat) tuples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
complex
|
Input. |
required |
Returns:
| Type | Description |
|---|---|
list[tuple[int, int, float, float, float]]
|
Number of det_result2d_t entries written to result. |
Examples:
>>> from doppler.spectral import CorrDetector2D
>>> import numpy as np
>>> ref = np.zeros((4, 4), dtype=np.complex64); ref[0, 0] = 1.0
>>> det = CorrDetector2D(ref=ref, dwell=1, noise_lo=1, noise_hi=15,
... noise_mode="mean", threshold=0.0)
>>> results = det.push(np.ones((4, 4), dtype=np.complex64))
>>> len(results)
1
>>> row, col, peak, noise, stat = results[0]
>>> row, col, round(peak, 4), round(noise, 4), round(stat, 4)
(0, 0, 1.0, 1.0, 1.0)
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 CorrDetector2D 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 CorrDetector2D 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 CorrDetector2D 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 CorrDetector2D be used in a with statement so its C resources
are released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
CorrDetector2D
|
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 CorrDetector2D.
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. |
...
|
Averaging PSD & measurements¶
PSD is a stateful Welch-method (averaging) power-spectral-density estimator —
the single PSD core the measurement suite also consumes (see
the Power Spectra & Measurements guide for the usage
walk-through). A capture longer than n is split into floor(len/n) segments;
each is windowed, zero-padded to nfft = next_pow2(n·pad), FFT'd, converted
to power, fftshifted to DC-centred order and folded into a running average
(AccTrace, mode of "mean" / "exp" / "maxhold"
/ "minhold"). Feed complex baseband with accumulate() or real input with
accumulate_real().
import numpy as np
from doppler.spectral import PSD, find_peaks_f32
cf32_capture = (np.random.randn(8192)
+ 1j * np.random.randn(8192)).astype(np.complex64)
w = PSD(n=1024, fs=1e6, window="kaiser", beta=8.0,
pad=2, full_scale=1.0, bits=0, mode="mean") # bits>0 -> 2**(bits-1)
w.accumulate(cf32_capture) # or w.accumulate_real(f32_capture)
w.n, w.nfft # 1024, 2048 (= next_pow2(1024 * 2))
# display spectra (DC-centred, dBFS w.r.t. full_scale)
psd_db = w.psd_db() # averaged power spectrum, dB
psd_dbhz = w.psd_dbhz() # PSD, dB/Hz (ENBW / fs normalised)
# raw linear power (cg²-normalised; full_scale NOT applied)
two = w.power_twosided() # length nfft, DC-centred
one = w.power_onesided() # length nfft//2 + 1, folded to [0, fs/2]
# band / level statistics
per_band = w.band_power(np.array([-2e5, -1e5, 1e5, 2e5])) # dB per band
total = w.total_band_power(np.array([-2e5, -1e5, 1e5, 2e5]))
obw = w.occupied_bw(0.99) # occupied bandwidth, Hz
nf = w.noise_floor() # median dB level
snr = w.snr(-1e5, 1e5) # peak-in-band minus noise floor, dB
sfdr = w.sfdr(min_db=-120.0) # spurious-free dynamic range, dB
# spectral peaks compose with the free function on the averaged trace:
peaks = find_peaks_f32(w.psd_db(), n_peaks=5, min_db=-60.0)
The pad factor interpolates the spectrum (finer bin spacing, not finer
resolution); full_scale sets the 0-dBFS reference for the dB getters only — the
linear power_* accessors are unaffected. All spectra are DC-centred, matching
find_peaks_f32's bin → frequency convention. The PSD getters return None
until the first frame is accumulated.
PSD
¶
Create an averaging PSD estimator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Window / frame length in samples. Must be >= 2. |
1024
|
fs
|
float
|
Sample rate in Hz (used for dB/Hz and band frequencies). |
1.0
|
window
|
Literal['hann', 'kaiser', 'blackman-harris']
|
Window index: 0 = Hann, 1 = Kaiser, 2 = Blackman-Harris. |
"hann"
|
beta
|
float
|
Kaiser beta (ignored for Hann/Blackman-Harris). |
0.0
|
pad
|
int
|
Zero-pad factor (>= 1); nfft = next_pow2(n * pad). |
1
|
full_scale
|
float
|
Amplitude that reads 0 dBFS in the dB getters (> 0). Ignored when bits
|
1.0
|
bits
|
int
|
ADC depth: when > 0, sets full_scale = 2^(bits-1) (the single definition of the dBFS reference); 0 = use full_scale directly. |
0
|
mode
|
Literal['mean', 'exp', 'maxhold', 'minhold']
|
Averaging mode index (0=mean, 1=exp, 2=maxhold, 3=minhold). |
"mean"
|
alpha
|
float
|
EMA smoothing factor (exp mode only). |
0.1
|
Examples:
>>> from doppler.spectral import PSD
>>> w = PSD(n=1024, fs=1.0e6, window="kaiser", beta=8.0, mode="mean")
>>> w.n, w.fs
(1024, 1000000.0)
>>> round(w.rbw / (w.fs / w.n), 3) == round(w.enbw, 3)
True
accumulate
¶
Window, FFT and fold floor(n_in/n) cf32 frames into the average.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex64]
|
Complex baseband samples (cf32). |
required |
Examples:
>>> import numpy as np
>>> from doppler.spectral import PSD
>>> n = 64
>>> w = PSD(n=n, fs=1.0, window="hann", mode="mean")
>>> k = 8
>>> x = np.exp(2j*np.pi*k*np.arange(n)/n).astype(np.complex64)
>>> for _ in range(4):
... w.accumulate(x)
>>> psd = w.psd_db()
>>> psd.shape
(64,)
>>> int(np.argmax(psd)) == n // 2 + k
True
>>> w.count
4
accumulate_real
¶
Window, zero-pad, FFT and fold floor(n_in/n) real frames into the average.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[float32]
|
Real samples (f32). |
required |
psd_db
¶
Averaged power spectrum in dB (None before any accumulate).
Returns:
| Type | Description |
|---|---|
NDArray[float32]
|
min(n, max_out), or 0 if empty. |
psd_db_max_out
¶
Output capacity hint for psd_db(); equals nfft.
Returns:
| Type | Description |
|---|---|
int
|
Output. |
psd_dbhz
¶
Averaged power spectral density in dB/Hz (None before any accumulate).
Returns:
| Type | Description |
|---|---|
NDArray[float32]
|
Output. |
Examples:
psd_dbhz_max_out
¶
Output capacity hint for psd_dbhz(); equals n.
Returns:
| Type | Description |
|---|---|
int
|
Output. |
power_twosided
¶
Averaged linear power, DC-centred two-sided (length nfft); cg^2-normalised.
Returns:
| Type | Description |
|---|---|
NDArray[float32]
|
min(nfft, max_out), or 0 if empty. |
power_twosided_max_out
¶
Output capacity hint for psd_power_twosided(); equals nfft.
Returns:
| Type | Description |
|---|---|
int
|
Output. |
power_onesided
¶
Averaged linear power, one-sided fold (length nfft/2+1); cg^2-normalised.
Returns:
| Type | Description |
|---|---|
NDArray[float32]
|
min(nfft/2 + 1, max_out), or 0 if empty. |
power_onesided_max_out
¶
Output capacity hint for psd_power_onesided(); equals nfft/2+1.
Returns:
| Type | Description |
|---|---|
int
|
Output. |
band_power
¶
Integrated power per band in dB; bands = [lo0,hi0,lo1,hi1,...] Hz.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bands
|
NDArray[float64]
|
Flat |
required |
Returns:
| Type | Description |
|---|---|
NDArray[float32]
|
min(n_bands, max_out), or 0 if empty. |
Examples:
band_power_max_out
¶
Output capacity hint for band_power(); 0 (binding sizes from bands).
Returns:
| Type | Description |
|---|---|
int
|
Output. |
total_band_power
¶
Total integrated power across all bands in dB.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bands
|
NDArray[float64]
|
Flat |
required |
Returns:
| Type | Description |
|---|---|
float
|
Total band power in dB (dB floor if empty). |
occupied_bw
¶
Occupied bandwidth in Hz holding the given fraction of total power.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fraction
|
float
|
Power fraction in (0, 1], e.g. 0.99. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Occupied bandwidth in Hz (0 if empty or no power). |
noise_floor
¶
Median of the averaged dB trace (noise-floor estimate).
Returns:
| Type | Description |
|---|---|
float
|
Median dB level (0 if empty). |
snr
¶
Peak-in-band level minus noise floor, in dB.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lo_hz
|
float
|
Band lower edge, Hz. |
required |
hi_hz
|
float
|
Band upper edge, Hz. |
required |
Returns:
| Type | Description |
|---|---|
float
|
SNR in dB (0 if empty). |
sfdr
¶
Spurious-free dynamic range in dB from the top two peaks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_db
|
float
|
Minimum peak level considered, dB. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Carrier-minus-highest-spur level in dB (0 if fewer than two peaks). |
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 PSD 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 PSD 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 PSD 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 PSD be used in a with statement so its C resources are
released deterministically on exit rather than at collection time.
Returns:
| Type | Description |
|---|---|
PSD
|
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 PSD.
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. |
...
|
Spectral helpers¶
Window functions (hann_window, kaiser_window + its kaiser_enbw equivalent
noise bandwidth and kaiser_beta_for_sidelobe window-design helper, and
blackman_harris_window for deep sidelobe rejection ~92 dB),
magnitude conversion to dB (magnitude_db_cf32 /
magnitude_db_cf64), and peak finding (find_peaks_f32) — the building blocks
for a spectrum display.
import numpy as np
from doppler.spectral import (
FFT, hann_window, magnitude_db_cf32, find_peaks_f32,
)
x = (np.random.randn(1024) + 1j * np.random.randn(1024)).astype(np.complex64)
w = np.empty(1024, dtype=np.float32)
hann_window(w) # fill in place
spec = FFT(1024, -1).execute_cf32(x * w) # returns the transform
db = magnitude_db_cf32(spec, lin_floor=1e-12, offset_db=0.0)
peaks = find_peaks_f32(db, n_peaks=5, min_db=-60.0)
hann_window
¶
Fill w with a Hann (raised-cosine) window. Computes w(k) = 0.5*(1 - cos(2π k/(N-1))) for k = 0..N-1. The window tapers smoothly to zero at both endpoints, providing ~31 dB first-sidelobe rejection. Takes no shape parameter; use Kaiser for adjustable roll-off.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
w
|
NDArray[float32]
|
Output buffer modified in-place; must be length >= 1. |
required |
Examples:
kaiser_window
¶
Fill w with a Kaiser window of shape parameter beta. I0 is computed
via the converging power-series expansion. Increasing beta raises
sidelobe attenuation at the cost of a wider main lobe (beta=0 →
rectangular, beta≈6 → ~60 dB sidelobe rejection). The output is
normalised so that w[0] = w[N-1] = I0(0)/I0(beta).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
w
|
NDArray[float32]
|
Output buffer modified in-place; must be length >= 1. |
required |
beta
|
float
|
Window shape parameter (float, >= 0). |
required |
Examples:
kaiser_enbw
¶
Compute the equivalent noise bandwidth of a window in bins. ENBW = N * sum(w²) / (sum(w))² quantifies how many noise bins the window smears into the main lobe. A rectangular window has ENBW = 1.0; tapered windows are > 1.0. Works with any window type, not just Kaiser.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
w
|
NDArray[float32]
|
Float32 window coefficients array; any length >= 1. |
required |
Returns:
| Type | Description |
|---|---|
float
|
ENBW in bins (dimensionless). |
Examples:
kaiser_beta_for_sidelobe
¶
Kaiser beta achieving a target window peak-sidelobe attenuation.
Inverts the Kaiser window-design formula (Kaiser 1974) so the window's own peak sidelobe sits at -atten_db: A > 60 dB : beta = 0.12438 * (A + 6.3) 13.26 < A <= 60 dB : beta = 0.76609(A-13.26)^0.4 + 0.09834(A-13.26) A <= 13.26 dB : beta = 0.0 (rectangular, sidelobes ~ -13.3 dB) Picking the smallest beta meeting a dynamic-range target keeps the main lobe (hence ENBW / resolution bandwidth) as narrow as the requirement allows — the basis of the measurement suite's auto-window selection.
This differs from doppler.resample.kaiser_beta(), which uses the Kaiser FIR-filter formula (A there is a filter stopband ripple, not a window sidelobe — about 13 dB lower for the same beta).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
atten_db
|
float
|
Desired window peak-sidelobe attenuation in dB (positive). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Kaiser beta (>= 0.0). |
Examples:
blackman_harris_window
¶
Fill w with a 4-term Blackman-Harris window. Computes the minimum 4-term Blackman-Harris window: w(k) = 0.35875 - 0.48829cos(2πk/(N-1)) + 0.14128cos(4πk/(N-1)) - 0.01168*cos(6πk/(N-1)) for k = 0..N-1. Provides approximately 92 dB first-sidelobe rejection, far deeper than Hann (~31 dB) or Kaiser at β=8 (~80 dB). Use for quantization and decimation spectra where you need to see low-level artefacts below the noise floor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
w
|
NDArray[float32]
|
Output buffer modified in-place; must be length >= 1. |
required |
Examples:
magnitude_db_cf32
¶
magnitude_db_cf32(
x: NDArray[complex64],
lin_floor: float,
offset_db: float,
) -> NDArray[np.float32]
Convert a CF32 complex spectrum to F32 dB magnitudes. Computes out(k) = 20*log10(max(|x(k)|, lin_floor)) + offset_db for each bin. The lin_floor guard prevents log10(0); a value of 1e-12 corresponds to a -240 dB noise floor. offset_db shifts the entire output for calibration (e.g., normalise to 0 dBFS).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex64]
|
CF32 complex spectrum array, length x_len. |
required |
lin_floor
|
float
|
Linear amplitude floor (must be > 0, e.g. 1e-12). |
required |
offset_db
|
float
|
Calibration offset added to every output bin. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[float32]
|
Output. |
Examples:
magnitude_db_cf64
¶
magnitude_db_cf64(
x: NDArray[complex128],
lin_floor: float,
offset_db: float,
) -> NDArray[np.float32]
Convert a CF64 complex spectrum to F32 dB magnitudes. Double-precision variant of magnitude_db_cf32(). Accepts a CF64 input array and a double lin_floor; output is still F32 because downstream display code typically works in single precision. The formula and offset_db semantics are identical.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
NDArray[complex128]
|
CF64 complex spectrum array, length x_len. |
required |
lin_floor
|
float
|
Linear amplitude floor (double, must be > 0). |
required |
offset_db
|
float
|
Calibration offset added to every output bin. |
required |
Returns:
| Type | Description |
|---|---|
NDArray[float32]
|
Output. |
Examples:
find_peaks_f32
¶
Find up to n_peaks local maxima in a DC-centred F32 dB spectrum.
Three-step algorithm: (1) local-max scan — db[k] > db[k-1] &&
db[k] >= db[k+1] with db[k] > min_db; (2) parabolic interpolation
on each local maximum to produce sub-bin freq_norm accuracy; (3) sort
descending and return the top n_peaks. freq_norm is DC-centred: bin i
maps to freq_norm = (i - N/2) / N so DC (bin N/2) → 0.0 and the first
negative frequency bin → −0.5. The spectrum must have at least 3 bins.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
db
|
NDArray[float32]
|
F32 dB spectrum, DC-centred, length >= 3. |
required |
n_peaks
|
int
|
Maximum number of peaks to return. |
required |
min_db
|
float
|
Amplitude gate; local maxima below this are discarded. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
Number of dp_peak_t entries written to result. |
Examples:
obw_from_power
¶
Obw from power.
Related pages¶
Gallery — CarrierAcquisition: RRC Pulse Shaping, Correlation and Detection, 2-D Acquisition Grid, Gallery, Four WCDMA Carriers — PSD, band_power, AccTrace
Guides — DSSS Burst Acquisition, Power Spectra & Measurements
Design — API taxonomy: the DSP building-block hierarchy and its naming axis, Corr2D: decoupled (interpolated) inverse length, DSSS acquisition: stateless, parallel, dynamics-capable, Design, Measurement Suite — single-tone ADC / spectral metrics, Spectral & Measurement API Map
Contributing — Code coverage, DSSS Primary Use Cases for Code Acquisition Design