*Summary: This article shows how to create a simple band-pass filter that passes frequencies between the cutoff frequencies \(f_L\) and \(f_H\), and rejects frequencies outside of that interval. It also shows how to create a band-reject filter for those cutoff frequencies. The article is complemented by a Filter Design tool that allows you to create your own custom versions of the example filters that are shown below, and download the resulting filter coefficients.*

*Band-pass* and *band-reject* filters can be created by combining *low-pass* and *high-pass* filters. To create these in the first place, have a look at How to Create a Simple Low-Pass Filter and How to Create a Simple High-Pass Filter.

To create band-pass and band-reject filters, you need *two* cutoff frequencies, a lower limit \(f_L\) and a higher limit \(f_H\). The combined filters inherit the *transition bandwidth* (or *roll-off*), which might be different at each end, from the low-pass and high-pass filters that were used to build it.

The *windowed-sinc* filters that are described in this article are both examples of *Finite Impulse Response* (*FIR*) filters.

## Band-Pass Filter

A band-pass filter *passes* frequencies between the lower limit \(f_L\) and the higher limit \(f_H\), and *rejects* other frequencies. If you don’t create a specific filter for this, you can get this result in two steps. In the first step, you apply a low-pass filter with cutoff frequency \(f_H\),

\[x_\mathrm{lpf,H}[n]=x[n]*h_\mathrm{lpf,H}[n],\]

where \(x[n]\) is the original signal, \(h_\mathrm{lpf,H}[n]\) is the low-pass filter with cutoff frequency \(f_H\), and \(x_\mathrm{lpf,H}[n]\) is the low-pass-filtered signal. The asterisk represents *convolution*. The result is a signal in which the rejection of frequencies larger than \(f_H\) has been taken care of. You can then filter that signal again, with a high-pass filter with cutoff frequency \(f_L\),

\[x_\mathrm{bp,LH}[n]=x_\mathrm{lpf,H}[n]*h_\mathrm{hpf,L}[n],\]

where \(h_\mathrm{hpf,L}[n]\) is the high-pass filter with cutoff frequency \(f_L\), and \(x_\mathrm{bp,LH}[n]\) is the required band-pass-filtered signal.

However, you can do better and combine both of these filters into a single one. How does that work? You can write

\[x_\mathrm{bp,LH}[n]=(x[n]*h_\mathrm{lpf,H}[n])*h_\mathrm{hpf,L}[n]=x[n]*(h_\mathrm{lpf,H}[n]*h_\mathrm{hpf,L}[n]),\]

where the last step follows from the associative property of convolution. This means that the required band-pass filter is

\[h_\mathrm{bp,LH}[n]=h_\mathrm{lpf,H}[n]*h_\mathrm{hpf,L}[n].\]

Hence, a band-pass filter can be created from a low-pass and a high-pass filter with appropriate cutoff frequencies by *convolving* the two filters. The example band-pass filter of Figure 1 has \(f_L=0.1\) and \(f_H=0.4\), with \(b=0.08\) as in the articles on low-pass and high-pass filters.

## Band-Reject Filter

A band-reject filter *rejects* frequencies between the lower limit \(f_L\) and the higher limit \(f_H\), and *passes* other frequencies. As for the band-pass filter, you can get this result in two steps. In the first step, you apply a low-pass filter with cutoff frequency \(f_L\),

\[x_\mathrm{lpf,L}[n]=x[n]*h_\mathrm{lpf,L}[n],\]

where \(x[n]\) is the original signal, \(h_\mathrm{lpf,L}[n]\) is the low-pass filter with cutoff frequency \(f_L\), and \(x_\mathrm{lpf,L}[n]\) is the low-pass-filtered signal.

The result is a signal in which the frequencies in the rejection interval have been eliminated, but in which the frequencies higher than \(f_H\) are also gone. This can be corrected by filtering the original signal again, with a high-pass filter with cutoff frequency \(f_H\), and adding the result to the first signal,

\[x_\mathrm{br,LH}[n]=x_\mathrm{lpf,L}+x[n]*h_\mathrm{hpf,H}[n],\]

