[SciPy-User] signal.firls
Gregory Allen
gallen at arlut.utexas.edu
Tue Apr 28 15:09:20 EDT 2015
I was surprised to find that there was no signal.firls function in scipy for designing FIR filters using least-squares error minimization.
I wrote this based on a post in this list [http://mail.scipy.org/pipermail/scipy-user/2009-November/023101.html], added band weights, and fleshed it out in the style of signal.remez. It’s not as full-featured as the matlab version, but it solved a need for me. :)
It could be pasted at the bottom of signal/fir_filter_design.py. I include it here in case it is of any use to someone else, or to be included in scipy.
Thanks,
-Greg
---
import numpy as np
from scipy.special import sinc
def firls(numtaps, bands, desired, weight=None, Hz=1):
"""
FIR filter design using least-squares error minimization.
Calculate the filter coefficients for the finite impulse response
(FIR) filter which has the best approximation to the desired
frequency response described by `bands` and `desired` in the
least squares sense.
Parameters
----------
numtaps : int
The number of taps in the FIR filter. `numtaps` must be odd.
bands : array_like
A monotonic sequence containing the band edges in Hz.
All elements must be non-negative and less than half the sampling
frequency as given by `Hz`.
desired : array_like
A sequence half the size of bands containing the desired gain
in each of the specified bands.
weight : array_like, optional
A relative weighting to give to each band region. The length of
`weight` has to be half the length of `bands`.
Hz : scalar, optional
The sampling frequency in Hz. Default is 1.
Returns
-------
out : ndarray
A rank-1 array containing the coefficients of the optimal
(in a least squares sense) filter.
Example
-------
We want to construct a filter with a passband at 0.2-0.3 Hz, and
stop bands at 0-0.1 Hz and 0.4-0.5 Hz. Note that this means that the
behavior in the frequency ranges between those bands is unspecified and
may overshoot.
>>> from scipy import signal
>>> bpass = signal.firls(71, [0, 0.1, 0.2, 0.3, 0.4, 0.5], [0, 1, 0])
>>> freq, response = signal.freqz(bpass)
>>> ampl = np.abs(response)
>>> import matplotlib.pyplot as plt
>>> fig = plt.figure()
>>> ax1 = fig.add_subplot(111)
>>> ax1.semilogy(freq/(2*np.pi), ampl, 'b-') # freq in Hz
>>> plt.show()
"""
if numtaps%2 == 0:
raise ValueError("numtaps must be odd.")
L = (numtaps-1)//2
# normalize bands and make it 2 columns
bands = np.asarray(bands).flatten()/Hz
if len(bands)%2 == 1:
raise ValueError("bands must contain frequency pairs.")
bands = bands.reshape(-1,2)
# check remaining params
if len(bands) != len(desired):
raise ValueError("desired must have one entry per band.")
if weight is None:
weight = np.ones_like(desired)
# set up the linear matrix equation to be solved, Ax = b
k = np.arange(L+1)[np.newaxis]
m = k.T
A,b = 0,0
for i, (f0,f1) in enumerate(bands):
Ai = f1 * (sinc(2*(m+k)*f1) + sinc(2*(m-k)*f1)) \
- f0 * (sinc(2*(m+k)*f0) + sinc(2*(m-k)*f0))
bi = desired[i] * (2*f1*sinc(2*m*f1) - 2*f0*sinc(2*m*f0))
A += Ai * abs(weight[i]**2)
b += bi * abs(weight[i]**2)
# solve and return
x = np.linalg.solve(A,b).squeeze()
h = np.hstack((x[:0:-1]/2, x[0], x[1:]/2))
return h
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4876 bytes
Desc: not available
URL: <http://mail.scipy.org/pipermail/scipy-user/attachments/20150428/9166ca22/attachment.bin>
More information about the SciPy-User
mailing list