Applying a Filter in Both Directions Makes it Zero Phase

I’d like to add another trick to your digital filter toolbox: when you apply any filter in both directions on your input signal, the combined filtering operation is zero phase.

As you know from Why use Symmetrical FIR Filters with an Odd Length?, FIR filters have several advantages. However, execution speed is typically not one of them. When the day comes that you need something that’s faster, you might need to switch to an IIR filter, for example, a single-pole IIR filter. However, as briefly explained in The Phase Response of a Filter, (causal) IIR filters are not linear phase. One consequence of this is that they don’t handle rising and falling edges in the input signal in the same way, as illustrated in Figure 1.

Figure 1. Block pulse filtered with single-pole IIR filter.Figure 1. Block pulse filtered with single-pole IIR filter.

The single-pole IIR filter is fast. Its update expression per sample is typically something like y += b * (x - y), where x is the input sample, y the output sample, and b a parameter of the filter. The value of b was 0.25 in the example of Figure 1, corresponding with a decay value of 0.75.

Apply in Both Directions

A way to work around the nonlinear phase is to apply the filter twice, both in the forward and in the reverse direction. The exact procedure to follow is the following.

  1. Filter the input signal \(x[n]\). This results in an intermediate signal \(x_i[n]\).
  2. Reverse the order of the samples in \(x_i[n]\).
  3. Filter \(x_i[n]\) with the same filter. This results in the output signal \(y[n]\).
  4. Reverse the order of the samples in \(y[n]\) to get the final output signal.

Figure 2 shows the result of following this procedure. Of course, a downside of this approach is that this filter is no longer causal. This is unavoidable, since a causal filter can never be zero phase. The non-causality is obvious in Figure 2, since the filtered signal starts to rise before the pulse arrives. This is not a problem for your further processing of the signal, but it precludes using this filter for real-time applications, at least not without introducing a delay. Which is, in turn, also no problem, since that makes the filter linear phase, which is almost always just as good as zero phase in practice.

Figure 2. Block pulse filtered in both directions.Figure 2. Block pulse filtered in both directions.

Python Code

In Python, assuming an input signal x of length n and using the LowPassSinglePole class from Low-Pass Single-Pole IIR Filter, this can be implemented as follows.

# Input assumed to be in an array x of length n.
 
# Create the filter.
low_pass_single_pole = LowPassSinglePole(decay=0.75)
 
# Filter the signal.
xi = np.zeros(n)
for i in range(n):
    xi[i] = low_pass_single_pole.filter(x[i])
 
# Reverse the order of the samples.
xi = xi[::-1]
 
# Filter the signal.
y = np.zeros(n)
low_pass_single_pole.reset()
for i in range(n):
    y[i] = low_pass_single_pole.filter(xi[i])
 
# Reverse the order of the samples.
y = y[::-1]

Note that Python is definitely not the language in which this kind of implementation is efficient. The explicit for loop over the samples of the signal is a dead giveaway in this respect. If you find yourself looping over samples in Python instead of using array operations, you know that your code is probably going to be slow. So, this code is just for demonstration purposes. In C or C++, on the other hand, an implementation like this is perfectly fine.

hi, your web site is great, lots of useful info! i think some of the theory sites don't have enough practical examples. I put a link to this article in my medium article on the same topic in the further reading section! my example is in excel, yours is in python, good combo.

https://medium.com/@nttp/two-pass-filtering-to-avoid-shifting-data-in-t…

i found your FIR filter design site the other day, super cool. going to see if i can try it for some examples. is there a reason you stopped it at N number of lags? I think the smallest i could get the high pass filter to go was 7 lags.

Add new comment

The content of this field is kept private and will not be shown publicly.
Spam avoidance measure, sorry for this.

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.
Submitted on 23 April 2018