Lab 5.4 — Aliasing Lab
← Course 2 syllabus · Module 5 · Prev: « Lab 5.3 · Next: Lab 6.1 »
Goal
See aliasing happen — not as a theorem, but as a real tone that lands at the wrong frequency in your captured data. Using the timer-triggered, DMA-fed acquisition from Lab 5.3, you sample a DAC-generated sine at a fixed \(f_s\) while sweeping the sine’s frequency above and below the Nyquist limit \(f_s/2\). Below Nyquist the measured frequency tracks the source; above it, the tone folds back and masquerades as a lower frequency. Then you insert the Lab 4.4 active low-pass filter ahead of the ADC as an anti-alias filter and watch the folded tones disappear before they can be sampled. This is the single most important practical lesson in sampling: what you don’t filter out before the ADC, you can never separate afterward.
Recommended reading
- O-S&S Ch. 7 — the sampling theorem and aliasing; the impulse-train sampler and spectral replication.
- O-DSP Ch. 4 — sampling of continuous-time signals, frequency-domain aliasing, and the role of the anti-aliasing filter.
- Lyons Ch. 2 — the practitioner’s picture of sampling and aliasing (the folding diagram; sampling of real signals).
- Course 1 Week 10 — distributions and the Dirac comb: uniform sampling is multiplication by \(\operatorname{\text{Ш}}_{T_s}(t) = \sum_n \delta(t - nT_s)\), whose transform is itself a comb, so the spectrum is replicated at every multiple of \(f_s\). Aliasing is those replicas overlapping — the rigorous reason for the folding you’ll measure.
Equipment & parts
- STM32 NUCLEO-L476RG running the Lab 5.3 acquisition firmware.
- MCP4725 DAC (3.3 V powered) generating the test sine (Lab 3.3); for source tones above ~1 kHz the STM32 on-chip DAC (timer + DMA driven) gives a cleaner high-frequency sine — either is a legitimate “DAC-generated” source.
- The Lab 4.4 active low-pass filter (MCP6002) as the anti-alias stage.
- Siglent SDS1104X-E (optional cross-check of the true source frequency and its FFT).
- Host Python (numpy/matplotlib) to FFT the captured buffer.
Safety & don’t-break-it
- The sine must stay 0 – 3.3 V into the ADC. A sine has to be DC-biased to sit inside the ADC’s unipolar range — center it at ~1.65 V with a swing that never goes below 0 V or above 3.3 V (e.g. 1.65 V ± 1.0 V). Verify the min/max on the scope before wiring to
PA0; a sine that dips negative or clips the 3.3 V rail damages the pin and distorts the spectrum with a different nonlinearity you’ll mistake for aliasing. - Keep the MCP4725 (and the MCP6002 filter) on the 3.3 V rail so nothing downstream can exceed VREF+.
- Common ground across DAC, filter, ADC, and scope.
Background
Uniform sampling at rate \(f_s\) replicates the signal’s spectrum at every integer multiple of \(f_s\). The Nyquist frequency is
\[f_N = \frac{f_s}{2},\]
and a real sinusoid is represented unambiguously only if its frequency \(f < f_N\). A pure tone at \(f > f_N\) is indistinguishable, after sampling, from a tone at the aliased frequency
\[f_\text{alias} = \bigl|\,f - k f_s\,\bigr|,\qquad k = \operatorname{round}\!\left(\frac{f}{f_s}\right),\]
which always lands in the baseband \([0, f_N]\). Geometrically the frequency axis folds at multiples of \(f_N\): as \(f\) climbs past \(f_N\), the apparent frequency walks back down toward 0, then back up, and so on.
The Dirac-comb view (Course 1 Week 10) makes the mechanism exact. Sampling multiplies \(x(t)\) by \(\operatorname{\text{Ш}}_{T_s}(t)=\sum_n \delta(t-nT_s)\); in frequency this convolves \(X(f)\) with a comb of spacing \(f_s\):
\[X_s(f) = f_s \sum_{k=-\infty}^{\infty} X(f - k f_s).\]
If \(X\) has energy above \(f_N\), the shifted copies overlap and add — the high tone’s replica falls into the baseband as \(f_\text{alias}\), and no post-processing can pull it back out. The only cure is to remove that energy before sampling: an anti-alias low-pass filter with its passband below \(f_N\). The Lab 4.4 active LPF is exactly that filter.
Procedure
Part A — Set up the sampler and a sweepable source.
- Run the Lab 5.3 firmware with a fixed, known \(f_s\) — pick \(f_s = \mathbf{2\ kHz}\) so \(f_N = 1\ \text{kHz}\) sits in a range the DAC can straddle. Have
process_block()copy a full buffer to the host over the VCP (or capture into RAM and dump once). - Configure the DAC to output a DC-biased sine at 1.65 V center. Confirm on the scope that it never leaves 0 – 3.3 V.
Part B — Sweep through Nyquist (no anti-alias filter).
- For each source frequency \(f\) in a sweep that crosses \(f_N\) — e.g. 300, 800, 1200, 1700, 2100 Hz — capture one buffer and FFT it on the host. Record the peak bin = the measured frequency.
- Below \(f_N\) (300, 800 Hz) the peak sits at the true \(f\). At and above \(f_N\) (1200, 1700, 2100 Hz) the peak appears at \(f_\text{alias}=|f-kf_s|\) — a lower frequency than the source. Cross-check the true source frequency independently on the Siglent so you can prove the ADC is lying, not the DAC.
- Note the tell-tale ambiguity: the 1200 Hz source and an 800 Hz source produce the same peak bin — they are aliases of each other.
Part C — Add the anti-alias filter.
- Insert the Lab 4.4 active LPF between the DAC output and the ADC input, with its cutoff \(f_c\) set at (or just below) \(f_N = 1\ \text{kHz}\).
- Repeat the sweep. Now the sub-Nyquist tones pass through and read correctly, but as \(f\) climbs above \(f_c\) the source amplitude is rolled off before the ADC — the aliased peak’s amplitude collapses (by the filter’s stopband attenuation) instead of appearing full-strength at the wrong frequency. Record the aliased-peak amplitude with and without the filter.
Deliverable & expected results
Host FFT plots for two or three source frequencies, and the alias table below. Predicted \(f_\text{alias}\) uses \(f_s = 2\ \text{kHz}\), \(f_N = 1\ \text{kHz}\).
| Source \(f\) | \(k=\operatorname{round}(f/f_s)\) | Predicted \(f_\text{alias}=|f-kf_s|\) | Measured peak (no AAF) | Alias amplitude (with AAF) |
|---|---|---|---|---|
| 300 Hz | 0 | 300 Hz (no alias) | … | … |
| 800 Hz | 0 | 800 Hz (no alias) | … | … |
| 1200 Hz | 1 | 800 Hz | … | … (attenuated) |
| 1700 Hz | 1 | 300 Hz | … | … (attenuated) |
| 2100 Hz | 1 | 100 Hz | … | … (attenuated) |
Analysis & reconciliation
For each source tone compute \(k=\operatorname{round}(f/f_s)\) and \(f_\text{alias}=|f-kf_s|\) by hand and confirm the FFT peak lands there (to within the FFT bin width \(\Delta f = f_s/N\) — report that resolution). The clean result is that 1200 Hz and 800 Hz are indistinguishable in the un-filtered captures: same bin, and if you overlay them, essentially the same samples. With the anti-alias filter in place, the sub-Nyquist tones are unchanged but the super-Nyquist tones’ peaks drop by the filter’s attenuation at that frequency — quantify it against the Lab 4.4 Bode rolloff (a first-order stage gives ~ −20 dB/decade past \(f_c\), so the gap between filtered and unfiltered alias amplitude should match the filter’s magnitude at \(f\)). If a residual aliased tone survives, it’s because a single-pole filter isn’t steep enough between \(f_c\) and \(f_s - f_c\) — the real-world reason anti-alias filters are higher-order, and the motivation for both sharper analog filters and oversampling.
Going further
- Fold the whole axis. Sweep finely from 0 to \(2f_s\) and plot measured-vs-source frequency; you’ll trace the classic triangular folding diagram (Lyons Ch. 2) with vertices at multiples of \(f_N\).
- Oversample instead of filter. Raise \(f_s\) to 8 kHz and repeat — the same source tones now sit below the new \(f_N\) and stop aliasing, showing the oversampling-vs-filtering trade.
- DC-offset and even harmonics. Because the sine is biased, any DAC nonlinearity adds harmonics that also alias; identify which extra peaks are aliased harmonics vs. the fundamental’s alias.
- This anti-alias front end is now mandatory for every real acquisition in Module 6 — the FIR/IIR/FFT labs assume the band above \(f_N\) has already been removed.