This is a pre-release version (latest).Go to latest (1.18.0)
{ } Raw JSON

bundles / scipy latest / scipy / signal / _fir_filter_design / firwin

function

scipy.signal._fir_filter_design:firwin

source: build-install/usr/lib/python3.14/site-packages/scipy/signal/_fir_filter_design.py:253

Signature

def firwin(numtaps, cutoff, *, width = None, window = hamming, pass_zero = True, scale = True, fs = None)

Summary

FIR filter design using the window method.

Extended Summary

This function computes the coefficients of a finite impulse response filter. The filter will have linear phase; it will be Type I if numtaps is odd and Type II if numtaps is even.

Type II filters always have zero response at the Nyquist frequency, so a ValueError exception is raised if firwin is called with numtaps even and having a passband whose right end is at the Nyquist frequency.

Parameters

numtaps : int

Length of the filter (number of coefficients, i.e., the filter order + 1). numtaps must be odd if a passband includes the Nyquist frequency.

cutoff : float or 1-D array_like

Cutoff frequency of filter (expressed in the same units as fs) or an array of cutoff frequencies (that is, band edges). In the former case, as a float, the cutoff frequency should correspond with the half-amplitude point, where the attenuation will be -6 dB. In the latter case, the frequencies in cutoff should be positive and monotonically increasing between 0 and fs/2. The values 0 and fs/2 must not be included in cutoff. It should be noted that this is different from the behavior of iirdesign, where the cutoff is the half-power point (-3 dB).

width : float or None, optional

If not None, then a kaiser window is calculated where width specifies the approximate width of the transition region (expressed in the same unit as fs). This is achieved by utilizing kaiser_atten to calculate an attenuation which is passed to kaiser_beta for determining the β parameter for the kaiser window. In this case, the window argument is ignored.

window : str or tuple of str and parameter values, optional

Desired window to use. Default is 'hamming'. The window will be symmetric, unless a suffix '_periodic' is appended to the window name (e.g., 'hamming_periodic') Consult get_window for a list of windows and required parameters.

pass_zero : {True, False, 'bandpass', 'lowpass', 'highpass', 'bandstop'}, optional

Toggles the zero frequency bin (or DC gain) to be in the passband (True) or in the stopband (False). 'bandstop', 'lowpass' are synonyms for True and 'bandpass', 'highpass' are synonyms for False. 'lowpass', 'highpass' additionally require cutoff to be a scalar value or a length-one array. Default: True.

scale : bool, optional

Set to True to scale the coefficients so that the frequency response is exactly unity at a certain frequency. That frequency is either:

  • 0 (DC) if the first passband starts at 0 (i.e., pass_zero is True)

  • fs/2 (the Nyquist frequency) if the first passband ends at fs/2 (i.e., the filter is a single band highpass filter); center of first passband otherwise

fs : float, optional

The sampling frequency of the signal. Each frequency in cutoff must be between 0 and fs/2. Default is 2.

Returns

h : ndarray

FIR filter coefficients as 1d array with numtaps entries.

Raises

: ValueError

If any value in cutoff is less than or equal to 0 or greater than or equal to fs/2, if the values in cutoff are not strictly monotonically increasing, or if numtaps is even but a passband includes the Nyquist frequency.

Examples

The following example calculates frequency responses of a 30 Hz low-pass filter with various numbers of taps. The transition region width is 20 Hz. The upper plot shows the gain and the lower plot the phase. The vertical dashed line marks the corner frequency and the gray background the transition region.
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as signal
fs = 200  # sampling frequency and number of taps
f_c, width = 30, 20  # corner frequency (-6 dB gain)
taps = [20, 40, 60]  # number of taps
fg, (ax0, ax1) = plt.subplots(2, 1, sharex='all', layout="constrained",
                              figsize=(5, 4))
✓
ax0.set_title(rf"Response of ${f_c}\,$Hz low-pass Filter with " +
              rf"${width}\,$Hz transition region")
