Process a noisy sensor signal
Sample a fictional noisy signal, expose aliasing, compare two filters, inspect a DFT and apply hysteresis.

Signal Sage: I want you to preserve the raw trace before judging any filtered result.
Predict the reading, then compare it with the measurement.
Python 3 in your browser (JupyterLite)
Python · no installOpen the notebook in your browser and run each Python cell; no install or account is needed.
Three ways to run: use JupyterLite here with no install; run main.py locally from the downloadable lab folder; or open the same notebook in Google Colab.
Steps
Step 1
- Do
- In the JupyterLite notebook, run the Step 1 cell to define the synthetic one-second signal and noise seed.
- You will see
- Input: execute notebook step 1 with its stated deterministic fixture. Observed: duration=1.0 s; sample rate=20 Hz; samples=20 Observed: components: baseline=20, wanted=1 Hz at amplitude 2 Observed: interference=9 Hz at amplitude 0.8; seeded noise=+/-0.35
- Why it matters
- Expected: the seed-42 fixture defines a one-second trace with 20 samples. The output gives the wanted 1 Hz and interference 9 Hz components. Stated units and bounded noise make later processing reproducible. Limit: units are arbitrary; this is not a calibrated sensor model.

Step 1 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 2
- Do
- In the JupyterLite notebook, run the Step 2 cell to sample at 20 Hz and 8 Hz and compare the strongest DFT bin.
- You will see
- Input: execute notebook step 2 with its stated deterministic fixture. Observed: 20 20 1 9 Hz visible Observed: 8 8 1 9 Hz aliases to 1 Hz Observed: Nyquist requires sample rate greater than twice the highest component.
- Why it matters
- Expected: 8 Hz sampling aliases the 9 Hz interference into the 1 Hz bin. The run shows the 9 Hz component as visible at 20 Hz and aliased at 8 Hz. This demonstrates why sample rate must exceed twice the highest component. Limit: the analysis uses this finite synthetic signal.

Step 2 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 3
- Do
- In the JupyterLite notebook, run the Step 3 cell to print the beginning of the seeded raw trace.
- You will see
- Input: execute notebook step 3 with its stated deterministic fixture. Observed: 0 0.00 20.10 Observed: 1 0.05 20.53 Observed: 2 0.10 20.55
- Why it matters
- Expected: the unfiltered record keeps the original seeded samples. The first three local values are 20.10, 20.53, and 20.55 at the shown times. The stored raw trace remains available for comparisons. Limit: arbitrary units and synthetic noise are not sensor readings.

Step 3 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 4
- Do
- In the JupyterLite notebook, run the Step 4 cell to compare a moving average with an exponential filter.
- You will see
- Input: execute notebook step 4 with its stated deterministic fixture. Observed: 0 20.10 20.10 20.10 Observed: 3 22.07 21.05 20.96 Observed: 5 22.92 22.10 21.72
- Why it matters
- Expected: the filters smooth the same raw samples using different histories. At index 5, moving output is 22.10 and exponential output is 21.72. The finite moving window and decaying exponential history respond differently. Limit: this is a six-sample comparison on one fixture.

Step 4 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 5
- Do
- In the JupyterLite notebook, run the Step 5 cell to calculate the strongest bins with a standard-library DFT.
- You will see
- Input: execute notebook step 5 with its stated deterministic fixture. Observed: 1 1.009 Observed: 9 0.365 Observed: 10 0.070
- Why it matters
- Expected: the one-second DFT has 1 Hz bins and exposes strong components. The run ranks bins 1, 9, and 10 with magnitudes 1.009, 0.365, and 0.070. The 1 Hz wanted tone and 9 Hz interference are the two largest peaks. Limit: rectangular-window leakage is not corrected.

Step 5 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 6
- Do
- In the JupyterLite notebook, run the Step 6 cell to apply high and low hysteresis thresholds.
- You will see
- Input: execute notebook step 6 with its stated deterministic fixture. Observed: 0 20.10 NORMAL Observed: 4 21.08 HIGH Observed: 11 20.11 NORMAL
- Why it matters
- Expected: hysteresis enters HIGH above 21.0 and leaves below 20.5. The run transitions to HIGH at index 4 and back to NORMAL at 11. Separate entry and exit thresholds reduce chatter near the boundary. Limit: these thresholds are for the synthetic fixture only.

Step 6 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab) Step 7
- Do
- In the JupyterLite notebook, run the Step 7 cell to validate reproducibility and state the model limits.
- You will see
- Input: execute notebook step 7 with its stated deterministic fixture. Observed: same seed reproduces raw trace: PASS Observed: raw trace preserved before filtering: PASS Observed: Decision: 20 Hz resolves this fixture; measure real bandwidth before deployment.
- Why it matters
- Expected: repeatability and raw-data preservation checks pass for this code. Both checks passed in the local run; the DFT caveat remains explicit. The fixture supports the 20 Hz sampling choice for its 9 Hz component. Limit: measure real sensor bandwidth before deployment.

Step 7 · Python 3 in your browser (JupyterLite); numbered callout added to a real capture. Enlarge screenshot (new tab)
Chapter checks
These questions refer to the chapter’s examples. Use the return links to review their answers.
What does one LSB, the least significant bit, represent for an ADC?
Return to the chapter’s knowledge checkReadings are mostly steady but occasionally show single large spikes from electrical interference. Which filter best removes the spikes while preserving the true level?
Return to the chapter’s knowledge check
Return to Signal Processing: Quantization and Filtering · Browse Labs