Digital Output Analysis (dout)#
The dout module provides tools for analyzing digital ADC outputs and bit-weighted architectures.
Weight Calibration#
- adctoolbox.calibrate_weight_sine(bits: ndarray | list[ndarray], freq: float | ndarray | None = None, force_search: bool | None = None, nominal_weights: ndarray | None = None, harmonic_order: int = 1, learning_rate: float = 0.5, reltol: float = 1e-12, max_iter: int = 100, verbose: int = 0, frequency_policy: str = 'python') dict[source]#
FGCalSine — Foreground calibration using a sinewave input
This function estimates per-bit weights and a DC offset for an ADC by fitting the weighted sum of raw bit columns to a sine series at a given (or estimated) normalized frequency Fin/Fs. Harmonic terms above the fundamental are fitted reference/nuisance terms: they can prevent source or test-chain harmonics from contaminating weight estimation, but they do not remove those harmonics from
calibrated_signal. It optionally performs a coarse and fine frequency search to refine the input tone frequency.Implementation uses a unified pipeline where single-dataset calibration is treated as a special case of multi-dataset calibration (N=1).
- Parameters:
bits (ndarray or list of ndarrays) – Binary data as matrix (N rows by M cols, N is data points, M is bitwidth). Each row is one sample; each column is a bit/segment. Can also be a list of arrays for multi-dataset calibration.
freq (float, array-like, or None, optional) – Normalized frequency Fin/Fs. Default is None (triggers auto frequency search). Use None for automatic frequency search, a float for one frequency shared by all datasets, or an array-like value for per-dataset frequencies in multi-dataset mode.
force_search (bool or None, optional) – Frequency fine-search policy. Default is None, which refines automatically estimated frequencies while keeping explicitly provided frequencies fixed. Set True to refine provided frequencies too, or False to disable fine search unless a zero frequency placeholder remains.
frequency_policy ({"python", "matlab"}, optional) – Coarse frequency estimator used when
freqisNoneor zero."python"preserves the historical Python estimator."matlab"uses a MATLABwcalsin(freq=0)compatible estimator based on nominally reconstructed rank-patched bit prefixes. Explicit nonzero frequencies are not changed by this option. Default is"python".nominal_weights (array-like, optional) – Nominal bit weights (only effective when rank is deficient). Default is 2^(M-1) down to 2^0.
harmonic_order (int, optional) – Number of harmonic terms included in the fitted reference. Default is 1 (fundamental only). Values greater than 1 include H2/H3/… as nuisance terms in
idealand exclude them fromerror. This is useful for source/test-chain harmonic nuisance modeling; it should not be interpreted as proof that ADC harmonic distortion has been removed fromcalibrated_signal.learning_rate (float, optional) – Adaptive learning rate for frequency updates (0..1), default is 0.5.
reltol (float, optional) – Relative error tolerance for convergence, default is 1e-12.
max_iter (int, optional) – Maximum iterations for fine frequency search, default is 100.
verbose (int, optional) – Print frequency search progress (1) or not (0), default is 0.
- Returns:
Calibration result containing
weight,offset,calibrated_signal,ideal,error, andrefined_frequency.idealincludes fitted harmonics up toharmonic_order;erroris the residual after subtracting that fitted reference. The returnedsnr_dbandenobare calibration fitted-residual metrics, not FFT dynamic SNDR/ENOB whenharmonic_orderis greater than 1. Use spectrum analysis oncalibrated_signalfor ADC dynamic SNDR/THD/HDx. Therank_patchentry reports any dropped or merged rank-deficient bit columns. Array-valued entries are returned as a single array for single-dataset input or as a list of arrays for multi-dataset input.initial_frequencyrecords the coarse frequency used before fine search, andfrequency_policyrecords the coarse-estimator policy.The calibrated waveform fields use
scale_convention == "solver_unit_sine": the least-squares solve fixes the fitted fundamental sine magnitude to one. Before interpreting dBFS or NSD against a physical ADC full-scale, rescale the result withscale_calibration_outputand pass an explicit full-scale range to the spectrum analyzer.- Return type:
- adctoolbox.calibration.calibrate_weight_sine_lite(bits: ndarray, freq: float) ndarray[source]#
Minimal calibration at known frequency. Returns normalized weights. Expects well-conditioned binary data (N samples x M bits).
The returned weights are in the same solver-unit-sine convention as
calibrate_weight_sine: the fitted fundamental sine magnitude is fixed to one for identifiability. Apply an explicit scale factor before using the reconstructed waveform for physical dBFS or NSD interpretation.
Bit And Weight Analysis#
analyze_weight_radix returns radix, wgtsca, and effres.
effres is computed from the significant absolute weights as
log2(sum(abs_w_sig) / min(abs_w_sig) + 1). It is a theoretical
weight-list span, not a missing-code, DNL, INL, or SAR reachability proof.
- adctoolbox.analyze_bit_activity(bits: ndarray, create_plot: bool = True, ax=None, title: str | None = None) ndarray[source]#
Analyze and plot the percentage of 1’s in each bit.
Ideal SAR ADC should have 50% activity for all bits. Deviations indicate input signal DC offset or amplitude clipping.
- Parameters:
bits (np.ndarray) – Binary matrix (N x B), N=samples, B=bits (MSB to LSB)
create_plot (bool, default=True) – If True, create bar chart visualization
ax (plt.Axes, optional) – Axes to plot on. If None, uses current axes (plt.gca())
title (str, optional) – Title for the plot. If None, uses default title
- Returns:
Percentage of 1’s for each bit (1D array of length B)
- Return type:
np.ndarray
- adctoolbox.analyze_overflow(raw_code: ndarray, weight: ndarray, ofb: int | None = None, create_plot: bool = True, ax=None, title: str | None = None) tuple[ndarray, ndarray, ndarray, ndarray][source]#
Analyze residue distribution at each bit position for overflow detection.
Calculates normalized residue (remaining bits weighted sum) and detects overflow conditions where residue exceeds [0, 1] range.
- Parameters:
raw_code (np.ndarray) – Digital codes array, shape (N, M) where N=samples, M=bits (MSB first)
weight (np.ndarray) – Weight array for each bit, shape (M,)
ofb (int, optional) – Overflow bit position for overflow detection. Default is M (check at MSB, MATLAB convention: 1=LSB, M=MSB)
create_plot (bool, default=True) – If True, generate residue distribution visualization
ax (plt.Axes, optional) – Axes to plot on. If None, uses current axes (plt.gca())
title (str, optional) – Title for the plot. If None, no title is set
- Returns:
range_min: Minimum normalized residue per bit (shape M,)
range_max: Maximum normalized residue per bit (shape M,)
ovf_percent_zero: Underflow percentage per bit (shape M,)
ovf_percent_one: Overflow percentage per bit (shape M,)
- Return type:
tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]
Notes
A bit segment is the sub-code formed from one bit to the LSB
Residue is normalized by dividing by the sum of weights in the segment
Matches MATLAB ovfchk.m behavior exactly
- adctoolbox.analyze_weight_radix(weights: ndarray, create_plot: bool = True, ax=None, title: str | None = None) dict[source]#
Analyze absolute bit weights, radix ratios, and weight-list resolution.
Pure binary: radix = 2.00. Sub-radix/redundancy: radix < 2.00.
effresis a theoretical resolution estimate from the supplied weight dynamic range, not a missing-code/DNL proof.- Parameters:
weights (np.ndarray) – Bit weights (1D array), nominally from MSB to LSB for radix plotting. Effective-resolution analysis uses sorted absolute magnitudes, so negative trim weights count by magnitude.
create_plot (bool, default=True) – If True, create line plot with radix annotations
ax (plt.Axes, optional) – Axes to plot on. If None, uses current axes (plt.gca())
title (str, optional) – Title for the plot. If None, uses default title
- Returns:
radix:Radix between consecutive input-order weights,
abs(weight[i-1]) / abs(weight[i]). The first entry isNaN.wgtsca:Scale factor that maps the significant absolute weights closest to integer LSB units.
effres:Effective resolution in bits,
log2(sum(abs_w_sig) / min(abs_w_sig) + 1).
- Return type:
Notes
The significant set
abs_w_sigis formed by sortingabs(weights)in descending order, then dropping the tail after the first adjacent ratio>= 3. This excludes very small trim/noise weights fromeffres.effresis useful for questions like “how many bits does this SAR weight list span?” It does not verify SAR decision reachability, code monotonicity, missing codes, DNL/INL, comparator noise, or sampling noise.What to look for in radix values: - Radix = 2.00: Binary scaling (SAR, pure binary) - Radix < 2.00: Redundancy or sub-radix (e.g., 1.5-bit/stage → ~1.90) - Radix > 2.00: Unusual, may indicate calibration error - Consistent pattern: Expected architecture behavior - Random jumps: Calibration errors or bit mismatch
ENOB Analysis#
- adctoolbox.analyze_enob_sweep(bits: ndarray, freq: float | None = None, harmonic_order: int = 1, osr: int = 1, win_type: str = 'hamming', calibration_mode: str = 'prefix_of_full_calibration', frequency_policy: str = 'python', create_plot: bool = True, ax=None, title: str | None = None, verbose: bool = False) tuple[ndarray, ndarray][source]#
Sweep ENOB vs number of bits used for calibration.
Incrementally adds bits (MSB to LSB) and measures ENOB after calibration to understand diminishing returns and optimal bit count.
- Parameters:
bits (np.ndarray) – Binary matrix (N samples x M bits, MSB to LSB order)
freq (float, optional) – Normalized frequency (0-0.5). If None, auto-detect from data
harmonic_order (int, default=1) – Harmonic order for calibrate_weight_sine
osr (int, default=1) – Oversampling ratio for spectrum analysis
win_type (str, default='hamming') – Window function: ‘boxcar’, ‘hann’, ‘hamming’
calibration_mode ({'prefix_of_full_calibration',) – ‘recalibrate_each_subset’}, default=’prefix_of_full_calibration’ ENOB sweep calibration policy.
'prefix_of_full_calibration'calibrates all bits once and sweeps prefixes of the full-weight solution.'recalibrate_each_subset'estimates the frequency once when needed, then recalibrates each bit-prefix subset independently.frequency_policy ({'python', 'matlab'}, default='python') – Coarse frequency estimator passed to
calibrate_weight_sinewhen automatic frequency search is requested.create_plot (bool, default=True) – If True, plot ENOB sweep curve
ax (plt.Axes, optional) – Axes to plot on. If None, uses current axes (plt.gca())
title (str, optional) – Title for the plot. If None, uses default title
verbose (bool, default=False) – If True, print progress messages
- Returns:
enob_sweep: ENOB for each bit count (length M)
n_bits_vec: Bit counts from 1 to M
- Return type:
tuple[np.ndarray, np.ndarray]
Notes
The default
'prefix_of_full_calibration'mode answers the calibration ablation question: “after a full-bit calibration, how much performance remains if lower-bit terms are removed?” Use'recalibrate_each_subset'only when intentionally asking: “if only the first n bits are available, how well can that n-bit subsystem be calibrated?”What to look for in the plot: - Increasing trend: More bits improve resolution - Plateau: Additional bits don’t help (noise/distortion limited) - Decrease: Extra bits add noise/calibration errors
Visualization#
- adctoolbox.plot_residual_scatter(signal: ndarray, bits: ndarray, weights: ndarray | None = None, pairs: list[tuple[int, int]] | None = None, alpha: float | str = 'auto', create_plot: bool = True) dict[source]#
Plot partial-sum residuals of an ADC bit matrix.
For each pair (x_bit, y_bit), computes the residual after subtracting the first x_bit (or y_bit) weighted bits from the input signal, then plots y-residual vs x-residual as a scatter plot.
- Parameters:
signal (np.ndarray) – Ideal input signal to the ADC (1D, length N).
bits (np.ndarray) – Raw ADC output bit matrix (N x M), MSB-first columns.
weights (np.ndarray, optional) – Bit weights (length M). Default: binary [2^(M-1), …, 1].
pairs (list of (int, int), optional) – Pairs of bit indices whose residuals are plotted. Range: 0 (raw signal) to M (residual after all bits). Default: [(0, M), (1, M), …, (M-1, M)].
alpha (float or 'auto', default='auto') – Marker transparency. ‘auto’ scales as clamp(1000/N, 0.1, 1.0).
create_plot (bool, default=True) – If True, create scatter subplots.
- Returns:
‘pairs’: list of (x_bit, y_bit) tuples used ‘residuals_x’: list of 1D arrays, one per pair ‘residuals_y’: list of 1D arrays, one per pair
- Return type: