Dither Calibration#

The public interfaces consume aligned observables; probe injection and device simulation stay with the caller. Use DitherGainTracker for a single-probe, gain-only stage and DitherHDCTracker for the three-PN, nominal-gain-2 HDC stage. Their input conventions differ. The class notes below state each tracker’s observability requirements and accuracy trade-offs; theory, references and complete experiments are in Dither mechanisms.

The HDC tracker runs a per-sample loop. For long captures, install the optional Numba acceleration with pip install "adctoolbox[accel]".

class adctoolbox.DitherGainTracker(*, probe_amplitude, initial_gain, backend_bits, gain_bounds, learning_rate=0.08)[source]#

Bases: object

Blockwise PN-correlation gain estimate followed by exponential averaging.

The known probe probe_amplitude * pn must be injected after the coarse decision and amplified together with the residue. Each block estimates the gain as cov(pn, backend) / (probe_amplitude * var(pn)) and moves the active estimate toward it by learning_rate. No ideal input, true gain, frequency or foreground label is accepted. A block is held, leaving the estimate unchanged, on any backend rail code (selecting only non-clipped samples can bias the correlation), on insufficient probe excitation, or when its estimate lies outside gain_bounds. Unknown injection amplitude is inseparable from gain; statistical bias, noise and excitation limits remain architecture-dependent.

Use process() for each contiguous block: it corrects with the active estimate and then updates from that block. apply() and update() expose the two steps separately.

Parameters:
  • probe_amplitude (float) – Known probe amplitude, in the input-referred units of coarse.

  • initial_gain (float) – Starting residue gain: the design value or a foreground estimate.

  • backend_bits (int) – Backend resolution; backend_code must lie in [0, 2**backend_bits).

  • gain_bounds (tuple of float) – (low, high) range containing initial_gain and the true gain. Block estimates outside it are held, so too narrow a range stops tracking.

  • learning_rate (float, default 0.08) – Exponential-averaging weight in (0, 1].

reset()[source]#

Restore the configured initial gain and zero accepted updates.

apply(coarse, backend, pn)[source]#

Correct with the current snapshot, without updating parameters.

update(backend, pn, backend_code)[source]#

Return gain_next, observed_gain and status; a hold preserves state.

Each call is one averaging window. Changing block size changes the estimator dynamics even for the same total number of samples.

process(coarse, backend, pn, backend_code, *, learn=True)[source]#

Correct one block with the active gain, then learn from that block.

Returns calibrated_signal and gain_used from apply(), plus gain_next, observed_gain and status from update(). learn=False freezes the estimate, ignores backend_code and reports status "frozen".

adctoolbox.apply_dither_gain(coarse, backend, pn, *, gain_estimate, probe_amplitude)[source]#

Apply q + r/G_est - a*p using a frozen positive gain estimate.

Inputs are aligned 1D observable arrays in consistent voltage/code units. pn is the normalized known probe; amplitude is in input-referred units. Returns calibrated_signal and gain_used. The known probe must pass through the residue gain, after the coarse decision. This function only reconstructs the digital output; it does not inject a probe.

class adctoolbox.DitherHDCTracker(*, fixed=False, window_log2=24, dither_peak=0.2, estimate_memory=True, estimate_dac_mismatch=True, initial_coefficients=None, fraction=24, bits=28, accumulator_bits=None, controller_latency=512)[source]#

Bases: object

Streaming three-PN Pipeline HDC in floating or fixed-point arithmetic.

The stage uses signed codes {-1, 0, 1}, nominal gain 2, DAC levels stage_code/2, and a normalized backend range [-1, 1]. Three independent PN signs inject dither_peak/3 * sum(pn_signs) before the amplifier. This is the model in HDC_FIXED.md: published gain/cubic HDC plus two extensions, a one-tap memory inverse and a two-element DAC mismatch whose roles are swapped according to selection.

