Fundamental Utilities (fundamentals)
The fundamentals module provides core utility functions and signal processing tools used across the toolbox.
Sine Fitting
- adctoolbox.fit_sine_4param(data, frequency_estimate=None, max_iterations=1, tolerance=1e-09)[source]
Fit sine wave: y = A*cos(wt) + B*sin(wt) + C.
- Parameters:
data – Input signal (1D or 2D array).
frequency_estimate – Initial normalized frequency (0 to 0.5). If None, estimated via FFT.
max_iterations – Iterations for frequency refinement.
tolerance – Convergence threshold for frequency updates.
- Returns:
- fitted_signal: Reconstructed sine wave
residuals: Data - fitted_signal
frequency: Normalized frequency (0 to 0.5)
amplitude: sqrt(A² + B²)
phase: atan2(-B, A) in radians
dc_offset: DC component
rmse: Root mean square error
For 2D input, all values (except fitted_signal, residuals) are 1D arrays.
- Return type:
Dictionary with fitted parameters
Frequency Utilities
- adctoolbox.find_coherent_frequency(fs, fin_target, n_fft, force_odd=True, search_radius=200)[source]
Calculate the precise coherent input frequency and bin index.
Supports Undersampling (Fin > Fs/2).
- Parameters:
- Returns:
(fin_actual, best_bin) - Coherent frequency and corresponding bin index
- Return type:
- Raises:
ValueError – If no valid coherent frequency is found within search radius
Examples
>>> fin, bin_idx = find_coherent_frequency(800e6, 100e6, 8192) >>> print(f"Coherent frequency: {fin/1e6:.6f} MHz at bin {bin_idx}")
- adctoolbox.estimate_frequency(data, frequency_estimate=None, fs=1.0)[source]
Estimate the physical fundamental frequency (Hz) of a signal.
This is a wrapper around the robust fit_sine_4param algorithm. It converts the normalized frequency (0 ~ 0.5) returned by fit_sine into physical frequency (Hz) based on the sampling rate.
- Parameters:
data (np.ndarray) – Input signal data. 1D or 2D array.
fs (float, optional) – Sampling frequency in Hz (default: 1.0)
- Returns:
Estimated frequency in Hz. (Scalar if input is 1D, Array if input is 2D)
- Return type:
float or np.ndarray
Examples
>>> import numpy as np >>> # Generate a 100 MHz sine wave sampled at 800 MHz >>> t = np.arange(8192) / 800e6 >>> signal = np.sin(2 * np.pi * 100e6 * t) >>> freq = estimate_frequency(signal, fs=800e6) >>> print(f"Estimated frequency: {freq/1e6:.2f} MHz") Estimated frequency: 100.00 MHz
- adctoolbox.fold_frequency_to_nyquist(fin, fs)[source]
Calculate the aliased (folded) frequency in the first Nyquist zone.
The aliased frequency is the absolute difference between the input frequency and the nearest integer multiple of the sampling rate.
- Parameters:
- Returns:
Aliased frequency in range [0, Fs/2]
- Return type:
float or np.ndarray
Examples
>>> fold_frequency_to_nyquist(100e6, 800e6) 100000000.0 >>> fold_frequency_to_nyquist(900e6, 800e6) 100000000.0
- adctoolbox.fold_bin_to_nyquist(bin_idx: float, n_fft: int) float[source]
Calculate the aliased bin index in the first Nyquist zone [0, n_fft/2].
For real signals, FFT bins above n_fft/2 are mirrored to the first Nyquist zone. This function handles the wrapping and mirroring.
- Parameters:
- Returns:
Aliased bin index in range [0, n_fft/2]
- Return type:
Examples
>>> fold_bin_to_nyquist(100, 8192) 100.0 >>> fold_bin_to_nyquist(100.5, 8192) # Fractional bins supported 100.5 >>> fold_bin_to_nyquist(5000, 8192) # Above Nyquist, mirrors back 3192.0 >>> fold_bin_to_nyquist(-100, 8192) # Negative wraps around 92.0
Unit Conversions
- adctoolbox.fundamentals.bin_to_freq(bin_idx, fs, n_fft)[source]
Convert FFT bin index to frequency (Hz)
- adctoolbox.fundamentals.freq_to_bin(freq, fs, n_fft)[source]
Convert frequency (Hz) to nearest FFT bin index
SNR/NSD Conversion
- adctoolbox.amplitudes_to_snr(sig_amplitude: float | ndarray, noise_amplitude: float | ndarray, osr: float = 1, return_power: bool = False) float | ndarray | tuple[float | ndarray, ...][source]
Calculate Signal-to-Noise Ratio (SNR) in dB from sine wave peak amplitude and noise RMS.
This function computes SNR, assuming the signal is a pure sine wave and the noise is Gaussian (White Noise).
SNR is calculated based on the power ratio: SNR (dB) = 10 * log10(P_sig / P_noise). When oversampling is used, SNR improves by 10*log10(OSR).
- Parameters:
sig_amplitude (float or array_like) – Sine wave peak amplitude (A), in Volts (V).
noise_amplitude (float or array_like) – Noise RMS amplitude (σ), in Volts (V).
osr (float, optional) – Oversampling ratio. SNR improves by 10*log10(OSR) dB. Default is 1 (no oversampling).
return_power (bool, optional) – If True, returns a tuple containing (snr_db, sig_power, noise_power). Default is False, returning only snr_db.
- Returns:
snr_db (float or ndarray) – The calculated SNR in dB. Returns np.inf if noise_amplitude is zero.
(snr_db, sig_power, noise_power) (tuple (if return_power=True)) – The SNR in dB, Signal Power (V^2), and Noise Power (V^2), respectively.
Examples
>>> snr = amplitudes_to_snr(sig_amplitude=1.0, noise_amplitude=0.01) >>> print(f"SNR = {snr:.2f} dB") SNR = 40.00 dB
>>> snr, sig_pwr, noise_pwr = amplitudes_to_snr(1.0, 0.01, return_power=True) >>> print(f"SNR = {snr:.2f} dB, Signal Power = {sig_pwr:.4f} V^2") SNR = 40.00 dB, Signal Power = 0.5000 V^2
- adctoolbox.snr_to_nsd(snr_db: float | ndarray, fs: float, osr: float = 1.0, psignal_dbfs: float = 0.0) float | ndarray[source]
Convert Signal-to-Noise Ratio (SNR) to Noise Spectral Density (NSD).
This function converts SNR in dB to NSD in dBFS/Hz, given the sampling frequency and oversampling ratio. It assumes a full-scale sine wave signal (0 dBFS) unless specified otherwise.
The relationship is derived from: - Signal power: P_signal = 10^(Psignal_dBFS / 10) - Noise power: P_noise = P_signal / 10^(SNR_dB / 10) - Noise bandwidth: BW = fs / (2 * OSR) - NSD = P_noise / BW (linear scale) - NSD_dBFS/Hz = 10 * log10(NSD)
- Parameters:
snr_db (float or array_like) – Signal-to-Noise Ratio in dB.
fs (float) – Sampling frequency in Hz.
osr (float, optional) – Oversampling ratio. Default is 1.0 (Nyquist sampling). The noise bandwidth is fs / (2 * OSR).
psignal_dbfs (float, optional) – Signal power in dBFS. Default is 0.0 dBFS (full-scale signal).
- Returns:
nsd_dbfs_hz – Noise Spectral Density in dBFS/Hz.
- Return type:
float or ndarray
Examples
>>> # For a full-scale signal with 80 dB SNR, fs=1 MHz, OSR=256 >>> nsd = snr_to_nsd(snr_db=80, fs=1e6, osr=256) >>> print(f"NSD = {nsd:.2f} dBFS/Hz") NSD = -134.08 dBFS/Hz
>>> # For a -6 dBFS signal with 70 dB SNR, fs=100 kHz, Nyquist sampling >>> nsd = snr_to_nsd(snr_db=70, fs=1e5, osr=1, psignal_dbfs=-6) >>> print(f"NSD = {nsd:.2f} dBFS/Hz") NSD = -122.99 dBFS/Hz
- adctoolbox.fundamentals.nsd_to_snr(nsd_dbfs_hz: float | ndarray, fs: float, osr: float = 1.0, psignal_dbfs: float = 0.0) float | ndarray[source]
Convert Noise Spectral Density (NSD) to Signal-to-Noise Ratio (SNR).
This function converts NSD in dBFS/Hz to SNR in dB, given the sampling frequency and oversampling ratio. It assumes a full-scale sine wave signal (0 dBFS) unless specified otherwise.
The relationship is derived from: - NSD in linear scale: NSD_linear = 10^(NSD_dBFS/Hz / 10) - Noise bandwidth: BW = fs / (2 * OSR) - Noise power: P_noise = NSD_linear * BW - Signal power: P_signal = 10^(Psignal_dBFS / 10) - SNR = 10 * log10(P_signal / P_noise)
- Parameters:
nsd_dbfs_hz (float or array_like) – Noise Spectral Density in dBFS/Hz.
fs (float) – Sampling frequency in Hz.
osr (float, optional) – Oversampling ratio. Default is 1.0 (Nyquist sampling). The noise bandwidth is fs / (2 * OSR).
psignal_dbfs (float, optional) – Signal power in dBFS. Default is 0.0 dBFS (full-scale signal).
- Returns:
snr_db – Signal-to-Noise Ratio in dB.
- Return type:
float or ndarray
Examples
>>> # For NSD = -134 dBFS/Hz, fs=1 MHz, OSR=256 >>> snr = nsd_to_snr(nsd_dbfs_hz=-134, fs=1e6, osr=256) >>> print(f"SNR = {snr:.2f} dB") SNR = 79.92 dB
>>> # For NSD = -123 dBFS/Hz, fs=100 kHz, Nyquist sampling, -6 dBFS signal >>> snr = nsd_to_snr(nsd_dbfs_hz=-123, fs=1e5, osr=1, psignal_dbfs=-6) >>> print(f"SNR = {snr:.2f} dB") SNR = 70.01 dB
Validation
Figures of Merit
- adctoolbox.fundamentals.calculate_walden_fom(power, fs, enob)[source]
Calculate Walden Figure of Merit (FoM_w).
Standard metric for medium-resolution ADCs. Lower is better.
Formula: Power / (2^ENOB * Fs)
- Parameters:
power – Power consumption (W)
fs – Sampling frequency (Hz)
enob – Effective number of bits
- Returns:
FoM_w in J/conv-step (Joules per conversion step)
- adctoolbox.fundamentals.calculate_schreier_fom(power, sndr_db, bw)[source]
Calculate Schreier Figure of Merit (FoM_s).
Standard metric for high-resolution / Sigma-Delta ADCs. Higher is better.
Formula: SNDR + 10*log10(BW / Power)
- Parameters:
power – Power consumption (W)
sndr_db – Signal-to-Noise and Distortion Ratio (dB)
bw – Signal bandwidth (Hz)
- Returns:
FoM_s in dB
- adctoolbox.fundamentals.calculate_thermal_noise_limit(cap_pf, v_fs=1.0)[source]
Calculate maximum achievable SNR limited by kT/C noise.
Thermal noise sets the fundamental limit for switched-capacitor circuits and sample-and-hold amplifiers.
- Parameters:
cap_pf – Sampling capacitance in pF
v_fs – Full-scale voltage (Vpp), default 1.0V
- Returns:
Maximum SNR in dB
- Theory:
Noise power = kT/C
Signal power (sine) = (Vfs/2)^2 / 2 = Vfs^2 / 8
SNR = 10*log10(P_signal / P_noise)
- adctoolbox.fundamentals.calculate_jitter_limit(freq, jitter_rms_sec)[source]
Calculate maximum achievable SNR limited by aperture jitter.
Sampling jitter creates phase noise that limits SNR, especially at high input frequencies.
Formula: SNR = -20 * log10(2 * pi * fin * tj)
- Parameters:
freq – Input frequency (Hz)
jitter_rms_sec – RMS jitter in seconds
- Returns:
Maximum SNR in dB