Q15 vs UQ15 Quantization¶
What you're seeing¶
All three panels show the spectrum of the same full-scale complex tone (N=65536 samples, Blackman-Harris windowed) at each stage of quantization.
Top — Input CF32. The reference: a single tone at 0.07 cycles/sample, noise floor limited by the float32 compute path (~−150 dBFS).
Middle — Q15 bipolar roundtrip. F32 → int16 → F32 via
doppler.cvt.F32ToI16 / I16ToF32. Zero maps to the integer zero;
negative values use two's-complement. Noise floor rises to ~−92 dBFS
(Q15 theoretical SNR = 6.02 × 15 + 1.76 dB). The tone is otherwise
identical to the input.
Bottom — UQ15 offset-binary roundtrip. F32 → v_Q15 + 32768 → uint16 → F32. The +32768 shift (DC in the integer domain) cancels
exactly on decode; the noise floor and tone level are indistinguishable
from the Q15 panel. This is the encoding convention used by the CIC
decimator's internal integer pipeline.
Why two conventions?¶
Bipolar (Q15): natural for audio, DSP literature, signed arithmetic. Zero is the integer zero; sign-extension preserves the value in wider containers.
Offset-binary (UQ15/UQ16): required when all downstream arithmetic
must be unsigned. The CIC integrators use uint64_t accumulators
whose overflow behaviour is defined by C99 (mod 2⁶⁴), unlike signed
integers which overflow with undefined behaviour. Keeping all inputs
non-negative avoids sign-extension casts that are implementation-defined
in C99 ((int16_t)(uint16_t)v for v ≥ 32768).
Both conventions use the same quantization step Δ = 2⁻¹⁵, so the noise floor is identical — the choice is purely architectural.
from doppler.cvt import F32ToI16, I16ToF32
import numpy as np
# Q15 bipolar roundtrip
enc, dec = F32ToI16(), I16ToF32()
x_q15 = dec.steps(enc.steps(x.real)) # real channel only
# UQ15 offset-binary roundtrip (numpy — no cvt UQ15 type yet)
v = np.clip(np.round(x.real * 32768.0), -32768, 32767).astype(np.int16)
u = (v.astype(np.int32) + 32768).astype(np.uint16)
x_uq15 = (u.astype(np.float32) - 32768.0) / 32768.0
See docs/design/QUANTIZATION.md for the
full mathematical model and C99 cast-chain analysis.
