Main Content

# filtfilt

Zero-phase digital filtering

## Syntax

``y = filtfilt(b,a,x)``
``y = filtfilt(sos,g,x)``
``y = filtfilt(d,x)``
``y = filtfilt(B,A,x,"ctf")``
``y = filtfilt({B,A,g},x)``

## Description

````y = filtfilt(b,a,x)` performs zero-phase digital filtering by processing the input data `x` in both the forward and reverse directions. After filtering the data in the forward direction, the function matches initial conditions to minimize startup and ending transients, reverses the filtered sequence, and runs the reversed sequence back through the filter. The result has these characteristics: Zero phase distortionA filter transfer function equal to the squared magnitude of the original filter transfer functionA filter order that is double the order of the filter specified by `b` and `a` `filtfilt` implements the algorithm proposed by Gustafsson [1].Do not use `filtfilt` with differentiator and Hilbert FIR filters, because the operation of those filters depends heavily on their phase response.```

example

````y = filtfilt(sos,g,x)` zero-phase filters the input data `x` using the second-order section (biquad) filter represented by the matrix `sos` and the scale values `g`.```
````y = filtfilt(d,x)` zero-phase filters the input data `x` using a digital filter `d`. Use `designfilt` to generate `d` based on frequency-response specifications.```

example

````y = filtfilt(B,A,x,"ctf")` zero-phase filters the input data `x` using Cascaded Transfer Functions (CTF) defined by the numerator and denominator coefficients `B` and `A`, respectively. (since R2024b) NoteSpecify the `"ctf"` option to disambiguate CTF numerator matrices `B` with six columns from second-order section matrix inputs, `sos`, when you specify `A` as a scalar or vector. ```

example

````y = filtfilt({B,A,g},x)` includes the filter scalar values, `g`, in the filtering of the input data, `x`. (since R2024b)```

example

## Examples

collapse all

Zero-phase filtering helps preserve features in a filtered time waveform exactly where they occur in the unfiltered signal.

Zero-phase filter a synthetic electrocardiogram (ECG) waveform. The function that generates the waveform is at the end of the example. The QRS complex is an important feature in the ECG. Here it begins around time point 160.

```wform = ecg(500); plot(wform) axis([0 500 -1.25 1.25]) text(155,-0.4,"Q") text(180,1.1,"R") text(205,-1,"S")```

Corrupt the ECG with additive noise. Reset the random number generator for reproducible results. Construct a lowpass FIR equiripple filter and filter the noisy waveform using both zero-phase and conventional filtering.

```rng("default") x = wform' + 0.25*randn(500,1); d = designfilt("lowpassfir", ... PassbandFrequency=0.15,StopbandFrequency=0.2, ... PassbandRipple=1,StopbandAttenuation=60, ... DesignMethod="equiripple"); y = filtfilt(d,x); y1 = filter(d,x); tiledlayout("flow") nexttile plot([y y1]) title("Filtered Waveforms") legend(["Zero-phase Filtering" "Conventional Filtering"]) nexttile plot(wform) title("Original Waveform")```

Zero-phase filtering reduces noise in the signal and preserves the QRS complex at the same time it occurs in the original. Conventional filtering reduces noise in the signal, but delays the QRS complex.

Repeat the filtering using a Butterworth second-order section filter.

```d1 = designfilt("lowpassiir",FilterOrder=12, ... HalfPowerFrequency=0.15,DesignMethod="butter"); y = filtfilt(d1,x); figure plot(x) hold on plot(y,LineWidth=3) hold off legend(["Noisy ECG" "Zero-Phase Filtering"])```

This function generates the ECG waveform.

```function x = ecg(L) %ECG Electrocardiogram (ECG) signal generator. % ECG(L) generates a piecewise linear ECG signal of length L. % % EXAMPLE: % x = ecg(500).'; % y = sgolayfilt(x,0,3); % Typical values are: d=0 and F=3,5,9, etc. % y5 = sgolayfilt(x,0,5); % y15 = sgolayfilt(x,0,15); % plot(1:length(x),[x y y5 y15]); % Copyright 1988-2002 The MathWorks, Inc. a0 = [0,1,40,1,0,-34,118,-99,0,2,21,2,0,0,0]; % Template d0 = [0,27,59,91,131,141,163,185,195,275,307,339,357,390,440]; a = a0/max(a0); d = round(d0*L/d0(15)); % Scale them to fit in length L d(15)=L; for i=1:14 m = d(i):d(i+1)-1; slope = (a(i+1)-a(i))/(d(i+1)-d(i)); x(m+1) = a(i)+slope*(m-d(i)); end end```

