Skip to content

File tonemeas_core.h

FileList > inc > tonemeas > tonemeas_core.h

Go to the source code of this file

ToneMeasure — single-tone ADC/converter spectral measurement. More...

  • #include "clib_common.h"
  • #include "jm_perf.h"
  • #include "measure/measure_core.h"
  • #include "psd/psd_core.h"
  • #include <complex.h>
  • #include "fft/fft_core.h"
  • #include "spectral/spectral_core.h"
  • #include "acc_trace/acc_trace_core.h"

Classes

Type Name
struct tonemeas_state_t
ToneMeasure state: owned window, FFT plan and analysis scratch.

Public Functions

Type Name
tone_meas_t tonemeas_analyze (tonemeas_state_t * state, const float * x, size_t n_in)
Analyse a real capture into the single-tone metric bag.
tone_meas_t tonemeas_analyze_complex (tonemeas_state_t * state, const float _Complex * x, size_t n_in)
Analyse a complex baseband capture (two-sided spectrum).
tonemeas_state_t * tonemeas_create (size_t n, double fs, size_t n_harmonics, double full_scale, size_t bits, double dynamic_range_db, size_t dc_guard)
Create a ToneMeasure analyser (auto Kaiser window).
void tonemeas_destroy (tonemeas_state_t * state)
Destroy a ToneMeasure analyser.
void tonemeas_reset (tonemeas_state_t * state)
Reset the analyser (a no-op: it holds no state between calls).
size_t tonemeas_spectrum_dbfs (tonemeas_state_t * state, const float * x, size_t x_len, float * out, size_t max_out)
DC-centred dBFS magnitude spectrum of a real capture (length nfft).
size_t tonemeas_spectrum_dbfs_max_out (tonemeas_state_t * state)
Capacity (== nfft) of the spectrum_dbfs output buffer.
time_stats_t tonemeas_time_stats (tonemeas_state_t * state, const float * x, size_t n_in)
Time-domain statistics of a real capture.

Detailed Description

Owns a window + zero-padded FFT and analyses one time-domain capture (real or complex) into the full single-tone metric bag (tone_meas_t). Each component's power is integrated over its window MAIN LOBE and the noise sum excludes the leakage bins around DC, the fundamental and each harmonic — the IEEE Std 1241 method — so a full-scale tone reads ~0 dBFS regardless of where it lands between FFT bins.

Lifecycle: create -> [analyze / analyze_complex / time_stats]* -> destroy

// 16-bit ADC: window auto-picked for ~100 dB dynamic range.
tonemeas_state_t *m = tonemeas_create(8192, 1.0, 8, 1.0, 16, 0.0, 0);
tone_meas_t r = tonemeas_analyze(m, capture, 8192);  // r.enob, r.sfdr_dbc...
tonemeas_destroy(m);

Public Functions Documentation

function tonemeas_analyze

Analyse a real capture into the single-tone metric bag.

tone_meas_t tonemeas_analyze (
    tonemeas_state_t * state,
    const float * x,
    size_t n_in
) 

Returns:

the metric record (by value).

>>> from doppler.measure import ToneMeasure
>>> import numpy as np
>>> n, t = 4096, np.arange(4096)
>>> # full-scale tone at 300 cycles + a 2nd harmonic 40 dB down
>>> x = (np.cos(2*np.pi*300*t/n)
...      + 0.01*np.cos(2*np.pi*600*t/n)).astype(np.float32)
>>> r = ToneMeasure(n=n, fs=1.0).analyze(x)
>>> type(r).__name__
'ToneMetrics'
>>> abs(r.fund_dbfs) < 0.1, round(r.thd, 1)  # 0 dBFS tone, THD -40
(True, -40.0)

function tonemeas_analyze_complex

Analyse a complex baseband capture (two-sided spectrum).

tone_meas_t tonemeas_analyze_complex (
    tonemeas_state_t * state,
    const float _Complex * x,
    size_t n_in
) 

>>> from doppler.measure import ToneMeasure
>>> import numpy as np
>>> i = np.arange(4096)
>>> x = np.exp(2j*np.pi*137*i/4096).astype(np.complex64)
>>> r = ToneMeasure(n=4096, fs=1.0).analyze_complex(x)
>>> round(r.fund_freq, 4), abs(r.fund_dbfs) < 0.2
(0.0334, True)

function tonemeas_create

