How to Create Simple Band-Pass and Band-Reject Filters

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.

Figure 1. Band-pass filter frequency response on a linear (left) and logarithmic (right) scale.Figure 1. Band-pass filter frequency response on a linear (left) and logarithmic (right) scale.

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\).

Figure 2. Band-reject filter frequency response on a linear (left) and logarithmic (right) scale.Figure 2. Band-reject filter frequency response on a linear (left) and logarithmic (right) scale.

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!

Filter designer.Filter designer.

Henry (not verified)

Mon, 07/08/2019 - 03:57

# 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.

Add new comment

The content of this field is kept private and will not be shown publicly.

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Submitted on 10 May 2014