Since R2024b

Use cascaded transfer functions to perform zero-phase filtering.

Design an elliptic filter with passband ripple and stopband attenuation of 0.1 dB and 40 dB, respectively. Specify a sample rate of 2000 Hz. Plot the frequency response of the filter.

```Fs = 2000; [B,A] = ellip(20,0.1,40,[0.3 0.7],"ctf"); freqz(B,A,2048,Fs,"ctf")```

Filter a linearly swept chirp signal, where the Nyquist frequency occurs at one second. Compare the spectra between the input and output signals.

```t = 0:1/Fs:1; x = chirp(t,0,t(end),Fs/2)'; y = filtfilt(B,A,x,"ctf"); pspectrum([x y],Fs,Leakage=1,FrequencyResolution=1)```

Since R2024b

Recreate noisy sinusoid artifacts with transfer-function zero-phase filtering. Filter an oscillating signal using CTF and scale values.

Create a signal `u` comprised of normally distributed noise and three sinusoidal waveforms. The sample rate is 1 kHz.

```rng("default") Fs = 1e3; ts = (0:1/Fs:2)'; a0 = [3 2 1]; f0 = [0.1 0.5 0.9]*Fs/2; p0 = [0 pi/4 pi/2]; u = 0.1*randn(size(ts)) + 0.1*sin(2*pi*f0.*ts+p0)*a0';```

Recreate noisy sinusoid artifacts by filtering `n0` with a third-order Butterworth bandstop digital filter and create a signal `v`.

```[b,a] = butter(3,[0.15 0.85],"stop"); v = filtfilt(b,a,u);```

Compare `u` and `v`. Observe that both signals are in phase.

```tiledlayout("flow") nexttile strips([u(ts<0.1) v(ts<0.1)],0.1,Fs) legend(["u" "v"],Location="southeast") xlabel("Time (seconds)") nexttile pspectrum([u v],Fs) legend(["u" "v"],Location="southeast")```

Create a voltage-controlled oscillating signal `x`. Add noisy sinusoid artifacts represented by the signal `v`.

```vo = exp(-2*abs(ts-1)).*sin(8*pi*ts); x = vco(vo,[0.25 0.75]*Fs/2,Fs) + v;```

Filter the signal `x` with a 24th-order Chebyshev type II filter. Use the CTF format and scale values (`B,A,g`).

```[B,A,g] = cheby2(24,50,[0.2 0.8],"ctf"); y = filtfilt({B,A,g},x);```

Compare the magnitude squared of the short-time Fourier transforms. Observe a sharp decrease in the magnitude at the stopbands.

```tiledlayout("flow") nexttile stft(x,Fs,Window=bohmanwin(128),OverlapLength=120, ... FFTLength=512,FrequencyRange="onesided") title("Input x") nexttile stft(y,Fs,Window=bohmanwin(128),OverlapLength=120, ... FFTLength=512,FrequencyRange="onesided") title("Output y")```

## Input Arguments

collapse all

Transfer function coefficients, specified as vectors. If you use an all-pole filter, set `b` to `1`. If you use an all-zero (FIR) filter, set `a` to `1`.

Example: `b = [1 3 3 1]/6` and ```a = [3 0 1 0]/3``` specify a third-order Butterworth filter with a normalized 3-dB frequency of 0.5π rad/sample.

Data Types: `double` | `single`

Input signal, specified as a real-valued or complex-valued vector, matrix, or N-D array. `x` must be finite-valued. The length of `x` must be greater than three times the filter order, defined as max(length(B)-1, length(A)-1). The function operates along the first array dimension of `x` unless `x` is a row vector. If `x` is a row vector, then the function operates along the second dimension.

Example: `cos(pi/4*(0:159))+randn(1,160)` is a single-channel row-vector signal.

Example: `cos(pi./[4;2]*(0:159))'+randn(160,2)` is a two-channel signal.

Data Types: `double` | `single`
Complex Number Support: Yes

Second-order section coefficients, specified as a matrix. `sos` is a L-by-6 matrix where the number of sections L must be greater than or equal to 2. If the number of sections is less than 2, then the function treats the input as a numerator vector. Each row of `sos` corresponds to the coefficients of a second-order (biquad) filter. The ith row of `sos` corresponds to `[bi(1) bi(2) bi(3) ai(1) ai(2) ai(3)]`.