where \(h_\mathrm{hpf,H}[n]\) is the high-pass filter with cutoff frequency \(f_H\), and \(x_\mathrm{br,LH}[n]\) is the required band-reject-filtered signal.

You can again to better and combine both operations into a single filter. You can write

\[x_\mathrm{br,LH}[n]=x[n]*h_\mathrm{lpf,L}[n]+x[n]*h_\mathrm{hpf,H}[n]=x[n]*(h_\mathrm{lpf,L}[n]+h_\mathrm{hpf,H}[n],\]

where the last step follows from the distributive property of convolution. This means that the required band-reject filter is

\[h_\mathrm{br,LH}[n]=h_\mathrm{lpf,L}[n]+h_\mathrm{hpf,H}[n].\]

Hence, a band-reject filter can be created from a low-pass and a high-pass filter with appropriate cutoff frequencies by *adding* the two filters. The example band-reject filter of Figure 2 has \(f_L=0.1\) and \(f_H=0.4\), with again \(b=0.08\).

## Python Code

In Python, all these formulas can be implemented concisely. The first code fragment shows how to implement a band-pass filter.

from __future__ import division import numpy as np fL = 0.1 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). fH = 0.4 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). b = 0.08 # Transition band, as a fraction of the sampling rate (in (0, 0.5)). N = int(np.ceil((4 / b))) if not N % 2: N += 1 # Make sure that N is odd. n = np.arange(N) # Compute a low-pass filter with cutoff frequency fH. hlpf = np.sinc(2 * fH * (n - (N - 1) / 2)) hlpf *= np.blackman(N) hlpf = hlpf / np.sum(hlpf) # Compute a high-pass filter with cutoff frequency fL. hhpf = np.sinc(2 * fL * (n - (N - 1) / 2)) hhpf *= np.blackman(N) hhpf = hhpf / np.sum(hhpf) hhpf = -hhpf hhpf[(N - 1) // 2] += 1 # Convolve both filters. h = np.convolve(hlpf, hhpf)

The second code fragment shows how to implement a band-reject filter.

from __future__ import division import numpy as np fL = 0.1 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). fH = 0.4 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). b = 0.08 # Transition band, as a fraction of the sampling rate (in (0, 0.5)). N = int(np.ceil((4 / b))) if not N % 2: N += 1 # Make sure that N is odd. n = np.arange(N) # Compute a low-pass filter with cutoff frequency fL. hlpf = np.sinc(2 * fL * (n - (N - 1) / 2)) hlpf *= np.blackman(N) hlpf /= np.sum(hlpf) # Compute a high-pass filter with cutoff frequency fH. hhpf = np.sinc(2 * fH * (n - (N - 1) / 2)) hhpf *= np.blackman(N) hhpf /= np.sum(hhpf) hhpf = -hhpf hhpf[(N - 1) // 2] += 1 # Add both filters. h = hlpf + hhpf

Applying the filter \(h\) to a signal \(s\) is done by convolution, as for the low-pass and high-pass filters, and can again be as simple as writing the single line:

s = np.convolve(s, h)

## Filter Design Tool

This article is complemented with a Filter Design tool. Experiment with different values for \(f_L\) and \(f_H\), visualize the resulting filters, and download the filter coefficients. Try it now!

# Add both filters.

h = hlpf + hhpf

This line:

h = hlpf + hhpf

should be changed to:

h = np.convolve(hlpf, hhpf)

No, the code as given is correct. Note that the the filters are combined in a different way for band-pass and band-reject. The mathematical reasoning behind this is given in the body of the article.

Thanks so much for this tutorial! Its very helpful. One quick comment:

Based on running this code, it seems like there could be a slight correction

`hhpf[(N - 1) // 2] += 1`

should be

`hhpf[(N - 1) // 2 + 1] += 1`

Thanks for your kind words! I think the code is correct as I wrote it. Let's look at an example: I make sure that

`N`

is odd, for example,`N=5`

. This means that the coefficients are numbered 0, 1, 2, 3, 4.`(N-1)//2`

equals two, so I indeed add one to the middle coefficient.## Add new comment