∿ LEARN DSPFrom signal to HF transceiver
Lab / lesson

Quantization: how amplitude becomes a code

We tell apart the digital code, the quantized value, and the quantization error.

← The Nyquist frequency

Two limits of a digital record

Until now, we selected the sampling instants and used ideal numbers for the sample values. A real analog-to-digital converter (ADC) also limits the precision in amplitude:

  1. Sampling selects the time instants tn=n/Fst_n=n/F_s.
  2. Quantization replaces the measured value with one level from a finite set of levels and gives the related digital code.

Resolution is the number of bits in the code. More bits let you tell apart amplitude values that are nearer to each other, but they do not correct the aliasing from the previous lessons. Here we change the resolution and keep the sample rate the same.

A converter model with positive and negative levels

We measure the signal in arbitrary units: the bottom of the scale is −1-1, and the top is +1+1. For a resolution of NN bits, there are L=2NL=2^N codes. In the selected model, the step between levels is

Δ=22N.\Delta=\frac{2}{2^N}.

With N=3N=3, we get eight signed codes and a step of 0.250.25:

Code kkQuantized value kΔk\Delta
−4−1.00
−3−0.75
−2−0.50
−1−0.25
00.00
10.25
20.50
30.75

This model has no +1+1 level. This comes from the range of signed integers that we selected: from −4-4 to +3+3. This convention agrees with two’s complement, which we discuss in more detail when we move to fixed-point. A specific ADC can have a different code format.

From value to code and back

For each input value x[n]x[n], do three steps:

  1. Divide by the step Δ\Delta.
  2. Round to the nearest integer.
  3. If the result is outside the code range, replace it with the nearest limit of the code range.

As a formula:

k[n]=clip⁡(round⁡x[n]Δ,−2N−1,  2N−1−1).k[n]=\operatorname{clip}\left(\operatorname{round}\frac{x[n]}{\Delta}, -2^{N-1},\;2^{N-1}-1\right).

Here round means rounding, and clip means limiting the range. The script uses np.rint: a value exactly in the middle between two integers is rounded to the nearest even integer.

To show the value of a code on a plot, multiply the code by the step:

xq[n]=k[n]Δ.x_q[n]=k[n]\Delta.

For example, with three bits, the input 0.620.62 gives 0.62/0.25=2.480.62/0.25=2.48. After rounding, the code is 22, and the quantized value is 0.500.50. The input 0.740.74 gives code 33 and value 0.750.75.

What a digital signal contains

The ADC gives codes. In our example, the code sequence contains the integer 2. On the selected scale, it means the value 0.50.

In Python, you can work with both representations:

codes = np.array([2, 3, -2])  # integer codes
step = 0.25
values = codes * step        # [0.50, 0.75, -0.50]

The values array is convenient for plots and for more calculations. Conversion to floating-point numbers does not recover the lost precision: the original values 0.62 and 0.50 could give the same code. To convert normalized values to volts, you also need the input range scale of the device.

Quantization error and range limit

We define the error as the difference between the quantized value and the original value:

e[n]=xq[n]−x[n].e[n]=x_q[n]-x[n].

If the rounded code is in the valid range, then

∣e[n]∣≤Δ2.|e[n]|\le\frac{\Delta}{2}.

If the code had to be limited, this guarantee is lost. For example, for the input 0.900.90, rounding gives code 44, but the maximum code is 33. We get the value 0.750.75 and the error −0.15-0.15; its magnitude is more than half the step, 0.1250.125. For the input 1.201.20, the error is even larger.

This limit is clipping (the signal is cut off at a level). In our asymmetric model, clipping can start before +1+1: use the valid codes as the reference, not only the nominal limits of the scale.

Quantization error does not always look like random noise. For a periodic signal, it can also be periodic. We examine its spectrum after we learn the DFT.

Lab: resolution and clipping

The script compares models with a resolution of 3 and 8 bits:

uv run labs/01/04-quantization.py --show

Quantization with a resolution of 3 and 8 bits

The gray line is the original cosine, the blue dots are ideal samples, and the orange dots are the values of the codes. Steps connect the orange dots to make the plot easier to read; the array contains separate samples. The bottom panel shows the error. The script also prints the codes and their values separately.

First compare a few resolutions, then increase the amplitude:

uv run labs/01/04-quantization.py --bits 2 3 4 8 --samples 48 --show
uv run labs/01/04-quantization.py --bits 3 --amplitude 1.2 --show --output-dir /tmp/dsp-clipping

In the second experiment, the peaks hit the limit codes. More bits make the step smaller, but they do not make the selected input scale wider.

What one more bit gives

Each additional bit doubles the number of levels and makes the step two times smaller. When rounding does not need code limiting, the upper limit of the error also becomes two times smaller.

In a real ADC, its own noise and nonlinearity also limit the precision. So the nominal resolution and the actual precision can be different. We keep the quantitative estimates of noise and the effective number of bits (ENOB) for the related part of the course.

Self-check

For the three-bit model, the step is Δ=0.25\Delta=0.25, and the codes are in the range from −4-4 to 33. We get the code by rounding x/Δx/\Delta to the nearest integer and then limiting the range. The quantized value is xq=kΔx_q=k\Delta.

Find the code kk and the quantized value xqx_q for the input x=0.62x=0.62.

Explanation

0.62/0.25=2.480.62/0.25=2.48. The nearest integer is 22, and it is in the range from −4-4 to 33. The quantized value is xq=2⋅0.25=0.50x_q=2\cdot0.25=0.50. The error is 0.50−0.62=−0.120.50-0.62=-0.12 — its magnitude is less than half the step, 0.1250.125.

Find the code kk and the quantized value xqx_q for the input x=−0.62x=-0.62.

Explanation

−0.62/0.25=−2.48→−2-0.62/0.25=-2.48\to-2. The result is symmetric to the previous one: xq=−0.50x_q=-0.50. The asymmetry of our model shows only at the edges of the range: code −4-4 exists, but code +4+4 does not.

For the input x=0.90x=0.90, find the code before limiting, the final code, and the error xq−xx_q-x.

Explanation

0.90/0.25=3.60.90/0.25=3.6, and rounding gives 44. This code does not exist, so we limit it to 33: xq=0.75x_q=0.75. The magnitude of the error −0.15-0.15 is more than half the step, 0.1250.125 — this is a sign of clipping.

The code is 22. Can you say that the original value was exactly 0.500.50?

Explanation

Code 22 comes from all inputs for which x/0.25x/0.25 rounds to 22 — that is, within ±Δ/2=±0.125\pm\Delta/2=\pm0.125 of 0.500.50. From the code, you know only this interval; the exact original value is lost.

Next lesson: a complex number as a vector — I and Q →