ax0.set(ylabel="Gain in dB", xlim=(0, fs/2))
ax1.set(xlabel=rf"Frequency $f\,$ in hertz (sampling frequency $f_S={fs}\,$Hz)",
        ylabel="Phase in Degrees")
for n in taps:  # calculate filter and plot response:
    bb = signal.firwin(n, f_c, width=width, fs=fs)
    f, H = signal.freqz(bb, fs=fs)  # calculate frequency response
    H_dB, H_ph = 20 * np.log10(abs(H)), np.rad2deg(np.unwrap(np.angle(H)))
    H_ph[H_dB<-150] = np.nan
    ax0.plot(f, H_dB, alpha=.5, label=rf"{n} taps")
    ax1.plot(f, H_ph, alpha=.5, label=rf"{n} taps")
for ax_ in (ax0, ax1):
    ax_.axvspan(f_c-width/2, f_c+width/2,color='gray', alpha=.25)
    ax_.axvline(f_c, color='gray', linestyle='--', alpha=.5)
    ax_.grid()
    ax_.legend()
✗
<matplotlib.patches.Rectangle object at 0x7f3f6834bed0>
<matplotlib.lines.Line2D object at 0x7f3f68ed1be0>
<matplotlib.legend.Legend object at 0x7f3f68ed1fd0>
<matplotlib.patches.Rectangle object at 0x7f3f68ea0cd0>
<matplotlib.lines.Line2D object at 0x7f3f68fd9e80>
<matplotlib.legend.Legend object at 0x7f3f68fdbcb0>
plt.show()
✓
fig-e027f7f4b01d2c3b.png
The plots show that with increasing number of taps, the suppression in the stopband increases and the (negative) slope of the phase steepens, which signifies a longer signal delay in the filter. Note that the plots contain numeric artifacts caused by the limited frequency resolution: The phase jumps are not real---in reality the phase is a straight line with a constant negative slope. Furthermore, the gains contain zero values (i.e., -∞ dB), which are also not depicted. The second example determines the frequency responses of a 30 Hz low-pass filter with 40 taps. This time the width of the transition region varies:
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as signal
fs = 200  # sampling frequency and number of taps
n, f_c = 40, 30  # number of taps and corner frequency (-6 dB gain)
widths = (2, 10, 25)  # width of transition region
fg, ax = plt.subplots(1, 1, layout="constrained",)
✓
ax.set(title=rf"Response of {n}-tap ${f_c}\,$Hz low-pass Filter",
       xlabel=rf"Frequency $f\,$ in hertz (sampling frequency $f_S={fs}\,$Hz)",
       ylabel="Gain in dB", xlim=(0, fs/2))
ax.axvline(f_c, color='gray', linestyle='--', alpha=.7)  # mark corner frequency
for width in widths:  # calculate filter and plot response:
    bb = signal.firwin(n, f_c, width=width, fs=fs)
    f, H = signal.freqz(bb, fs=fs)  # calculate frequency response
    H_dB= 20 * np.log10(abs(H))  # convert to dB
    ax.plot(f, H_dB, alpha=.5, label=rf"width$={width}\,$Hz")
✗
[<matplotlib.lines.Line2D object at 0x7f3f68269e80>]
[<matplotlib.lines.Line2D object at 0x7f3f69162e40>]
[<matplotlib.lines.Line2D object at 0x7f3f69162cf0>]
ax.grid()
✓
ax.legend()
✗
<matplotlib.legend.Legend object at 0x7f3f682697f0>
plt.show()
✓
fig-a9fd0371cc89902d.png
It can be seen in the plot above that with increasing width of the transition region the suppression in the stopband increases. Since the phase does not vary, it is not depicted. Instead of defining a transition region width, a window function can also be specified. The plot below depicts the response of an 80 tap band-pass filter having a passband ranging form 40 Hz to 60 Hz (denoted by a gray background) with different windows:
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as signal
fs, n = 200, 80  # sampling frequency and number of taps
cutoff = [40, 60]  # corner frequencies (-6 dB gain)
windows = ('boxcar', 'hamming', 'hann', 'blackman')
fg, ax = plt.subplots(1, 1, layout="constrained")  # set up plotting
✓
ax.set(title=rf"Response of {n}-tap Filter with ${cutoff}\,$Hz passband",
       xlabel=rf"Frequency $f\,$ in hertz (sampling frequency $f_S={fs}\,$Hz)",
       ylabel="Gain in dB", xlim=(0, fs/2))