Create a ToneMeasure analyser (auto Kaiser window).

tonemeas_state_t * tonemeas_create (
    size_t n,
    double fs,
    size_t n_harmonics,
    double full_scale,
    size_t bits,
    double dynamic_range_db,
    size_t dc_guard
) 

The window is always Kaiser; its shape is chosen automatically so the sidelobes sit below the requested dynamic range (see measure_resolve_dr()), keeping the resolution bandwidth as fine as n allows. The realised RBW is reported back in every result (tone_meas_t::rbw_hz).

Parameters:

  • n Capture/frame length (>= 2).
  • fs Sample rate (Hz, > 0).
  • n_harmonics Harmonics to track (k = 2..n_harmonics).
  • full_scale Amplitude that equals 0 dBFS (> 0). Ignored if bits > 0.
  • bits ADC depth: bits>0 sets the 0-dBFS reference to 2^(bits-1) and, unless overridden, the dynamic-range target (6.02*bits + 1.76 + headroom).
  • dynamic_range_db Explicit sidelobe/dynamic-range target (dB); used when > 0, else derived from bits (or a deep default when both are 0).
  • dc_guard Extra bins excluded beyond L around DC.

Returns:

Heap state, or NULL on bad args / allocation failure.

Note:

Caller must tonemeas_destroy() when done.


function tonemeas_destroy

Destroy a ToneMeasure analyser.

void tonemeas_destroy (
    tonemeas_state_t * state
) 

Parameters:

  • state May be NULL.

function tonemeas_reset

Reset the analyser (a no-op: it holds no state between calls).

void tonemeas_reset (
    tonemeas_state_t * state
) 

Every analyze() / analyze_complex() / time_stats() / spectrum_dbfs() call re-windows and re-transforms its own capture from scratch, so there is nothing carried between calls to clear. The method exists only so ToneMeasure honours the same reset() contract as every other doppler object, letting a generic pipeline reset each stage uniformly.

Parameters:

  • state The analyser (left unchanged).
>>> from doppler.measure import ToneMeasure
>>> m = ToneMeasure(n=4096, fs=1.0)
>>> m.reset()            # stateless: provided only for API uniformity
>>> m.reset() is None    # returns nothing; safe to call anytime
True

function tonemeas_spectrum_dbfs

DC-centred dBFS magnitude spectrum of a real capture (length nfft).

size_t tonemeas_spectrum_dbfs (
    tonemeas_state_t * state,
    const float * x,
    size_t x_len,
    float * out,
    size_t max_out
) 

The windowed, zero-padded magnitude spectrum behind the metrics, laid out DC-centred (fftshifted) and normalised to dBFS so it drops straight under an analyzer trace. Use it to eyeball where the fundamental, harmonics and spurs that analyze() quantifies actually sit.

Parameters:

  • state The analyser.
  • x Real time-domain capture (length x_len).
  • x_len Number of input samples.
  • out Destination buffer (length >= max_out).
  • max_out Capacity of out (== nfft).

Returns:

DC-centred dBFS magnitude spectrum, one value per FFT bin (nfft).

>>> from doppler.measure import ToneMeasure
>>> import numpy as np
>>> t = np.arange(4096)
>>> x = np.cos(2*np.pi*300*t/4096).astype(np.float32)  # full-scale
>>> s = ToneMeasure(n=4096, fs=1.0).spectrum_dbfs(x)  # DC-centred dBFS
>>> s.shape                     # zero-padded to next power of two
(8192,)
>>> round(float(s.max()), 1)   # two real images, ~6 dB each
-6.0

function tonemeas_spectrum_dbfs_max_out

Capacity (== nfft) of the spectrum_dbfs output buffer.

size_t tonemeas_spectrum_dbfs_max_out (
    tonemeas_state_t * state
) 


function tonemeas_time_stats

Time-domain statistics of a real capture.

time_stats_t tonemeas_time_stats (
    tonemeas_state_t * state,
    const float * x,
    size_t n_in
) 

>>> from doppler.measure import ToneMeasure
>>> import numpy as np
>>> t = np.arange(4096)
>>> x = (0.8*np.cos(2*np.pi*50*t/4096)).astype(np.float32)
>>> ts = ToneMeasure(n=4096, fs=1.0).time_stats(x)
>>> round(ts.crest_db, 2), round(ts.fs_util_pct, 0)  # crest ~3.01 dB
(3.01, 80.0)


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