Start from the perfect low-pass and keep only its middle taps. Below, the cut keeps 21, then 41, then 81 taps. Watch the bracket at the edge shrink, and the dotted level at the top stay where it is.
Cut the ideal, and the edge ripples
12.4's ideal low-pass, Ω_c = 0.25π, moved to its centre and cut to N_h taps.
21 taps: the gain overshoots to 1.073 next to the edge and ripples on both sides.
Describe this picture
Two stacked panels for 12.4’s ideal low-pass, , moved to its centre and cut to taps. The taps panel plots against , the samples from the centre: the kept taps are stems with dot heads, and the ideal’s further values are faint open circles labelled “cut off”. The gain panel plots against in rad/sample. The ideal is a thin dashed box, and the design is a solid curve. A dotted level sits at the design’s highest value and is labelled with it, and a bracket labelled “edge” spans the edge from the 0.9 crossing to the 0.1 crossing. The readouts are the taps , the highest value and the edge width.
The clip lasts 13 s, with a blank caption while the taps change. At 21 taps the gain overshoots to 1.073 next to the edge and ripples on both sides; the edge width is rad/sample. The taps grow at both ends to 41, held at 5.25 s: the edge is twice as steep, , but the overshoot is still there, 1.099. Last the taps grow to 81: steeper again, , and the overshoot stays near 1.09, the 9 % of 7.3. The end caption adds that keeping taps is multiplying by a rectangular window. After the clip a slider named “Taps N_h” takes the odd values from 11 to 101: the arrow keys move it by 2, Page Up and Page Down by 10, and Home and End jump to 11 and 101. The choice is kept in the link, as cut.N.
Cut the ideal, and the edge ripples
In Filter specifications (18.1) we wrote down how close to ideal a low-pass has to be. Now I want to build one. The plan is the simplest I know: start from the perfect filter and keep the part of it that matters.
On the page Frequency response of discrete-time systems (12.4), the ideal low-pass with cutoff had this impulse response. I call it the desired response, :
It never ends in either direction, which is why it cannot be built. But its values shrink as moves away from 0, so the part near the peak carries most of it.
So let’s keep only that part. Shift to the right by samples, so that the kept part starts at , and keep taps:
The taps are symmetric about . By Linear-phase systems (17.3), the filter is then linear phase with a delay of samples, and its gain is the real zero-phase response .
The picture at the top of the page uses 12.4’s own example, , whose centre tap is . It cuts it to 21, 41 and then 81 taps. To compare the cuts I measure an edge width. It is the distance in from the last point before the cutoff where the gain is 0.9 to the first point after it where it is 0.1.
Watch what moves and what stays. The edge width roughly halves each time the taps double: it is about . The highest value hardly moves. It stays near 1.09, an overshoot of about 9 % of the jump from 1 to 0, which is the bump of Convergence and the Gibbs phenomenon (7.3).
After the clip, set the number of taps yourself with the slider. At 61 taps the highest value is 1.084 and the edge width 0.029π. Try 101: the edge narrows to 0.017π, and the highest value is still 1.086. Then try 11. The edge is now so wide, 0.179π, that the highest value, 1.078, is at and not next to the edge.
Why does the ripple stay? Keeping taps multiplies by a rectangular window: 1 on the kept taps and 0 everywhere else. By Properties of the DTFT (12.3), multiplying in time convolves the spectra. So the gain is the ideal box smeared by the rectangular window’s spectrum.
As grows, that spectrum’s main lobe narrows, so the edge gets steeper. Its side lobes narrow too, but they keep their height relative to the main lobe. They are what makes the ripple, so the ripple keeps its height as well.
A smoother window has smaller side lobes (Window functions compared, 15.2). So let’s multiply by one of those instead. Windows for filters are the symmetric kind: for to is symmetric about . In SciPy that is get_window(name, N_h, fftbins=False), and NumPy’s np.hanning is symmetric already.
15.2 used periodic windows, which are made for spectra. A symmetric window keeps the product symmetric, so the filter stays linear phase. That gives the window design:
The maths behind it · orthogonal projections
Keeping the first terms of an expansion in orthogonal functions is an orthogonal projection, and 7.3 showed it gives the smallest squared error. So the plain cut is the least-squares best -tap filter for the ideal box over the whole band. A window gives that up to shrink the worst error instead. Optimal FIR design (19.3) makes both choices exact.
A window trades a wider edge for less ripple
The instrument below keeps the 41 taps and changes only the window: rectangular, which is the plain cut, then Hann, Hamming and Blackman. It is like sanding a sawn edge. The splinters go, and the edge gets rounder.
I write for a design’s ripple: the largest distance of its gain from 1 in the pass band, or from 0 in the stop band. For a window design the ripples in the two bands come out nearly equal, so one describes both. The instrument gives the stop band’s ripple in dB, .
Its transition is measured with that . It runs from the last below the cutoff where the gain is within of 1 to the first above it where the gain is within of 0. It is not the edge width of the first instrument, which used the fixed levels 0.9 and 0.1.
A window trades a wider edge for less ripple
The same 41 taps of the ideal low-pass (Ω_c = 0.25π), multiplied by four windows.
Rectangular, the plain cut: ripple up to −21.7 dB, and the narrowest transition, 0.047π.
Describe this picture
Two stacked panels for the same 41 taps of the ideal low-pass (), multiplied by four windows. The taps panel draws against the sample as stems with dot heads, with the window, scaled to the panel, as a dashed curve labelled with its name. The gain panel plots the gain from −100 to 5 dB against in rad/sample as a solid curve. A dotted level sits at the stop band’s highest peak and is labelled with it, and a bracket labelled “transition” spans the transition. The readouts are the window, the stop band and the transition.
The clip lasts 17 s and cross-fades from window to window, with a blank caption during each cross-fade. Rectangular, the plain cut, has ripple up to −21.7 dB and the narrowest transition, 0.047π. At 5.45 s comes Hann: the taps taper to 0 at the ends, the ripple falls to −44.0 dB, and the transition widens to 0.156π. At 9.65 s Hamming reaches −52.3 dB with a transition of 0.165π. At the end Blackman reaches −75.6 dB, but with a transition of 0.294π, six times the plain cut’s. After the clip the four windows are buttons in a group named “Window”; the arrow keys move between them, and Home and End jump to the first and the last. Each choice shows that window’s caption from the clip, and it is kept in the link, as trade.w.
After the clip, press each window’s button to compare them yourself. Notice that Blackman’s stop band is 54 dB below the plain cut’s, while its transition is six times as wide.
A window’s side lobe is not the filter’s ripple
Hann’s highest side lobe is −31.5 dB (15.2), yet the Hann filter reaches −44 dB. Why the difference? The convolution adds up the window’s spectrum over a stretch as wide as the box. As crosses the edge, that stretch slides off , so the gain there is a running area under .
A running area is set by how much area the side lobes hold, not by how tall the tallest one is. Side lobes that alternate in sign and shrink quickly add up to very little. So each window reaches more as a filter than its side lobe suggests.
The transition shrinks like , so I give it as a multiple of . The table puts the textbook values (Oppenheim and Schafer, table 7.2) beside my own measurements at 201 taps, with each window’s highest side lobe from 15.2.
| Window | Highest side lobe | Stop band (textbook) | Stop band (201 taps) | Transition × (textbook) | Transition × (201 taps) |
|---|---|---|---|---|---|
| rectangular | −13.3 dB | −21 dB | −21.2 dB | ||
| Hann | −31.5 dB | −44 dB | −43.9 dB | ||
| Hamming | −42.7 dB | −53 dB | −53.7 dB | ||
| Blackman | −58.1 dB | −74 dB | −75.3 dB |
Now let’s size a Hann design for the running spec of 18.1. It needs 40 dB, and Hann reaches 44. The transition is , so , which gives and . The shortest Hann design that really passes has 50 taps: the table is a guide, not a guarantee.
From the spec to the taps
The table makes you pick from four windows with fixed ripples. The Kaiser window of 15.2 has a knob, , and Kaiser found two formulas that turn a spec straight into and a length (Oppenheim and Schafer, §7.6). A window design has one ripple in both bands, so take the smaller of and , and .
For from 21 to 50 dB, which covers the running spec, the first formula is
Above 50 dB it is . Below 21 dB it is , the rectangular window, which reaches 21 dB by itself. The second formula gives the length:
These are not 15.2’s side-lobe rule. There was chosen for a window’s side lobe; here it is chosen for a filter’s ripple. Place the cutoff in the middle of the transition, . In SciPy, kaiserord gives both numbers and firwin gives the taps.
One difference from the first two instruments: firwin scales the taps so that the gain at is exactly 1. The plain product does not. Think of the formulas as a recipe’s baking time. It is a good start, but you still check the cake. Below, the formulas size a design for the running spec, and the zones check it.
From the spec to the taps
The running spec of 18.1. Kaiser's formulas choose β and N_h; the design is checked against the zones.
Describe this picture
Two stacked panels for the running spec of 18.1, where Kaiser’s formulas choose and and the design is checked against the zones. The taps panel draws against the sample as stems with dot heads; its range grows with the design. The gain panel is the spec drawing of 18.1: the gain from −80 to 5 dB against frequency from 0 to 4000 Hz, with the running spec’s forbidden zones hatched and “−40 dB” at the stop edge. The design is a solid curve, and a cross marks its worst stop-band point when it fails. The readouts are , the taps and the worst stop-band level with a word, such as “−39.8 dB, fails”. Each reads “not yet” until its number exists: once the zones are in, the length when the taps start to grow, and the worst level when the curve is complete.
The clip lasts 13 s. First the zones fade in. The stricter ripple is the stop band’s, 0.01, so dB, and the transition is 500 Hz, ; Kaiser’s first formula gives . Then the second formula gives the length, 37 taps, which grow from the centre outwards while the curve draws. At 7 s the gain at 1500 Hz is −39.8 dB, 0.2 dB short of the spec: the formula is an estimate. Then the design morphs to 38 taps: −41.7 dB, with the pass band within 0.012 of 1. It passes, with a delay of 18.5 samples, 2.3 ms. After the clip the stop edge is a handle named “Stop edge”, from 1100 to 2500 Hz in steps of 25 Hz, with a value like “1500 Hz: 38 taps”; the pass edge stays at 1000 Hz. The arrow keys move the edge by 25 Hz, Page Up and Page Down by 100 Hz, and Home and End jump to 1100 and 2500 Hz. The position is kept in the link, as kaiser.fstop.
The formula was 0.2 dB short, and one more tap fixed it. Notice that 38 is even, so the centre falls between two taps. By 17.3 an even-length symmetric filter has a zero forced at , but a low-pass does not mind a zero at .
After the clip, drag the stop edge yourself, or use the arrow keys; the pass edge stays at 1000 Hz. The widget takes Kaiser’s length and adds one tap at a time until the design passes. stays 3.40, because stays 40 dB. At 1250 Hz Kaiser’s estimate is 73 taps, and 74 pass. At 2000 Hz, 19 taps pass, as estimated.
Halve the transition and the length doubles: 37 taps at 500 Hz, 73 at 250 Hz. The estimate is right or one tap short across the whole range.
One low-pass, four filters
The other three shapes can be built from low-passes. Start with a high-pass. Subtract the low-pass from a pure delay:
The delay passes every frequency with gain 1 and the same delay , so the high-pass’s zero-phase response is . Where the low-pass passes, the high-pass stops, and the other way round.
This needs an odd , so that is a whole number of samples. There is a second reason. An even-length symmetric filter has a zero forced at (17.3), so it cannot pass , and SciPy’s firwin refuses an even-length high-pass.
Subtracting from 1 turns the low-pass’s pass-band ripple into the high-pass’s stop-band ripple. So design the low-pass to the stricter . A different route also works: by 12.3, multiplying by slides the response by , so is a high-pass with cutoff .
A band-pass from to is a low-pass with cutoff minus a low-pass with cutoff , both with the same and window: . A band-stop is minus a band-pass. The figure builds all four from one 39-tap Kaiser design.
Worked example
Let’s redo the page’s numbers by hand where we can, and with SciPy where we can’t.
1. The ideal, cut. With and 41 taps, and the centre tap is . Every fourth tap from the centre is 0, because . The gain peaks at 1.099, dips to −0.082 and has an edge width of 0.045π.
That dip, 0.0825 below zero, is the rectangular window’s ripple in the second instrument: dB.
2. Kaiser for the running spec. The stop band’s 0.01 is stricter than the pass band’s 0.05, so dB. Then . The transition is rad/sample.
The length formula gives , so and . The cutoff goes in the middle of the transition, Hz. There 37 taps fail by a little, −39.80 dB at 1500 Hz. 38 taps pass, at −41.68 dB.
The 38 taps have their centre between and , which both equal 0.3009. The ideal there, half a sample from its centre, is , so the window and the scaling barely touch the middle. The end taps are −0.0016.
3. Other windows on the running spec. I tried firwin with each window and growing until the design passed. The table gives the shortest length that passes, and the shortest odd one. Kaiser’s tuned saves 12 taps over Hann.
| Window | Shortest that passes | Shortest odd |
|---|---|---|
| Hann | 50 | 51 |
| Hamming | 50 | 51 |
| Blackman | 66 | 67 |
| Kaiser, | 38 | 39 |
4. A high-pass from the 39-tap low-pass. The low-pass’s centre tap is 0.3139, so the high-pass’s centre tap is . All the other taps just change sign. The low-pass has pass-band deviation 0.0136 and stop band −40.57 dB. The high-pass swaps them: pass-band deviation 0.0094, and stop band dB.
The band-pass’s centre tap is , and the band-stop’s is . Every cutoff gain is 0.50 to two decimals.
5. The window table at 201 taps. I used firwin(201, 0.25, window=name) and the rules of the second instrument: the ripple beyond the first null after the cutoff, and the transition between the points within that ripple of 1 and of 0. That gave stop bands of −21.2, −43.9, −53.7 and −75.3 dB, and transitions of , , and over 200.
Where you’ll meet this
The window method is the default FIR design in SciPy (firwin) and MATLAB (fir1). It makes anti-alias and decimation filters (Downsampling and decimation, 22.1), audio crossovers and biomedical filters. Reach for it wherever a quick, reliable linear-phase design is enough.
When every tap counts, Optimal FIR design (19.3) meets the same spec with fewer. Frequency-sampling design (19.2) designs from samples of the gain instead. Whether an FIR is the right choice at all is Choosing FIR or IIR (20.6), and long FIRs run fast with Fast convolution (14.3).
The maths behind it · kernel smoothing
In kernel smoothing, a hard-edged box kernel makes a fitted curve ring next to jumps. A smooth kernel, such as the Epanechnikov or the Gaussian, trades that ringing for blur. A window on is the same trade, with the transition width as the blur.
Reference card
| Quantity | Formula | Notes |
|---|---|---|
| Window design | , | symmetric : linear phase |
| Ideal low-pass | , | mid-transition |
| Plain cut | ripple about 9 % at any | 7.3’s Gibbs bump |
| Window as filter | stop band: rectangular −21, Hann −44, Hamming −53, Blackman −74 dB | transition , , , over |
| Kaiser | for 21 to 50 dB; above | |
| Kaiser length | an estimate: check, add a tap | |
| High-pass | , odd | ripples swap bands |
| Band-pass | band-stop: |