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.

Peter (not verified)

Mon, 02/10/2020 - 20:44

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.

Manpreet Kaur (not verified)

Mon, 05/11/2020 - 12:49

Hi Tom,

Thanks for the article. It's very much helpful:)
But the results(I mean Filter Plots), I got, are pretty much different as shown above with same Cutoff Frequency.

Thanks again:)

Add new comment

The content of this field is kept private and will not be shown publicly.
Spam avoidance measure, sorry for this.

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