I'm trying to implement an 8th order IIR filter and every application note and textbook I've read says that it is best to implement any filter of order more than 2 as second order sections. I used tf2sos
in MATLAB to get the coefficients for second order sections which gave me a 6x4 coeffs for 4 second order sections, as expected.
Prior to implementation as SOS, the 8th order filter required 7 previous sample values to be stored (and output values as well). Now when implementing as second order sections how does the flow work from input to output, do I need to store only 2 previous sample values? Or does the output of the first filter feed as x_in
in to the second filter and so on?
-
$\begingroup$ u need to store previous states for every stage,depending on the order of the filter in that stage so it would not be just 2 as u mentioned $\endgroup$– user1460Jun 1, 2012 at 9:57
3 Answers
It's the last thing you said ("Or does the output of the first filter feed as x_in in to the second filter and so on?"). The idea is simple: you treat the biquads as separate second-order filters that are in cascade. The output from the first filter is the input to the second, and so on, so the delay lines are spread out among the filters. If you need to optimize the structure in a memory-constrained environment, you can note that adjacent biquads have redundant delay memory (i.e. the last few output samples of stage 1 are the same as the last few input samples of stage 2, so you don't have to store them separately like you would if you just implement the filters in isolation).
-
$\begingroup$ Thanks! I just managed to quickly do it in MATLAB. The cause of earlier confusion was that I forgot to multiply the gain (ugh!) and hence all sorts of ideas started creeping in. $\endgroup$ Sep 12, 2011 at 14:20
-
$\begingroup$ If you don't bother asking for the gain as an output arg from tf2sos (like in my example code posted) then you don't need to bother multiplying it back in again. $\endgroup$– learnvstFeb 24, 2012 at 17:55
There are actually two ways to implement second order sections: parallel and serial. In the serial version, the outputs of section N are the inputs to section N+1. In the parallel version all sections have the same input (and only one real zero instead of a conjugate complex pair of zeros) and each sections output is simply summed up. The two methods are related through partial-fraction expansion of the Z-domain transfer function. WARNING: this is a numerically tricky problem and the standard Matlab implementation "residuez" can have very large numerical errors for typical audio filters that have poles close to the unit circle.
Here is a little bit of demo code to show why you are better off cascading 2nd order sections.
clc
sr = 44100;
order = 13;
[b,a] = butter(order,1000/(sr/2),'low');
[sos] = tf2sos(b,a);
x = [1; zeros(299,1)]; %impulse
% all in one
Y = filter(b,a,x);
% cascaded biquads
Z = x;
for nn = 1:size(sos,1);
Z = filter(sos(nn,1:3),sos(nn,4:6), Z );
end
cla; plot(Y, 'k'); hold on; plot(Z,':r'); hold off
For the lowpass filter given in the above example, by orders of about about 12 to 13, the numerical errors build up to give a visibly different impulse response for the implementation that does not use cascaded biquads. Depending on the filter, your mileage will vary.
ORDER = 10
ORDER = 13
-
1$\begingroup$ @learvst Correct me if i am wrong, but your code misses the gains. Shouldn't it be:
[sos gain] = tf2sos(b,a); // Rest of code for nn = 1:size(sos,1); Z = filter(sos(nn,1:3),sos(nn,4:6), Z ); end Z = filter(gain,1,Z);
$\endgroup$ Mar 30, 2015 at 23:44