QUANTIZATION

← all examples · interactive version of ex/q.py and ex/quantization.py · data converters
CONTINUOUS t, CONTINUOUS v
SAMPLE ÷4
QUANTISE 1b
DISCRETE t, DISCRETE v
QUANTISER
B bits means 2B levels. The obvious one-liner round(x·2B)/2B counts fractional bits instead — its step is 2-B, so “1 bit” gives five levels over ±1 and the SQNR beats 6.02B + 1.76 by a whole bit. ex/q.py used to do exactly that; tick the box to see what it looked like.
SIGNAL
VIEW
LEVELS
LSB
SQNR
6.02B+1.76
ENOB
f_1 BIN
Continuous
Samples
Quantiser output
Quantization error
Odd harmonics

WHAT YOU ARE LOOKING AT

An analog-to-digital converter does two separate things, and it is worth keeping them apart in your head. It makes time discrete (sampling) and it makes value discrete (quantization). The three spectra along the bottom are the same signal after each step: continuous time and continuous value, then discrete time and continuous value, then discrete time and discrete value.

These are complex FFTs, so they show both negative and positive frequencies, which is why one sine gives two spikes — sin(x) = (eix − e-ix)/2i, and both exponentials are there. Everything is normalised to its own peak, so 0 dB is the tone.

Quantization replaces the signal with the nearest of 2B levels spread over the full scale, so one LSB is 2/2B for a ±1 input. The difference is the error in the top middle panel, bounded by ±½ LSB by construction. If that error looks like noise, treating it as noise gives the familiar SQNR ≈ 6.02·B + 1.76 dB. If it does not — at one bit it very much does not — the error is a deterministic function of the signal and lands in harmonics instead of spreading out, and the formula stops describing anything.

THINGS WORTH TRYING

THE PYTHON

def adc(x,bits):
    levels = 2**bits
    y = np.round(x*levels)/levels
    return y

#- Sampling frequency is 1/nfs of the time vector
x_sn = x_s[0::nfs]
y_sn = adc(x_sn,bits)

def freqDomain(x,hann=True):
    N = len(x)
    w = np.hanning(N+1) if hann else np.ones(N+1)
    X = np.fft.fftshift(np.fft.fft(np.multiply(w[0:N],x)))
    return X/np.max(np.abs(X))   # normalise to max output power

Source: ex/q.py, ex/quantization.py (the same thing with np.sign(), i.e. a real one-bit case). This page defaults to a conventional B-bit converter — 2B levels, step 2/2B, mid-riser, saturating at the end codes — so that the slider, the level count and 6.02B + 1.76 all agree with each other. The script's adc() is one tick box away.