Signal Processing

Signal processing is the math and methods for analyzing, modifying, and extracting information from signals — any quantity that varies with time (or space). It bridges the analog physical world and digital computation.

Why It Matters

Signals are everywhere: audio waveforms, radio transmissions, sensor readings, control system feedback, images, communication channels. The Fourier transform, sampling theorem, and digital filtering are foundational tools used in control systems, communications, embedded systems, audio, and machine learning.

Signals: Continuous vs Discrete

Continuous (analog):    x(t) — defined for all t
Discrete (digital):     x[n] — defined only at sample indices n = 0, 1, 2, ...

Analog → Digital:  sample at rate Fs → x[n] = x(n/Fs)
Digital → Analog:  DAC + reconstruction filter
PropertyContinuousDiscrete
TimeContinuous (t)Discrete (n)
ValuesContinuous (amplitude)Quantized (ADC resolution)
MathCalculus, Laplace (s)Difference equations, Z-transform (z)
ProcessingAnalog circuitsMicrocontrollers, DSPs

Fourier Transform

Any signal can be decomposed into a sum of sinusoids at different frequencies. The Fourier transform reveals which frequencies are present and how strong each is.

Time domain:   signal as amplitude vs time
                    ↕  Fourier Transform
Frequency domain:  signal as magnitude vs frequency (spectrum)

DFT/FFT

The Discrete Fourier Transform (DFT) converts N time-domain samples into N frequency-domain coefficients. The FFT (Fast Fourier Transform) computes it in O(N log N) instead of O(N²).

import numpy as np
import matplotlib.pyplot as plt
 
# Generate signal: 50 Hz + 120 Hz
Fs = 1000         # sample rate
t = np.arange(0, 1.0, 1/Fs)
signal = np.sin(2 * np.pi * 50 * t) + 0.5 * np.sin(2 * np.pi * 120 * t)
signal += 0.3 * np.random.randn(len(t))  # add noise
 
# Compute FFT
fft_vals = np.fft.rfft(signal)
freqs = np.fft.rfftfreq(len(signal), 1/Fs)
magnitude = 2.0 / len(signal) * np.abs(fft_vals)
 
plt.plot(freqs, magnitude)
plt.xlabel('Frequency (Hz)'); plt.ylabel('Magnitude')
plt.title('Frequency Spectrum'); plt.grid(); plt.show()
# Peaks at 50 Hz and 120 Hz clearly visible above noise floor

Sampling Theorem (Nyquist-Shannon)

To faithfully digitize a signal of bandwidth B, the sample rate must be:

Fs ≥ 2 × B    (Nyquist rate)

Sampling below this causes aliasing — high frequencies fold into low frequencies and cannot be recovered:

Original:  ~~~~ 800 Hz signal
Sampled at 1000 Hz:
  800 Hz appears as 200 Hz (1000 - 800 = 200)  ← aliased!
  Cannot tell them apart from samples alone

Anti-aliasing filter: a low-pass filter before the ADC that removes frequencies above Fs/2. Without it, noise and interference above Nyquist corrupt your measurements.

Practical rule: sample at 5-10× the signal bandwidth for clean reconstruction.

Filtering

Filters remove unwanted frequencies from a signal:

Filter TypePassesBlocksUse Case
Low-passBelow cutoffAbove cutoffNoise removal, smoothing sensor data
High-passAbove cutoffBelow cutoffRemove DC offset, detect edges
Band-passBetween two cutoffsOutside bandSelect frequency band (radio tuning)
Band-stop (notch)Outside bandWithin bandRemove specific interference (50/60 Hz mains)

FIR vs IIR Filters

AspectFIR (Finite Impulse Response)IIR (Infinite Impulse Response)
StructureUses only input samplesUses input + previous output (feedback)
StabilityAlways stableCan be unstable if poorly designed
PhaseCan be exactly linear phaseNonlinear phase (unless special design)
Order neededHigher (more coefficients)Lower (fewer coefficients for same performance)
ComputationMore multiplies per sampleFewer multiplies
Exampley[n] = Σ b[k]·x[n-k]y[n] = Σ b[k]·x[n-k] - Σ a[k]·y[n-k]
from scipy.signal import butter, lfilter
 
# Design a 4th-order Butterworth low-pass at 100 Hz (IIR)
b, a = butter(4, 100, btype='low', fs=1000)
filtered = lfilter(b, a, signal)

Convolution

The output of a linear system equals the input convolved with the impulse response:

y[n] = x[n] * h[n] = Σ x[k] · h[n-k]

In the frequency domain, convolution becomes multiplication: Y(f) = X(f) · H(f). This is why filters are designed in the frequency domain and applied via convolution (or equivalently, FFT → multiply → IFFT).

Windowing

The FFT assumes the signal is periodic. A finite segment that isn’t a perfect number of cycles causes spectral leakage — energy spreads to adjacent frequency bins.

Fix: multiply the signal by a window function before FFT:

WindowSidelobe LevelMain Lobe WidthUse
Rectangular (none)-13 dBNarrowestMaximum frequency resolution
Hann-31 dBMediumGeneral purpose
Hamming-43 dBMediumAudio, vibration analysis
Blackman-58 dBWidestHighest dynamic range
windowed = signal * np.hanning(len(signal))
fft_vals = np.fft.rfft(windowed)

Z-Transform

The discrete-time equivalent of the Laplace transform. Maps discrete sequences to the z-domain:

X(z) = Σ x[n] · z⁻ⁿ

Transfer function of a discrete filter: H(z) = Y(z)/X(z). Poles inside the unit circle = stable. Directly connected to Digital Control — the z-transform describes both digital filters and discrete controllers.

Applications

DomainSignal Processing Use
AudioFFT for spectrum analysis, FIR/IIR equalization, compression
CommunicationsModulation/demodulation, channel equalization, error correction
Radar/SonarMatched filtering, Doppler estimation, beamforming
BiomedicalECG filtering, EEG frequency analysis, image reconstruction
ControlAnti-aliasing before ADC, digital PID filtering, system identification
EmbeddedDigital filtering of sensor data, motor vibration analysis