File measure_core.h¶
File List > inc > measure > measure_core.h
Go to the documentation of this file
#ifndef MEASURE_CORE_H
#define MEASURE_CORE_H
#include "clib_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ── auto-window design policy ──────────────────────────────────────────────
* The measurement objects pick their Kaiser window automatically: the user
* states a target dynamic range (directly, or implied by the ADC bit depth)
* and the analyser chooses the *minimum* Kaiser beta whose sidelobes sit below
* that range — so window leakage never caps SFDR/SNR — while keeping the main
* lobe (hence resolution bandwidth) as narrow as the data allows. */
/* Internal zero-pad factor (nfft = next_pow2(n * MEASURE_PAD)). */
#define MEASURE_PAD 2u
/* Sidelobe headroom below the ideal converter SNR: a B-bit ADC's spur/noise
* floor sits at -(6.02*B+1.76) dBc, so target sidelobes this much deeper to be
* sure window leakage stays under the floor being measured. */
#define MEASURE_DR_MARGIN_DB 12.0
/* Dynamic-range target when neither `bits` nor an explicit override is given
* (general DUT): deep enough for ~19-bit measurements. */
#define MEASURE_DR_DEFAULT_DB 120.0
/* Extra main-lobe widths excluded past the first null when searching for spurs,
* so a component's near-in sidelobes are never mistaken for a spur. */
#define MEASURE_SPUR_SIDELOBES 1.0
static inline double
measure_dr_from_bits (size_t bits)
{
return 6.02 * (double)bits + 1.76 + MEASURE_DR_MARGIN_DB;
}
static inline double
measure_resolve_dr (double dynamic_range_db, size_t bits)
{
if (dynamic_range_db > 0.0)
return dynamic_range_db;
if (bits > 0)
return measure_dr_from_bits (bits);
return MEASURE_DR_DEFAULT_DB;
}
typedef struct {
double snr; /* 10log10(P_fund / P_noise) */
double sinad; /* 10log10(P_fund / (P_noise + P_harm)) */
double thd; /* 10log10(P_harm / P_fund) [dBc] */
double thd_pct; /* 100*sqrt(P_harm / P_fund) [%] */
double thd_n; /* 10log10((P_noise+P_harm)/P_fund) = -SINAD */
double sfdr_dbc; /* fundamental − worst spur [dBc] */
double sfdr_dbfs; /* full scale − worst spur [dBFS] */
double enob; /* (SINAD − 1.76)/6.02 */
double enob_fs; /* full-scale-corrected ENOB */
double noise_floor_dbfs; /* mean per-bin noise power, dBFS */
double fund_freq; /* fundamental frequency [Hz] */
double fund_dbfs; /* fundamental level [dBFS] */
double worst_spur_freq; /* worst spur frequency [Hz] */
double worst_spur_dbc; /* worst spur level relative to fund [dBc] */
int worst_spur_is_harm;/* 1 if the worst spur is a harmonic */
double rbw_hz; /* resolution bandwidth = enbw*fs/n [Hz] */
double enbw_hz; /* equivalent noise bandwidth [Hz] (= rbw_hz) */
double bin_hz; /* FFT bin spacing = fs/nfft [Hz] */
size_t lobe_bins; /* window main-lobe half-width L [bins] */
size_t n_noise_bins; /* bins counted as noise */
double proc_gain_db; /* FFT processing gain = 10log10(nfft/2) */
double amp_uncert_db; /* amplitude-read uncertainty bound [dB] */
double floor_uncert_db; /* noise-floor standard error [dB] */
} tone_meas_t;
typedef struct {
double rms; /* root-mean-square (DC included) */
double peak; /* peak |x − DC| */
double crest_db; /* 20log10(peak_ac / rms_ac) */
double papr_db; /* peak-to-average power ratio (= crest) */
double dc_offset; /* mean(x) */
double fs_util_pct; /* 100 * max|x| / full_scale */
} time_stats_t;
typedef struct {
double f1; /* lower tone frequency [Hz] */
double f2; /* upper tone frequency [Hz] */
double p1_dbfs; /* lower tone level [dBFS] */
double p2_dbfs; /* upper tone level [dBFS] */
double imd2_dbc; /* 2nd-order product (f2-f1) vs mean tone [dBc] */
double imd3_dbc; /* worst 3rd-order product vs mean tone [dBc] */
double imd2_freq; /* 2nd-order product frequency [Hz] */
double imd3_lo_freq; /* 2f1-f2 product frequency [Hz] */
double imd3_hi_freq; /* 2f2-f1 product frequency [Hz] */
double toi_dbfs; /* third-order intercept [dBFS] */
double soi_dbfs; /* second-order intercept [dBFS] */
double rbw_hz; /* resolution bandwidth [Hz] */
} imd_meas_t;
typedef struct {
double npr_db; /* 10log10(mean in-band PSD / mean notch PSD) */
double inband_psd_dbfs; /* mean in-band noise power per bin (dBFS) */
double notch_psd_dbfs; /* mean power that folded into the notch (dBFS) */
size_t n_inband_bins; /* bins averaged in the active band */
size_t n_notch_bins; /* bins averaged inside the notch */
double rbw_hz; /* resolution bandwidth (Hz) */
} npr_meas_t;
/* ── capture-planning helpers ──────────────────────────────────────────────
* Pure functions that answer "how much data, and at what frequency?" for an
* IEEE-1241 single-tone test. See docs/design/measurement-suite.md. */
size_t measure_min_samples(double fs, double target_rbw, size_t bits,
double dynamic_range_db, int complex_input);
size_t measure_rec_nfft(size_t n, size_t pad);
double measure_proc_gain(size_t nfft);
double dp_coherent_freq(double fs, double f_target, size_t N);
#ifdef __cplusplus
}
#endif
#endif /* MEASURE_CORE_H */