File psd_core.h¶
FileList > inc > psd > psd_core.h
Go to the source code of this file
PSD — averaging power-spectral-density estimator (Welch's method) and spectral measurement suite. More...
#include "clib_common.h"#include "dp_state.h"#include "jm_perf.h"#include "fft/fft_core.h"#include "acc_trace/acc_trace_core.h"
Classes¶
| Type | Name |
|---|---|
| struct | psd_state_t PSD state. Allocate with psd_create() . |
Public Functions¶
| Type | Name |
|---|---|
| void | psd_accumulate (psd_state_t * state, const float complex * x, size_t x_len) Window, FFT and fold complex baseband frames into the average. Processes floor(n_in / n) full frames; a trailing partial frame is ignored. |
| void | psd_accumulate_real (psd_state_t * state, const float * x, size_t x_len) Window, zero-pad, FFT and fold real frames into the average. The real-input counterpart to psd_accumulate() : each length-n frame is windowed, zero-padded to nfft, transformed and folded as a DC-centred two-sided power spectrum (a real frame is Hermitian, so the +k and -k bins carry equal power). Read the one-sided fold withpsd_power_onesided() . Processes floor(n_in / n) full frames. |
| size_t | psd_band_power (psd_state_t * state, const double * bands, size_t bands_len, float * out) Integrated power per band in dB. bands is a flat array of[lo0, hi0, lo1, hi1, ...] band edges in Hz; the output holds one dB value per band (n_bands = bands_len / 2). Edges are clamped to the analysed span; a band fully outside the span integrates to the dB floor. Returns 0 before any frame is accumulated. |
| size_t | psd_band_power_max_out (psd_state_t * state) Output capacity hint for band_power(); 0 (binding sizes from bands). |
| psd_state_t * | psd_create (size_t n, double fs, int window, float beta, size_t pad, double full_scale, size_t bits, int mode, double alpha) Create an averaging PSD estimator. |
| void | psd_destroy (psd_state_t * state) Destroy a PSD instance and release all memory. |
| void | psd_get_state (const psd_state_t * state, void * blob) |
| double | psd_noise_floor (psd_state_t * state) Noise-floor estimate: median of the averaged dB spectrum. |
| double | psd_occupied_bw (psd_state_t * state, double fraction) Occupied bandwidth in Hz holding fraction of the total power. |
| size_t | psd_power_onesided (psd_state_t * state, size_t cap, float * out) Averaged linear power, one-sided (length nfft/2 + 1). Folds the DC-centred two-sided estimate onto [0, fs/2] : the DC and Nyquist bins are kept as-is, every interior bin is the sum of its +k and -k halves (so a real-input tone reads 2*avg|X[k] |^2 / cg^2 there). Coherent-gain normalised; full_scale is NOT applied. Returns 0 before any accumulate. |
| size_t | psd_power_onesided_max_out (psd_state_t * state) Output capacity hint for psd_power_onesided() ; equals nfft/2+1. |
| size_t | psd_power_twosided (psd_state_t * state, size_t cap, float * out) Averaged linear power, DC-centred two-sided (length nfft). Coherent-gain normalised ( out[k] = avg|X[k] |^2 / cg^2); full_scale is NOT applied (callers that want a dBFS reference divide by full_scale^2). This is the raw spectral estimate the measurement kernels integrate over. Returns 0 (and writes nothing) before any frame is accumulated. |
| size_t | psd_power_twosided_max_out (psd_state_t * state) Output capacity hint for psd_power_twosided() ; equals nfft. |
| size_t | psd_psd_db (psd_state_t * state, size_t n, float * out) Averaged power spectrum in dB, DC-centred. Normalised by window coherent gain so a full-scale tone reads its true power (a unit-amplitude tone peaks near 0 dB). Returns 0 (Python None) before any frame is accumulated. |
| size_t | psd_psd_db_max_out (psd_state_t * state) Output capacity hint for psd_db(); equals nfft. |
| size_t | psd_psd_dbhz (psd_state_t * state, size_t n, float * out) Averaged power spectral density in dB/Hz, DC-centred. Normalised by fs * sum(w^2) (ENBW-aware), the standard one-sided-free PSD scaling. Differs from psd_db() by the constant10*log10(cg^2/(fs*s2)) . Returns 0 (Python None) before any frame is accumulated. |
| size_t | psd_psd_dbhz_max_out (psd_state_t * state) Output capacity hint for psd_dbhz(); equals n. |
| void | psd_reset (psd_state_t * state) Discard the running average; the next accumulate re-seeds it. |
| int | psd_set_state (psd_state_t * state, const void * blob) |
| double | psd_sfdr (psd_state_t * state, float min_db) Spurious-free dynamic range in dB from the two strongest peaks. |
| double | psd_snr (psd_state_t * state, double lo_hz, double hi_hz) In-band SNR in dB: peak level in [lo_hz, hi_hz] minus the noise floor. |
| size_t | psd_state_bytes (const psd_state_t * state) |
| double | psd_total_band_power (psd_state_t * state, const double * bands, size_t bands_len) Total integrated power across all bands in dB. |
Macros¶
| Type | Name |
|---|---|
| define | PSD_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc) ('P','S','D',' ') |
| define | PSD_STATE_VERSION 1u |
Detailed Description¶
A stateful, C-first periodogram averager (Welch's method). It composes the existing pieces of the library rather than re-implementing them: an fft_state_t forward plan, a spectral window (Hann or Kaiser) with its coherent gain and ENBW, an acc_trace_state_t per-bin power averager (mean / EMA / max-hold / min-hold), and the spectral free functions (magnitude_db_cf32, find_peaks_f32, obw_from_power, noise_floor_db) for the derived measurements.
Feed complex baseband frames with psd_accumulate(); each length-n frame is windowed, FFT'd, converted to power, fftshifted to DC-centred order and folded into the running average. Then read: * psd_psd_db() : averaged power spectrum, dB (peak reads tone power) * psd_psd_dbhz() : averaged PSD, dB/Hz (ENBW / fs normalised) * psd_band_power() / psd_total_band_power() : integrated band power, dB * psd_occupied_bw() : occupied bandwidth, Hz * psd_noise_floor() / psd_snr() / psd_sfdr() : level statistics, dB
All spectra are DC-centred (fftshift), matching find_peaks_f32's bin -> frequency convention (bin i maps to (i - n/2)/n in normalised frequency, so spectral peaks are obtained idiomatically with find_peaks_f32(w.psd_db(), n_peaks, min_db)).
Lifecycle: create -> (accumulate / reset)* -> (measurement getters)* -> destroy
Public Functions Documentation¶
function psd_accumulate¶
Window, FFT and fold complex baseband frames into the average. Processes floor(n_in / n) full frames; a trailing partial frame is ignored.
Parameters:
stateMust be non-NULL.xComplex baseband samples (cf32).x_lenNumber of samples inx.
>>> 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
function psd_accumulate_real¶
Window, zero-pad, FFT and fold real frames into the average. The real-input counterpart to psd_accumulate() : each length-n frame is windowed, zero-padded to nfft, transformed and folded as a DC-centred two-sided power spectrum (a real frame is Hermitian, so the +k and -k bins carry equal power). Read the one-sided fold withpsd_power_onesided() . Processes floor(n_in / n) full frames.
Parameters:
stateMust be non-NULL.xReal samples (f32).x_lenNumber of samples inx.
function psd_band_power¶
Integrated power per band in dB. bands is a flat array of[lo0, hi0, lo1, hi1, ...] band edges in Hz; the output holds one dB value per band (n_bands = bands_len / 2). Edges are clamped to the analysed span; a band fully outside the span integrates to the dB floor. Returns 0 before any frame is accumulated.
Parameters:
stateMust be non-NULL.bandsFlat[lo,hi,...]band edges, Hz.bands_lenNumber of edge values (2 * n_bands).outDestination, at least n_bands float32 elements.
Returns:
n_bands, or 0 if empty.
>>> import numpy as np
>>> from doppler.spectral import PSD
>>> w = PSD(n=64, fs=1.0, window="hann", mode="mean")
>>> w.accumulate(np.ones(64, dtype=np.complex64))
>>> pb = w.band_power(np.array([-0.5, 0.0, 0.0, 0.5]))
>>> pb.shape
(2,)
function psd_band_power_max_out¶
Output capacity hint for band_power(); 0 (binding sizes from bands).
function psd_create¶
Create an averaging PSD estimator.
psd_state_t * psd_create (
size_t n,
double fs,
int window,
float beta,
size_t pad,
double full_scale,
size_t bits,
int mode,
double alpha
)
Parameters:
nWindow / frame length in samples. Must be >= 2.fsSample rate in Hz (used for dB/Hz and band frequencies).windowWindow index: 0 = Hann, 1 = Kaiser, 2 = Blackman-Harris.betaKaiser beta (ignored for Hann/Blackman-Harris).padZero-pad factor (>= 1); nfft = next_pow2(n * pad).full_scaleAmplitude that reads 0 dBFS in the dB getters (> 0). Ignored whenbits> 0.bitsADC depth: when > 0, sets full_scale = 2^(bits-1) (the single definition of the dBFS reference); 0 = usefull_scaledirectly.modeAveraging mode index (0=mean, 1=exp, 2=maxhold, 3=minhold).alphaEMA smoothing factor (exp mode only).
Returns:
Heap-allocated state, or NULL on invalid argument or OOM.
Note:
Caller must call psd_destroy() when done.
>>> 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
function psd_destroy¶
Destroy a PSD instance and release all memory.
Parameters:
stateMay be NULL (no-op).
function psd_get_state¶
function psd_noise_floor¶
Noise-floor estimate: median of the averaged dB spectrum.
Returns:
Median dB level (0 if empty).
function psd_occupied_bw¶
Occupied bandwidth in Hz holding fraction of the total power.
Parameters:
stateMust be non-NULL.fractionPower fraction in (0, 1], e.g. 0.99.
Returns:
Occupied bandwidth in Hz (0 if empty or no power).
function psd_power_onesided¶
Averaged linear power, one-sided (length nfft/2 + 1). Folds the DC-centred two-sided estimate onto [0, fs/2] : the DC and Nyquist bins are kept as-is, every interior bin is the sum of its +k and -k halves (so a real-input tone reads 2*avg|X[k] |^2 / cg^2 there). Coherent-gain normalised; full_scale is NOT applied. Returns 0 before any accumulate.
Parameters:
stateMust be non-NULL.capCaller buffer capacity (must be >= nfft/2 + 1).outDestination, at least nfft/2 + 1 float32 elements.
Returns:
nfft/2 + 1, or 0 if empty.
function psd_power_onesided_max_out¶
Output capacity hint for psd_power_onesided() ; equals nfft/2+1.
function psd_power_twosided¶
Averaged linear power, DC-centred two-sided (length nfft). Coherent-gain normalised ( out[k] = avg|X[k] |^2 / cg^2); full_scale is NOT applied (callers that want a dBFS reference divide by full_scale^2). This is the raw spectral estimate the measurement kernels integrate over. Returns 0 (and writes nothing) before any frame is accumulated.
Parameters:
stateMust be non-NULL.capCaller buffer capacity (must be >= nfft).outDestination, at least nfft float32 elements.
Returns:
nfft, or 0 if empty.
function psd_power_twosided_max_out¶
Output capacity hint for psd_power_twosided() ; equals nfft.
function psd_psd_db¶
Averaged power spectrum in dB, DC-centred. Normalised by window coherent gain so a full-scale tone reads its true power (a unit-amplitude tone peaks near 0 dB). Returns 0 (Python None) before any frame is accumulated.
Parameters:
stateMust be non-NULL.nCaller buffer capacity (ignored; buffer is pre-sized to n).outDestination, at least n float32 elements.
Returns:
n, or 0 if empty.
function psd_psd_db_max_out¶
Output capacity hint for psd_db(); equals nfft.
function psd_psd_dbhz¶
Averaged power spectral density in dB/Hz, DC-centred. Normalised by fs * sum(w^2) (ENBW-aware), the standard one-sided-free PSD scaling. Differs from psd_db() by the constant10*log10(cg^2/(fs*s2)) . Returns 0 (Python None) before any frame is accumulated.
>>> import numpy as np
>>> from doppler.spectral import PSD
>>> w = PSD(n=32, fs=2.0, window="hann", mode="mean")
>>> w.accumulate(np.ones(32, dtype=np.complex64))
>>> a = w.psd_db(); b = w.psd_dbhz()
>>> bool(np.allclose(a - b, (a - b)[0])) # offset is a constant
True
function psd_psd_dbhz_max_out¶
Output capacity hint for psd_dbhz(); equals n.
function psd_reset¶
Discard the running average; the next accumulate re-seeds it.
Parameters:
stateMust be non-NULL.
function psd_set_state¶
function psd_sfdr¶
Spurious-free dynamic range in dB from the two strongest peaks.
Parameters:
stateMust be non-NULL.min_dbMinimum peak level considered, dB.
Returns:
Carrier-minus-highest-spur level in dB (0 if fewer than two peaks).
function psd_snr¶
In-band SNR in dB: peak level in [lo_hz, hi_hz] minus the noise floor.
Parameters:
stateMust be non-NULL.lo_hzBand lower edge, Hz.hi_hzBand upper edge, Hz.
Returns:
SNR in dB (0 if empty).
function psd_state_bytes¶
function psd_total_band_power¶
Total integrated power across all bands in dB.
Parameters:
stateMust be non-NULL.bandsFlat[lo,hi,...]band edges, Hz.bands_lenNumber of edge values (2 * n_bands).
Returns:
Total band power in dB (dB floor if empty).
Macro Definition Documentation¶
define PSD_STATE_MAGIC¶
define PSD_STATE_VERSION¶
The documentation for this class was generated from the following file native/inc/psd/psd_core.h