1
$\begingroup$

I'm designing a bandpass filter in scipy following the cookbook. However, if I decrecrease the filtering frequencies too much I end up with garbage at high order filters. What am I doing wrong?

from scipy.signal import butter, lfilter

def butter_bandpass(lowcut, highcut, fs, order=5):
    nyq = 0.5 * fs
    low = lowcut / nyq
    high = highcut / nyq
    b, a = butter(order, [low, high], btype='band')
    return b, a

if __name__ == "__main__":
    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.signal import freqz  
    # Sample rate and desired cutoff frequencies (in Hz).
    fs = 25
    # Plot the frequency response for a few different orders.
    plt.figure(1)
    plt.clf()
    for order in [1, 3, 5, 6, 9]:
        b, a = butter_bandpass(0.5, 4, fs, order=order)
        w, h = freqz(b, a, worN=2000)#np.logspace(-4, 3, 2000))
        plt.semilogx((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)  
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Gain')
    plt.grid(True)
    plt.legend(loc='best')

    plt.figure(2)
    plt.clf()
    for order in [1, 3, 5, 6, 9]:
        b, a = butter_bandpass(0.05, 0.4, fs, order=order)
        w, h = freqz(b, a, worN=2000)#np.logspace(-4, 3, 2000))
        plt.semilogx((fs * 0.5 / np.pi) * w, abs(h), label="order = %d" % order)  
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Gain')
    plt.grid(True)
    plt.legend(loc='best')

    plt.show()

fs = 25, low = 0.5, high = 4 fs = 25, low = 0.05, high = 0.4

$\endgroup$
2
$\begingroup$

You're probably running into numerical precision issues on the higher-order filters with sharp cutoffs. It could be a limitation of the butter function in SciPy, or it could be due to the filter structure that it's using when evaluating the frequency response. A better way of implementing high-order IIR filters is using a second-order sections (SOS) structure.

A little Googling suggests that SciPy doesn't yet support converting a filter to its SOS representation. Furthermore, there are apparently some issues with how filter parameters are handled internally that cause bad results with high-order filters. That sounds like what you're describing. These are apparently in the process of being addressed.

With that said, I came across this example that shows how you might implement an SOS structure using SciPy. I haven't tested the code at all, so there's no warranty, but it may be worth a look.

$\endgroup$
1
  • $\begingroup$ Yes, I had found the github discussion too. Thanks for the example $\endgroup$ – Fra Feb 20 '14 at 1:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.