Working Through DSP Theory and Implementation
Digital signal processing sits somewhere between pure mathematics and hardware engineering, which means most textbooks treat it as if you have infinite precision and zero real-world constraints. That is why applied work tends to fall apart fast once you leave the idealized examples. I spent several years debugging filter implementations on embedded systems where the theory looked correct but the output was garbage, and the problem was always some combination of finite word length, aliasing, or a misunderstanding of how the DFT actually behaves with real data. The core concepts are straightforward enough. You take a continuous signal, sample it at some rate, and manipulate the discrete representation using operations like convolution, filtering, and transformation. The Fourier transform is the backbone of almost everything, breaking a time-domain signal into its frequency components. A lowpass filter removes high frequencies. A highpass filter removes the low end. A bandpass lets through a specific range. This is the basic vocabulary. Where people trip up is in the implementation details. I worked on a project where we needed to extract a 50 Hz vibration signature from accelerometer data sampled at 10,000 Hz. The theoretical approach was clean: design a bandpass filter around 50 Hz, apply it with a forward-backward zero-phase method to avoid phase distortion, then compute the power spectral density. The actual implementation required handling several non-obvious problems. The first was anti-aliasing. The sensor had a built-in antialiasing filter rated at 2.5 kHz, but our sampling rate was 10 kHz. Without additional analog filtering before the ADC, energy above 5 kHz would fold back into the spectrum and contaminate the measurement. I added a simple RC lowpass stage at 4 kHz before the signal reached the acquisition board, which eliminated the aliasing artifacts that were showing up as spurious peaks in the spectrum.
Another issue was filter design itself. Using a standard windowed sinc approach gave me a filter with acceptable stopband attenuation but terrible group delay variation near the passband edges. For vibration analysis at 50 Hz with a narrow bandwidth requirement, that group delay ripple was introducing amplitude modulation artifacts that looked like actual vibration harmonics. I switched to an elliptic filter design with a carefully chosen ripple specification, which gave me a much sharper transition band with fewer coefficients. The tradeoff was increased sensitivity to coefficient quantization, but on a 32-bit floating point DSP that was not a practical concern. One of the more frustrating aspects of practical DSP is round-off noise and coefficient quantization. When you implement filters in fixed-point arithmetic, which is what you do on any microcontroller or FPGA, the multiplier and adder operations introduce quantization errors that accumulate. A second-order section cascade structure is standard for mitigating this, but even then you need to check your Q-factor. Filters with very high Q values, meaning very narrow bandwidths, can become unstable when coefficients are rounded to finite precision. I once had a 12th-order Butterworth lowpass filter oscillate at DC when deployed on a 16-bit fixed-point system. The fix was retuning the pole positions and switching to a biquad cascade with manual coefficient scaling to keep internal signals within the dynamic range of the processor. The discrete Fourier transform deserves a more careful explanation than most introductory material gives it. The DFT assumes your signal is periodic, repeating indefinitely. If your capture window does not contain an integer number of cycles for any frequency component, you get spectral leakage. This is not a bug in the transform. It is a consequence of analyzing a finite-length segment. Window functions like Hanning, Blackman-Harris, or flat-top reduce leakage at the cost of frequency resolution. The flat-top window is particularly useful when you need accurate amplitude measurement, which is common in calibration and vibration analysis. It has poor resolution but gives amplitude accuracy within 0.01 dB, whereas a Hanning window might introduce several percent error depending on the bin alignment.
Zero-padding is another technique that people misuse frequently. Adding zeros to the end of a signal before computing the FFT does not increase actual frequency resolution. It only interpolates between the existing DFT bins, giving you a smoother looking spectrum. I see engineers do this expecting to resolve two closely spaced frequencies, and when it fails they blame the hardware. The resolution limit is determined by the observation time, not the FFT size. If you need better resolution, record longer. Convolution in the time domain is mathematically equivalent to multiplication in the frequency domain, but the computational considerations are different. Direct time-domain convolution of an N-sample signal with an M-sample filter kernel takes O(NM) operations. Frequency-domain convolution using FFT takes O(N log N) plus the cost of two transforms. The crossover point depends on your specific processor and filter length, but for anything longer than roughly 64 taps, the FFT approach becomes more efficient. For real-time processing where you cannot afford the latency of a full FFT, overlap-save or overlap-add methods give you the efficiency of frequency-domain filtering while processing one block at a time. Adaptive filtering is where DSP gets genuinely interesting and genuinely difficult. The LMS algorithm is simple enough to implement in a few lines, but convergence speed depends heavily on the eigenvalue spread of the input signal autocorrelation matrix. White noise input converges quickly. Colored or correlated input, which is far more common, causes the adaptive filter to converge much slower along certain eigenvector directions. The normalized LMS variant handles this better by scaling the step size with the input power, but it still struggles with highly correlated signals. I worked on an echo cancellation system where the near-end speaker and the far-end playback were creating a highly correlated input to the adaptive filter. The standard NLMS algorithm would adapt for a few seconds after a call path switch and then drift back, never fully tracking the echo path. Switching to a full-step affine projection algorithm solved the problem, though it increased computational load by roughly three times.
Get the Full Details