Example: `s = [2 4 2 6 0 2;3 3 0 6 0 0]` specifies a third-order Butterworth filter with a normalized 3-dB frequency of 0.5π rad/sample.

Data Types: `double` | `single`

Since R2024b

Scale values, specified as a real-valued scalar or as a real-valued vector with L + 1 elements, where L is the number of CTF sections. The scale values represent the distribution of the filter gain across sections of the cascaded filter representation.

The `filtfilt` function applies a gain to the filter sections using the `scaleFilterSections` function depending on how you specify `g`:

• Scalar — The function distributes the gain uniformly across all filter sections.

• Vector — The function applies the first L gain values to the corresponding filter sections and distributes the last gain value uniformly across all filter sections.

Data Types: `double` | `single`

Digital filter, specified as a `digitalFilter` object. Use `designfilt` to generate a digital filter based on frequency-response specifications.

Example: ```d = designfilt("lowpassiir",FilterOrder=3,HalfPowerFrequency=0.5)``` specifies a third-order Butterworth filter with a normalized 3-dB frequency of 0.5π rad/sample.

Since R2024b

Cascaded transfer function (CTF) coefficients, specified as scalars, vectors, or matrices. `B` and `A` list the numerator and denominator coefficients of the cascaded transfer function, respectively.

`B` must be of size L-by-(m + 1) and `A` must be of size L-by-(n + 1), where:

• L represents the number of filter sections.

• m represents the order of the filter numerators.

• n represents the order of the filter denominators.

For more information about the cascaded transfer function format and coefficient matrices, see Specify Digital Filters in CTF Format.

Note

If any element of `A(:,1)` is not equal to `1`, then `filtfilt` normalizes the filter coefficients by `A(:,1)`. In this case, `A(:,1)` must be nonzero.

Data Types: `double` | `single`
Complex Number Support: Yes

## Output Arguments

collapse all

Filtered signal, returned as a vector, matrix, or N-D array.

• The `filtfilt` function returns `y` with the same size as `x`.

• If you specify any input argument as type `single`, then `filtfilt` computes the filtering operation using single-precision arithmetic, and returns `y` as type `single`. Otherwise, the `filtfilt` returns `y` as type `double`.

## More About

collapse all

### Cascaded Transfer Functions

Partitioning an IIR digital filter into cascaded sections improves its numerical stability and reduces its susceptibility to coefficient quantization errors. The cascaded form of a transfer function H(z) in terms of the L transfer functions H1(z), H2(z), …, HL(z) is

`$H\left(z\right)=\prod _{l=1}^{L}{H}_{l}\left(z\right)={H}_{1}\left(z\right)×{H}_{2}\left(z\right)×\cdots ×{H}_{L}\left(z\right).$`

### Specify Digital Filters in CTF Format

You can specify digital filters in the CTF format for analysis, visualization, and signal filtering. Specify a filter by listing its coefficients `B` and `A`. You can also include the filter scaling gain across sections by specifying a scalar or vector `g`.

Filter Coefficients

When you specify the coefficients as L-row matrices,

`$B=\left[\begin{array}{cccc}{b}_{11}& {b}_{12}& \cdots & {b}_{1,m+1}\\ {b}_{21}& {b}_{22}& \cdots & {b}_{2,m+1}\\ ⋮& ⋮& \ddots & ⋮\\ {b}_{L1}& {b}_{L2}& \cdots & {b}_{L,m+1}\end{array}\right],\text{ }A=\left[\begin{array}{cccc}{a}_{11}& {a}_{12}& \cdots & {a}_{1,n+1}\\ {a}_{21}& {a}_{22}& \cdots & {a}_{2,n+1}\\ ⋮& ⋮& \ddots & ⋮\\ {a}_{L1}& {a}_{L2}& \cdots & {a}_{L,n+1}\end{array}\right],$`

it is assumed that you have specified the filter as a sequence of L cascaded transfer functions, such that the full transfer function of the filter is

`$H\left(z\right)=\frac{{b}_{11}+{b}_{12}{z}^{-1}+\cdots +{b}_{1,m+1}{z}^{-m}}{{a}_{11}+{a}_{12}{z}^{-1}+\cdots +{a}_{1,n+1}{z}^{-n}}×\frac{{b}_{21}+{b}_{22}{z}^{-1}+\cdots +{b}_{2,m+1}{z}^{-m}}{{a}_{21}+{a}_{22}{z}^{-1}+\cdots +{a}_{2,n+1}{z}^{-n}}×\cdots ×\frac{{b}_{L1}+{b}_{L2}{z}^{-1}+\cdots +{b}_{L,m+1}{z}^{-m}}{{a}_{L1}+{a}_{L2}{z}^{-1}+\cdots +{a}_{L,n+1}{z}^{-n}},$`

