Skip to content

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.

void psd_accumulate (
    psd_state_t * state,
    const float complex * x,
    size_t x_len
) 

Parameters:

  • state Must be non-NULL.
  • x Complex baseband samples (cf32).
  • x_len Number of samples in x.
>>> 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.

void psd_accumulate_real (
    psd_state_t * state,
    const float * x,
    size_t x_len
) 

Parameters:

  • state Must be non-NULL.
  • x Real samples (f32).
  • x_len Number of samples in x.

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.

size_t psd_band_power (
    psd_state_t * state,
    const double * bands,
    size_t bands_len,
    float * out
) 

Parameters:

  • state Must be non-NULL.
  • bands Flat [lo,hi,...] band edges, Hz.
  • bands_len Number of edge values (2 * n_bands).
  • out Destination, 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).

size_t psd_band_power_max_out (
    psd_state_t * state
) 


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:

  • n Window / frame length in samples. Must be >= 2.
  • fs Sample rate in Hz (used for dB/Hz and band frequencies).
  • window Window index: 0 = Hann, 1 = Kaiser, 2 = Blackman-Harris.
  • beta Kaiser beta (ignored for Hann/Blackman-Harris).
  • pad Zero-pad factor (>= 1); nfft = next_pow2(n * pad).
  • full_scale Amplitude that reads 0 dBFS in the dB getters (> 0). Ignored when bits > 0.
  • bits ADC depth: when > 0, sets full_scale = 2^(bits-1) (the single definition of the dBFS reference); 0 = use full_scale directly.
  • mode Averaging mode index (0=mean, 1=exp, 2=maxhold, 3=minhold).
  • alpha EMA 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.

void psd_destroy (
    psd_state_t * state
) 

Parameters:

  • state May be NULL (no-op).

function psd_get_state

void psd_get_state (
    const psd_state_t * state,
    void * blob
) 

function psd_noise_floor

Noise-floor estimate: median of the averaged dB spectrum.

double psd_noise_floor (
    psd_state_t * state
) 

Returns:

Median dB level (0 if empty).


function psd_occupied_bw

Occupied bandwidth in Hz holding fraction of the total power.

double psd_occupied_bw (
    psd_state_t * state,
    double fraction
) 

Parameters:

  • state Must be non-NULL.
  • fraction Power 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.

size_t psd_power_onesided (
    psd_state_t * state,
    size_t cap,
    float * out
) 

Parameters:

  • state Must be non-NULL.
  • cap Caller buffer capacity (must be >= nfft/2 + 1).
  • out Destination, 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.

size_t psd_power_onesided_max_out (
    psd_state_t * state
) 


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.

size_t psd_power_twosided (
    psd_state_t * state,
    size_t cap,
    float * out
) 

Parameters:

  • state Must be non-NULL.
  • cap Caller buffer capacity (must be >= nfft).
  • out Destination, 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.

size_t psd_power_twosided_max_out (
    psd_state_t * state
) 


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.

size_t psd_psd_db (
    psd_state_t * state,
    size_t n,
    float * out
) 

Parameters:

  • state Must be non-NULL.
  • n Caller buffer capacity (ignored; buffer is pre-sized to n).
  • out Destination, 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.

size_t psd_psd_db_max_out (
    psd_state_t * state
) 


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.

size_t psd_psd_dbhz (
    psd_state_t * state,
    size_t n,
    float * out
) 

>>> 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.

size_t psd_psd_dbhz_max_out (
    psd_state_t * state
) 


function psd_reset

Discard the running average; the next accumulate re-seeds it.

void psd_reset (
    psd_state_t * state
) 

Parameters:

  • state Must be non-NULL.

function psd_set_state

int psd_set_state (
    psd_state_t * state,
    const void * blob
) 

function psd_sfdr

Spurious-free dynamic range in dB from the two strongest peaks.

double psd_sfdr (
    psd_state_t * state,
    float min_db
) 

Parameters:

  • state Must be non-NULL.
  • min_db Minimum 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.

double psd_snr (
    psd_state_t * state,
    double lo_hz,
    double hi_hz
) 

Parameters:

  • state Must be non-NULL.
  • lo_hz Band lower edge, Hz.
  • hi_hz Band upper edge, Hz.

Returns:

SNR in dB (0 if empty).


function psd_state_bytes

size_t psd_state_bytes (
    const psd_state_t * state
) 

function psd_total_band_power

Total integrated power across all bands in dB.

double psd_total_band_power (
    psd_state_t * state,
    const double * bands,
    size_t bands_len
) 

Parameters:

  • state Must be non-NULL.
  • bands Flat [lo,hi,...] band edges, Hz.
  • bands_len Number 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_MAGIC `DP_FOURCC ('P','S','D',' ')`

define PSD_STATE_VERSION

#define PSD_STATE_VERSION `1u`


The documentation for this class was generated from the following file native/inc/psd/psd_core.h