The job of an IIR filter design program is to transform a filter specification into a corresponding set of IIR filter coefficients. After that, the user is responsible for actually implementing the filter using the coefficients provided.
Implementing an IIR filter may seem difficult, but it really is not complicated. However, in order to implement an IIR filter successfully, it is important to understand how the IIR filter coefficients should integrate with the code that actually implements the intended IIR filter.
Choice of Arithmetic
Although it is possible to implement IIR filters with fixed-point arithmetic, the use of floating-point arithmetic is greatly preferred when implementing IIR filters of any significant order. The main reason is that IIR filter coefficients cannot be limited to a fractional range, as can FIR filter coefficients. Also, the values calculated internally by IIR filters often span a dynamic range that is too wide for common fractional fixed-point formats.
The coefficients of an IIR filter design must be represented accurately in order for the filter to have the intended response. As a rule of thumb, 32-bit coefficients are sufficient for most designs, though more bits may be required to implement very demanding IIR filter designs.
Putting these two ideas together, most IIR filters can be implemented using 32-bit floating-point arithmetic (such as C’s “float” type), but using 64-bit floating-point arithmetic (such as C’s “double” type) is not a bad idea if that is feasible in your application.
IIR Filter Representation
An IIR filter design can be represented in several different forms, including poles/zeros, direct form, and cascaded biquadratic (“biquad”) form. IIR filter design programs typically can provide coefficients in those forms. Each of those are equivalent in terms of the pure mathematics of the resulting filter transfer function. However, in practice, IIR filters are always implemented on processors that do arithmetic using a finite number of bits. So, in any practical, non-ideal implementation, the choice of IIR filter representation becomes an important consideration in terms of the resulting filter performance.
The biquad form is the implementation topology of choice for all but the simplest IIR filters. If you are not familiar with biquads, see Wikipedia’s Digital biquad filter page for a detailed explanation. Basically, biquads provide a simple, uniform building block that can be used to implement any IIR filter. This simplicity results in efficiency, ease of implementation, and most importantly, in relative insensitivity to coefficient quantization, which is a highly desirable numerical property in the context of non-ideal computer arithmetic.
Since biquads offer so many advantages, they are highly recommended for implementing IIR filters, and we will assume or the rest of this article that you are using biquads, though other methods certainly are possible.
When implemented via biquads, an IIR filter consists of a series of one or more cascaded (chained) biquads, where the input signal is fed into the first biquad, the output of each biquad supplies the input of the next biquad, and the output of the last biquad is the output of the overall filter.
Mathematically, each biquad implements a ratio of two quadratic (second order) functions of Z, where Z-1 represents a unit sample delay. The biquad’s numerator and denominator polynomials each have three coefficients, so a biquad is completely defined by six coefficients, and an overall IIR filter is defined by one or more sets of biquad coefficients.
A biquad is defined as a ratio of second-order polynomials in Z. In order to implement biquads in a uniform way, we need a name for each of the six biquad coefficients. The most commonly used notation appears on Wikipedia’s Digital biquad filter page and also in most DSP textbooks, though other notations are sometimes used. In the common convention, the numerator polynomial coefficients are named b0, b1, and b2, and the denominator polynomial coefficients are named a0, a1, a2.
Wikipedia’s biquad page is very good so we will not repeat that material here. However, we highly recommend you study that material at this point to help you understand what follows.
Conventionally, each biquad is normalized by dividing each coefficient by the a0 value produced by the design process, which results in each a0 used to actually implement the filter becoming equal to 1. Using an a0 value of 1 simplifies and speeds the implementation by eliminating a single multiply per biquad since there is no mathematical benefit to multiplying anything by 1.
Beyond that, it also is possible to further normalize a biquad by dividing each of the b coefficients by b0, then accounting for that by multiplying the input or output of the filter by the product of all the original b0 values.
In practice, biquads generally are implemented using a biquad function similar to this example. The example is practical for use on a general-purpose processor such as a PC. It implements biquads using the Direct Form 2 topology, which uses just two delay registers (memory elements) per biquad, which is the minimum possible number.
It is instructive to compare the C example code to the diagram and equations for Direct Form 2 on Wikipedia. The code is a straight-forward implementation of the equations, with a few details added such the calculation of the intermediate value of w, and the movement of data through the Z-1 delay lines.
Implementing Biquads in Real Time
If your IIR filter must run in real time, such as in an embedded DSP application, it is best to use a highly optimized biquad function rather than the C example referenced above. If you are using a specialized DSP processor, biquad functions usually are provided as part of the processor’s DSP library. These functions are highly optimized for the target processor, and usually are hand-written in assembly code for maximum efficiency.
Unfortunately, the biquad functions found in DSP libraries use various conventions of coefficient layout. These variations are mostly driven by the need to implement a biquad as efficiently as possible on the target processor, with consideration of the details of the processor’s architecture. Biquad coefficient layouts vary in terms of:
- The ordering of the a and b coefficients
- Whether or not a0 should be supplied
- The sign of the a1 and a2 coefficients
Of course, the required syntax of the biquad coefficient code also varies depending on the intended implementation language, though C is the common choice for most real-time applications.
When you use a biquad function from a DSP library, it is very important to study the function’s documentation and then select the biquad output format in your IIR design program that matches the needs of the library function. Any discrepancy in terms of the order or the sign of any coefficient will result in complete failure of the IIR filter implementation. There is no such thing as “close” here: if the filter coefficients are not in the exact format assumed by the biquad function you use, the filter will not have anything like the intended frequency response, and it may even be unstable.
Since any discrepancy in the biquad coefficients results in disaster, it is very important to test your IIR filter implementation with impulse and perhaps even step signal inputs, and compare its outputs to the corresponding impulse and step data provided by your IIR design program, if available. The outputs may not match bit-for-bit due to small differences in arithmetic, but you should see the same overall amplitude and shape in your filter’s output.
If your filter’s output does not match the reference output from your filter design program, you will need to review the documentation of the biquad function you are using and resolve the discrepancy between what it requires and the biquad coefficient output format that you have selected in the filter design program.
With care and persistence, you will end up with a successful IIR filter implementation. Fortunately, once you get all the implementation details right – especially by selecting the correct output format options in your filter design program to match the needs of your biquad function – you can redesign your filter at any time and it generally will “just work”.
Here is a summary of the primary points to successfully implement an IIR filter:
- Use 32-bit floating-point arithmetic or even 64-bit, if possible, to implement IIR filters.
- Implement all but the simplest IIR filters using biquads.
- Study and understand how biquads are used to implement IIR filters.
- Use a function to implement each biquad, such as the example C biquad function or a biquad function supplied with a DSP library.
- Export biquad coefficients from your IIR design program in the format required by the biquad function you use.
- Implement the overall IIR filter by cascading biquads. That is, use the output of each biquad as the input to the next biquad.
- Test your overall IIR filter implementation using impulse and step input signals, and compare the resulting output to the IIR impulse and step signals that are produced by the design program, if available.