Each update uses one averaging window only and replaces the active coefficients controller_latency samples later; windows are not averaged together, so estimator spread falls only with window length, by about half for every +2 in window_log2. On the HDC_FIXED.md model, 2^20-sample windows gave about 51 dB SNDR and 2^24 about 56 dB, close to the limit of the cubic inverse itself; at 2^24 the cubic coefficient error still exceeded 10% for two of three seeds. A 2^24 window lasts 0.17 s at 100 MS/s.

selection must be a balanced random sign sequence independent of the input, such as an LFSR. With constant tags the mismatch estimate is out of range, so only dac_mismatch is held (counted in held_dac_mismatch) while the other coefficients keep learning. A tag mean m over nonzero stage codes biases the mismatch estimate by about -2*m*mean(abs(input)) over those codes. For a stage without role swapping, use estimate_dac_mismatch=False; with a zero dac_mismatch coefficient, selection may then be None.

Arbitrary contiguous block partitions give the same result. learn=False freezes active coefficients, cancels a pending update and discards partial estimation windows, but retains signal history. Call reset() for an independent capture. The estimator keeps the documented HDC moment approximation; a public interface does not remove finite-record bias or expand physical validity. Without the optional accel extra (Numba), the per-sample loop runs in pure Python at roughly 10^5 samples per second.

Parameters:
  • fixed (bool, default False) – Use the bit-accurate fixed-point datapath of HDC_FIXED.md instead of floating arithmetic.

  • window_log2 (int, default 24) – log2 of the samples in each estimation window, in [8, 30].

  • dither_peak (float, default 0.2) – Peak of the summed probe in stage-input units, in (0, 1). It must match the analog injection.

  • estimate_memory (bool, default True) – Learn the memory or DAC-mismatch extension. When False, that coefficient keeps its initial value.

  • estimate_dac_mismatch (bool, default True) – Learn the memory or DAC-mismatch extension. When False, that coefficient keeps its initial value.

  • initial_coefficients (dict, optional) – Exactly linear, cubic, memory and dac_mismatch, default 1, 0, 0, 0. These digital inverse coefficients c1, k3, h, delta are not physical device parameters: for r = backend/2, w = c1*r - k3*(c1*r)**3, v = w - h*v_previous and output = stage_code/2 + v - probe + delta*selection*stage_code/2.

  • fraction (int, default 24 and 28) – Fixed-point fractional and total word bits. Validated in both modes, but used in arithmetic only when fixed is True.

  • bits (int, default 24 and 28) – Fixed-point fractional and total word bits. Validated in both modes, but used in arithmetic only when fixed is True.

  • accumulator_bits (int, optional) – Fixed-point correlation accumulator width, default bits + window_log2.

  • controller_latency (int, default 512) – Samples from the end of a window to the commit of its coefficients.

property coefficients#

A detached dict of active digital correction coefficients.

reset()[source]#

Restore initial coefficients and clear all stream/estimator state.

process(backend, pn_signs, stage_code, selection, backend_rails, *, learn=True)[source]#

Correct one contiguous block and optionally learn from its moments.

Parameters:
  • backend (array_like, shape (N,)) – Finite normalized backend samples in [-1, 1], not integer codes.

  • pn_signs (array_like, shape (N, 3)) – Aligned signs {-1, +1} for the three injected PN streams.

  • stage_code (array_like, shape (N,)) – Signed coarse decisions {-1, 0, 1}, before Dither injection.

  • selection (array_like, shape (N,), or None) – Observable DAC role-selection tags {-1, +1}, balanced and random (see the class notes). None is allowed only when estimate_dac_mismatch is False and dac_mismatch is zero.

  • backend_rails (array_like, shape (N,)) – Boolean rail flags; any flag rejects its entire estimation window.

  • learn (bool) – False freezes coefficients while advancing correction history.

Returns:

calibrated_signal in stage input units; active coefficients after the block; updates (accepted, possibly pending) and held_updates cumulative counts; held_dac_mismatch, the accepted updates that kept the previous dac_mismatch; pending_update flag; and cumulative saturations for datapath, accumulator and controller. A block may span coefficient commits; the returned coefficients are not necessarily those used for every sample in that block.

Return type:

dict