ADC Quantisation — 6–10 Bits¶
What you're seeing¶
A real sinusoid at -10 dBFS is passed through doppler.cvt.ADC at five
bit depths (6–10 bits) with no dithering. The decoded output is shown in
both time and frequency domains.
Top — Time domain (3 cycles). The float32 reference (grey) overlays the decoded signal at each bit depth. The quantisation staircase is coarse and clearly stepped at 6 bits; it becomes nearly indistinguishable from the reference at 10 bits. Each additional bit halves the step size.
Bottom — One-sided spectrum (Blackman-Harris, N = 8192). The tone stays fixed at −10 dBFS across all bit depths. The wideband quantisation noise floor descends by ≈ 6 dB with each additional bit, following the theoretical relationship:
| Bits | Theoretical noise floor | Theoretical SNR |
|---|---|---|
| 6 | −37.9 dBFS | 37.9 dB |
| 7 | −43.9 dBFS | 43.9 dB |
| 8 | −50.0 dBFS | 50.0 dB |
| 9 | −55.9 dBFS | 55.9 dB |
| 10 | −61.9 dBFS | 61.9 dB |
The dashed horizontal lines mark the theoretical noise floor for each bit depth. At low bit depths (6–7 bits) harmonic spurs from deterministic quantisation distortion are visible above the noise floor; they disappear into the noise at 9–10 bits.
The ADC object¶
doppler.cvt.ADC models a signed two's-complement ADC with configurable
full-scale reference and optional TPDF dither.
from doppler.cvt import ADC
import numpy as np
# Sinusoid at -10 dBFS
amplitude = 10 ** (-10.0 / 20.0) # ≈ 0.316
N = 8192
f0 = 0.05 # cycles/sample
x = (amplitude * np.sin(2 * np.pi * f0 * np.arange(N))).astype(np.float32)
# Quantise at 8 bits, -10 dBFS full-scale reference
adc = ADC(bits=8, dbfs=-10.0, dithering=0)
q = adc.steps(x) # int64, range [-128, 127]
print(adc.scale) # ≈ 400.0 (= 2^7 × 10^(10/20))
print(adc.clipped) # False — input was at reference level
# Decode back to float for analysis
x_hat = q.astype(np.float64) / adc.scale
snr_db = 10 * np.log10(
np.mean(x.astype(np.float64)**2) /
np.mean((x.astype(np.float64) - x_hat)**2)
)
print(f"Measured SNR: {snr_db:.1f} dB") # ≈ 49–50 dB
# TPDF dither breaks up harmonic spurs at the cost of a slight noise rise
adc_d = ADC(bits=8, dbfs=-10.0, dithering=1)
q_d = adc_d.steps(x)
Parameters¶
| Parameter | Default | Description |
|---|---|---|
bits |
16 |
ADC resolution, 1–64 bits |
dbfs |
-10.0 |
Full-scale input level in dBFS. A signal at this amplitude fills the converter — scale = 2^(bits-1) × 10^(-dbfs/20) |
dithering |
0 |
0 = off; non-zero = TPDF dither added before rounding |
Properties¶
| Property | Type | Description |
|---|---|---|
.scale |
float |
Precomputed scale factor — divide int64 output by this to reconstruct float |
.bits |
int |
ADC resolution |
.clipped |
bool |
Sticky flag — set if any sample saturated since last reset() |
