Signal Processing with Scipy: Linear Filters
Total Page:16
File Type:pdf, Size:1020Kb
PyData CookBook 1 Signal Processing with SciPy: Linear Filters Warren Weckesser∗ F Abstract—The SciPy library is one of the core packages of the PyData stack. focus on a specific subset of the capabilities of of this sub- It includes modules for statistics, optimization, interpolation, integration, linear package: the design and analysis of linear filters for discrete- algebra, Fourier transforms, signal and image processing, ODE solvers, special time signals. These filters are commonly used in digital signal functions, sparse matrices, and more. In this chapter, we demonstrate many processing (DSP) for audio signals and other time series data of the tools provided by the signal subpackage of the SciPy library for the design and analysis of linear filters for discrete-time signals, including filter with a fixed sample rate. representation, frequency response computation and optimal FIR filter design. The chapter is split into two main parts, covering the two broad categories of linear filters: infinite impulse response Index Terms—algorithms, signal processing, IIR filter, FIR filter (IIR) filters and finite impulse response (FIR) filters. Note. We will display some code samples as transcripts CONTENTS from the standard Python interactive shell. In each interactive Python session, we will have executed the following without Introduction....................1 showing it: IIR filters in scipy.signal ..........1 IIR filter representation.........2 >>> import numpy as np >>> import matplotlib.pyplot as plt Lowpass filter..............3 >>> np.set_printoptions(precision=3, linewidth=50) Initializing a lowpass filter.......5 Bandpass filter..............5 Also, when we show the creation of plots in the transcripts, we Filtering a long signal in batches....6 won’t show all the other plot commands (setting labels, grid Solving linear recurrence relations...6 lines, etc.) that were actually used to create the corresponding FIR filters in scipy.signal ..........7 figure in the paper. Complete scripts for all the figures in Apply a FIR filter............7 this paper are available in the papers/scipy directory at Specialized functions that are FIR filters8 https://github.com/pydata/pydata-cookbook/. FIR filter frequency response......8 FIR filter design.............8 IIR filters in scipy.signal FIR filter design: the window method.8 An IIR filter can be written as a linear recurrence relation, FIR filter design: least squares.....9 in which the output yn is a linear combination of xn , the M FIR filter design: Parks-McClellan... 10 previous values of x and the N previous values of y: FIR filter design: linear programming. 10 Determining the order of a FIR filter.. 13 M N a0 yn = ∑ bi xn−i − ∑ ai yn−N (1) Kaiser’s window method........ 13 i=0 i=1 Optimizing the FIR filter order..... 13 (See, for example, Oppenheim and Schafer [OS], Chapter 6. References 14 Note, however, that the sign convention for a[1], ..., a[N] in Eqn. (1) and used in SciPy is the opposite of that used in Oppenheim and Schafer.) Introduction DRAFTBy taking the z-transform of Eqn. (1), we can express the The SciPy library, created in 2001, is one of the core pack- filter as ages of the PyData stack. It includes modules for statistics, Y(z) = H(z)X(z) (2) optimization, interpolation, integration, linear algebra, Fourier transforms, signal and image processing, ODE solvers, special where b + b z−1 + ··· + b z−M functions, sparse matrices, and more. H(z) = 0 1 M (3) a + a z−1 + ··· + a z−N The signal subpackage within the SciPy library includes 0 1 N tools for several areas of computation, including signal pro- is the transfer function associated with the filter. The functions cessing, interpolation, linear systems analysis and even some in SciPy that create filters generally set a0 = 1. elementary image processing. In this Cookbook chapter, we Eqn. (1) is also known as an ARMA(N, M) process, where "ARMA" stands for Auto-Regressive Moving Average. ∗ Corresponding author: [email protected] b holds the moving average coefficients, and a holds the auto- Copyright ○c 2016 Warren Weckesser. regressive coefficients. 2 PyData CookBook When a1 = a2 = ··· = aN = 0, the filter is a finite impulse the other filter design functions, we might as well create the response filter. We will discuss those later. filter in the transfer function or SOS format when the filter is created. IIR filter representation SOS. In the second order sections (SOS) representation, the In this section, we discuss three representations of a linear filter is represented using one or more cascaded second order filter: filters (also known as "biquads"). The SOS representation is • transfer function implemented as an array with shape (n, 6), where each row • zeros, poles, gain (ZPK) holds the coefficients of a second order transfer function. The • second order sections (SOS) first three items in a row are the coefficients of the numerator of the biquad’s transfer function, and the second three items SciPy also provides a state space representation, but we are the coefficients of the denominator. won’t discuss that format here. Transfer function. The transfer function representation of The SOS format for an IIR filter is more numerically stable a filter in SciPy is the most direct representation of the than the transfer function format, so it should be preferred data in Eqn. (1) or (3). It is two one-dimensional arrays, when using filters with orders beyond, say, 7 or 8, or when conventionally called b and a, that hold the coefficients of the the bandwidth of the passband of a filter is sufficiently small. polynomials in the numerator and denominator, respectively, (Unfortunately, we don’t have a precise specification for what of the transfer function H(z). "sufficiently small" is.) For example, we can use the function A disadvantage of the SOS format is that the function sosfilt scipy.signal.butter to create a Butterworth lowpass (at least at the time of this writing) applies an SOS filter of order 6 with a normalized cutoff frequency of 1/8 filter by making multiple passes over the data, once for each the Nyquist frequency. The default representation created by second order section. Some tests with an order 8 filter show sosfilt(sos, x) butter is the transfer function, so we can use butter(6, that can require more than twice the lfilter(b, a, x) 0.125): time of . Here we create a Butterworth filter using the SOS represen- >>> from scipy.signal import butter >>> b, a = butter(6, 0.125) tation: >>> b >>> sos = butter(6, 0.125, output="sos") array([ 2.883e-05, 1.730e-04, 4.324e-04, >>> sos 5.765e-04, 4.324e-04, 1.730e-04, array([[ 2.883e-05, 5.765e-05, 2.883e-05, 2.883e-05]) 1.000e+00, -1.349e+00, 4.602e-01], >>> a [ 1.000e+00, 2.000e+00, 1.000e+00, array([ 1. , -4.485, 8.529, -8.779, 5.148, 1.000e+00, -1.454e+00, 5.741e-01], -1.628, 0.217]) [ 1.000e+00, 2.000e+00, 1.000e+00, 1.000e+00, -1.681e+00, 8.198e-01]]) The representation of a filter as a transfer function with coef- ficients (b, a) is convenient and of theoretical importance, The array sos has shape (3, 6). Each row represents a biquad; but with finite precision floating point, applying an IIR filter for example, the transfer function of the biquad stored in the of even moderately large order using this format is susceptible last row is to instability from numerical errors. Problems can arise when 1 + 2z−1 + z−2 designing a filter of high order, or a filter with very narrow H(z) = −1 −2 pass or stop bands. 1 − 1:681z + 0:8198z ZPK. The ZPK representation consists of a tuple containing Converting between representations. The signal module three items, (z, p, k). The first two items, z and p, provides a collection of functions for converting one represen- are one-dimensional arrays containing the zeros and poles, tation to another: respectively, of the transfer function. The third item, k, is a sos2tf, sos2zpk, ss2tf, ss2zpk, scalar that holds the overall gain of the filter. tf2sos, tf2zz, tf2zpk, zpk2sos, zpk2ss, zpk2tf We can tell butter to create a filter using the ZPK zpk2sos representation by using the argument output="zpk": For example, converts from the ZPK representation to the SOS representation. In the following, z, p and k have >>> z, p, k = butter(6, 0.125,DRAFT output=’zpk’) >>> z the values defined earlier: array([-1., -1., -1., -1., -1., -1.]) >>> from scipy.signal import zpk2sos >>> p >>> zpk2sos(z, p, k) array([ 0.841+0.336j, 0.727+0.213j, array([[ 2.883e-05, 5.765e-05, 2.883e-05, 0.675+0.072j, 0.675-0.072j, 1.000e+00, -1.349e+00, 4.602e-01], 0.727-0.213j, 0.841-0.336j]) [ 1.000e+00, 2.000e+00, 1.000e+00, >>> k 1.000e+00, -1.454e+00, 5.741e-01], 2.8825891944002783e-05 [ 1.000e+00, 2.000e+00, 1.000e+00, 1.000e+00, -1.681e+00, 8.198e-01]]) A limitation of the ZPK representation of a filter is that SciPy does not provide functions that can directly apply the filter to Limitations of the transfer function representation. Earlier a signal. The ZPK representation must be converted to either we said that the transfer function representation of moderate to the SOS format or the transfer function format to actually filter large order IIR filters can result in numerical problems. Here a signal. If we are designing a filter using butter or one of we show an example. SIGNAL PROCESSING WITH SCIPY: LINEAR FILTERS 3 0 1.00 2 0.75 0.50 4 Gain 0.25 6 0 20 40 60 80 100 120 0.00 Sample number 0.0 0.2 0.4 0.6 0.8 1.0 Fig.