OVERSAMPLING

← all examples · interactive version of ex/osr.py · data converters
SIGNAL
SAMPLE ÷4
QUANTISE 10b
SUM OF 4
DIGITAL
OVERSAMPLING
The filter is oversample(): a running sum of OSR samples, i.e. a boxcar, i.e. sinc. It is the cheapest low-pass there is. B bits means 2B levels; the script's adc() counts fractional bits and gives 2B+1+1, one bit more than it says.
SIGNAL
VIEW
LEVELS
BAND
IN-BAND SNR
ENOB
GAIN OVER OSR=1
FILTER ZEROS
Quantiser output y_sn
After the running sum y_on
Boxcar |H(f)|
Band edge f_s/2·OSR
Ideal 3 dB per octave

WHAT YOU ARE LOOKING AT

Quantization noise power is set by the LSB alone: Δ2/12, whatever the sample rate. Spread that fixed power over a wider band and there is less of it in any given slice of spectrum. That is the whole idea of oversampling: run the converter faster than you need, then throw the part of the band you do not need away.

Doubling the sample rate halves the noise density, so filtering back down to the original band buys 3 dB, or half a bit. The SNR-vs-OSR panel plots that directly: measured in-band SNR against OSR, with the ideal 3 dB per octave line for comparison. Ten bits of ADC plus an OSR of 4 gets you about the same SNR as eleven bits at Nyquist.

The filter here is the crudest possible: oversample() adds up OSR consecutive samples. That is a boxcar in time, so a sinc in frequency, with zeros at every multiple of fs/OSR — count the notches in the filtered spectrum. It is a bad low-pass filter, and it still works, which tells you how much slack oversampling gives you.

THINGS WORTH TRYING

THE PYTHON

#- Oversample
OSR = 4

def oversample(x,OSR):
    N = len(x)
    y = np.zeros(N)
    for n in range(0,N):
        for k in range(0,OSR):
            m = n+k
            if(m < N):
                y[n] += x[m]
    return y

y_on = oversample(y_sn,OSR)

Source: ex/osr.py. The running sum is not decimated afterwards, so the plots stay on the same frequency axis and the filter shape is easy to see.