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 = False, 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) 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. 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, optional) – Force fine frequency search even when frequency is provided. Default is False. Set to True to refine provided frequencies.
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 to exclude in calibration. Default is 1 (fundamental only, no harmonic exclusion). Higher values exclude more harmonics from the error term.
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. Array-valued entries are returned as a single array for single-dataset input or as a list of arrays for multi-dataset input.- Return type:
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', 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’
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
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: