Skip to content

ADC Quantisation — 6–10 Bits

ADC quantisation demo

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:

noise floor ≈ −(6.02 × bits + 1.76) dBFS
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()
python examples/python/adc_demo.py   # → adc_demo.png