where m ≥ 0 is the numerator order of the filter and n ≥ 0 is the denominator order.

• If you specify both B and A as vectors, it is assumed that the underlying system is a one-section IIR filter (L = 1), with B representing the numerator of the transfer function and A representing its denominator.

• If B is scalar, it is assumed that the filter is a cascade of all-pole IIR filters with each section having an overall system gain equal to B.

• If A is scalar, it is assumed that the filter is a cascade of FIR filters with each section having an overall system gain equal to 1/A.

Note

• To convert second-order section matrices to cascaded transfer functions, use the `sos2ctf` function.

• To convert a zero-pole-gain filter representation to cascaded transfer functions, use the `zp2ctf` function.

Coefficients and Gain

If you have an overall scaling gain or multiple scaling gains factored out from the coefficient values, you can specify the coefficients and gain as a cell array of the form `{B,A,g}`. Scaling filter sections is especially important when you work with fixed-point arithmetic to ensure that the output of each filter section has similar amplitude levels, which helps avoid inaccuracies in the filter response due to limited numeric precision.

The gain can be a scalar overall gain or a vector of section gains.

• If the gain is scalar, the value applies uniformly to all the cascade filter sections.

• If the gain is a vector, it must have one more element than the number of filter sections L in the cascade. Each of the first L scale values applies to the corresponding filter section, and the last value applies uniformly to all the cascade filter sections.

If you specify the coefficient matrices and gain vector as

`$B=\left[\begin{array}{cccc}{b}_{11}& {b}_{12}& \cdots & {b}_{1,m+1}\\ {b}_{21}& {b}_{22}& \cdots & {b}_{2,m+1}\\ ⋮& ⋮& \ddots & ⋮\\ {b}_{L1}& {b}_{L2}& \cdots & {b}_{L,m+1}\end{array}\right],\text{ }A=\left[\begin{array}{cccc}{a}_{11}& {a}_{12}& \cdots & {a}_{1,n+1}\\ {a}_{21}& {a}_{22}& \cdots & {a}_{2,n+1}\\ ⋮& ⋮& \ddots & ⋮\\ {a}_{L1}& {a}_{L2}& \cdots & {a}_{L,n+1}\end{array}\right],\text{ }g=\left[\begin{array}{ccccc}{g}_{1}& {g}_{2}& \cdots & {g}_{L}& {g}_{\text{S}}\end{array}\right],$`

it is assumed that the transfer function of the filter system is

`$H\left(z\right)={g}_{\text{S}}\left({g}_{1}\frac{{b}_{11}+{b}_{12}{z}^{-1}+\cdots +{b}_{1,m+1}{z}^{-m}}{{a}_{11}+{a}_{12}{z}^{-1}+\cdots +{a}_{1,n+1}{z}^{-n}}×{g}_{2}\frac{{b}_{21}+{b}_{22}{z}^{-1}+\cdots +{b}_{2,m+1}{z}^{-m}}{{a}_{21}+{a}_{22}{z}^{-1}+\cdots +{a}_{2,n+1}{z}^{-n}}×\cdots ×{g}_{L}\frac{{b}_{L1}+{b}_{L2}{z}^{-1}+\cdots +{b}_{L,m+1}{z}^{-m}}{{a}_{L1}+{a}_{L2}{z}^{-1}+\cdots +{a}_{L,n+1}{z}^{-n}}\right).$`

## References

[1] Gustafsson, F. “Determining the initial states in forward-backward filtering.” IEEE® Transactions on Signal Processing. Vol. 44, April 1996, pp. 988–992. https://doi.org/10.1109/78.492552.

[2] Lyons, Richard G. Understanding Digital Signal Processing. Upper Saddle River, NJ: Prentice Hall, 2004.

[3] Mitra, Sanjit K. Digital Signal Processing. 2nd Ed. New York: McGraw-Hill, 2001.

[4] Oppenheim, Alan V., and Ronald W. Schafer, with John R. Buck. Discrete-Time Signal Processing. 2nd Ed. Upper Saddle River, NJ: Prentice Hall, 1999.

## Version History

Introduced before R2006a

expand all