ax.axvspan(*cutoff, color='gray', alpha=.25)  # mark passband
for win in windows:  # calculate filter and plot response:
    bb = signal.firwin(n, cutoff, window=win, pass_zero=False, fs=fs)
    f, H = signal.freqz(bb, fs=fs)  # calculate frequency response
    H_dB = 20 * np.log10(abs(H))  # convert to dB
    ax.plot(f, H_dB, alpha=.5, label=win)
✗
[<matplotlib.lines.Line2D object at 0x7f3f60171010>]
[<matplotlib.lines.Line2D object at 0x7f3f60171160>]
[<matplotlib.lines.Line2D object at 0x7f3f601712b0>]
[<matplotlib.lines.Line2D object at 0x7f3f60171400>]
ax.grid()
✓
ax.legend()
✗
<matplotlib.legend.Legend object at 0x7f3f685512b0>
plt.show()
✓
fig-5fd9dc6ef044e12c.png
The plot shows that the choice of window mainly influences the balance of the suppression in the stopband against the width of the transition region. Note that utilizing the `~scipy.signal.windows.boxcar` window corresponds to just truncating the ideal infinite impulse response to the length of `numtaps` samples. The last example illustrates how to use the `cutoff` and `pass_zero` parameters to create a low-pass, a high-pass, a band-stop and a band-pass filter. The desired ideal frequency gain is drawn as a gray dashed line whereas the response of the FIR filter is depicted as a blue continuous line:
from itertools import product
import matplotlib.pyplot as plt
import numpy as np
import scipy.signal as signal
cutoffs = [0.5, (.25, .75)]  # cutoff parameters
fg, axx = plt.subplots(4, 1, sharex='all', layout="constrained",
                       figsize=(5, 4))
✓
for ax, (cutoff, pass_zero) in zip(axx, product(cutoffs, (True, False))):
    ax.set(title=f"firwin(41, {cutoff=}, {pass_zero=}, fs=2)", ylabel="Gain")
    ax.set_yticks([0.5], minor=True)  # mark gain of 0.5 (= -6 dB)
    ax.grid(which='minor', axis='y')

    bb = signal.firwin(41, cutoff, pass_zero=pass_zero, fs=2)
    ff, HH = signal.freqz(bb, fs=2)
    ax.plot(ff, abs(HH), 'C0-', label="FIR Response")

    f_d = np.hstack(([0], np.atleast_1d(cutoff), [1]))
    H_d = np.tile([1, 0] if pass_zero else [0, 1], 2)[:len(f_d)]
    H_d[-1] = H_d[-2] # account for symmetry at Nyquist frequency
    ax.step(f_d, H_d, 'k--', where='post', alpha=.3, label="Desired Response")
axx[-1].set(xlabel=r"Frequency $f\,$ in hertz (sampling frequency $f_S=2\,$Hz)",
            xlim=(0, 1))
✗
[Text(0.5, 0, 'Frequency $f\\,$ in hertz (sampling frequency $f_S=2\\,$Hz)'), (0.0, 1.0)]
plt.show()
✓
fig-55ed6ede192524b8.png

See also

firls

FIR filter design using least-squares error minimization.

firwin2

Window method FIR filter design specifying gain-frequency pairs.

firwin_2d

2D FIR filter design using the window method.

minimum_phase

Convert a FIR filter to minimum phase

remez

Calculate the minimax optimal filter using the Remez exchange algorithm.

Aliases

  • scipy.signal.firwin

Referenced by