Multirate signal processing, or sampling rate conversion, is another area where theory and practice diverge. Decimation and interpolation seem simple in principle, but practical implementations need anti-aliasing and image-rejection filters that are often overlooked. If you decimate by a factor of 4 without sufficient anti-aliasing filtering, high-frequency energy folds into your baseband and corrupts everything below the Nyquist of the new lower rate. A cascaded integrator-comb filter is an efficient choice for large decimation factors because it requires no multiplications, but it has poor stopband rejection. You typically pair it with a half-band FIR filter to clean up what the comb leaves behind. Phase response is something most beginners ignore until it breaks their system. Finite impulse response filters can be designed to have exactly linear phase, which means all frequency components are delayed by the same amount. This preserves waveform shape. Infinite impulse response filters, which include the standard Butterworth, Chebyshev, and elliptic designs, have nonlinear phase. The group delay varies across the passband, meaning different frequency components arrive at different times. For audio applications this can be audible. For data communications, it causes intersymbol interference. For vibration analysis, it distorts transient waveforms. There is no free lunch here. If you need linear phase, use FIR. If you need sharp roll-off with few coefficients, use IIR and accept the phase distortion or apply digital phase compensation afterward. Sampling theorem is not just about picking a rate higher than twice your signal bandwidth. Real-world anti-aliasing filters are not ideal brick walls. You need guard band between your signal bandwidth and the Nyquist frequency to allow the analog filter to roll off gradually. A common rule of thumb is sampling at 2.5 to 3 times the maximum signal frequency, depending on how much attenuation your analog front end provides. I measured a datalogger setup where the manufacturer specified 2 kHz sampling for a 500 Hz signal. The analog anti-aliasing filter had a -3 dB point at 600 Hz and a rolloff of only 12 dB per octave. Energy from motor drive harmonics at 2 kHz and above was aliasing directly into the baseband, creating false peaks that looked like bearing defects. Doubling the sample rate to 4 kHz and adding a proper 4th-order active lowpass filter at 700 Hz eliminated the problem entirely.
When working with real sensors, you also need to think about the noise floor and dynamic range. An ADC might have 16 bits of resolution, which theoretically gives you 96 dB of dynamic range. But if your analog front end adds noise that fills only 12 bits of the ADC range, you have wasted the top four bits and gained nothing from the extra resolution. Proper gain staging is essential. I spent two weeks troubleshooting what I thought was a software bug before realizing the signal was being amplified too little relative to the ADC reference voltage. The quantization noise was swamping the actual signal. A simple gain adjustment in the analog stage before the ADC resolved the issue, and the effective resolution improved by about 8 bits. There is no single textbook or solution manual that covers all of these practical issues because they depend heavily on your specific application. The theory gives you the foundation, but the practice requires understanding your hardware constraints, your signal characteristics, and your performance requirements. Start with the specifications. Know what your signal looks like, what noise is present, what computational resources you have, and what accuracy you need. Then choose your algorithms accordingly, test them on real data, and iterate. The simulations in MATLAB or Python are useful for validation, but they rarely capture the imperfections you will encounter in production. Always verify with actual hardware before you commit to a design.