Electronic – Why the low pass digital filter amplifies in the passband

filterMATLAB

I'm trying to implement a 10th-order Butterworth Low Pass digital filter on my MCU.

To find the coefficients I used Matlab's Filter Design & Analisys Tool. So I create two little scripts: in the first of them I used the Filter Object created by the tool and it seems to works perfectly, but in the second one I tryed to implement the general code based to the Biquad Direct-Form-II using only the coefficients generated by the tool and, in this case, the filter works good except for a signal amplification of about 5E+7!

My code in matlab is the following:

input = AmplitudeF8.';

delayline_lp = [0,0,0,0,0,0,0,0,0,0];
coeff_lp = [-1.7582972612745587, 0.89109671836321969, 2, 1, -1.5932765493727838, 0.71361243648860972, 2, 1, -1.4754804435926461, 0.58691950806119031, 2, 1, -1.4002643388507841, 0.50602253348347193, 2, 1, -1.3637162169443289, 0.46671403035270148, 2, 1];

for k=1:length(output1)    
    y = output1(k);

    for j=1:5 %the biquad sections (order 10 = num_sections = 5)
        w = y;
        for i=1:2
            w = w - (coeff_lp(((j - 1) * 4) + i) * delayline_lp(((j - 1) * 2) + i));
        end

        y = w;
        for i=1:2
            y = y + (coeff_lp(((j - 1) * 4) + i + 2) * delayline_lp(((j - 1) * 2) + i));
        end

        delayline_lp(j * 2) = delayline_lp((j * 2) - 1);        
        delayline_lp((j * 2) - 1) = w;
    end

    output2(k) = y;
end

Consider that in the coeff_lp array there are a1,a2,b1,b2,…. since a0=b0=1 for every biquad section.

Why in this case the filtered signal is amplified by a 5E+7 factor? Where I'm wrong?

Best Answer

I found the mistake in my code:

when the Matlab's Filter Design & Analisys Tool generates the cofficients, there are even the Gains for the every biquad section. Before the value goes out from one section, it must be multiplied with this gain.

So the right code is the following:

input = AmplitudeF8.';

delayline_lp = [0,0,0,0,0,0,0,0,0,0];
scale_values = [0.033199864272165251, 0.030083971778956442, 0.027859766117136031, 0.026439548658171989, 0.025749453352093145];
coeff_lp = [-1.7582972612745587, 0.89109671836321969, 2, 1, -1.5932765493727838, 0.71361243648860972, 2, 1, -1.4754804435926461, 0.58691950806119031, 2, 1, -1.4002643388507841, 0.50602253348347193, 2, 1, -1.3637162169443289, 0.46671403035270148, 2, 1];

for k=1:length(input)    
    y = input(k);

    for j=1:5 %the biquad sections
        w = y;
        for i=1:2
            w = w - (coeff_lp(((j - 1) * 4) + i) * delayline_lp(((j - 1) * 2) + i));
        end

        y = w;
        for i=1:2
            y = y + (coeff_lp(((j - 1) * 4) + i + 2) * delayline_lp(((j - 1) * 2) + i));
        end

        y = y * scale_values(j);

        delayline_lp(j * 2) = delayline_lp((j * 2) - 1);        
        delayline_lp((j * 2) - 1) = w;
    end

    output2(k) = y;
end

Then the C-Code for my MCU is (maybe is usefull for someone):

double Filter_IIR_Biquad_Sec( double signal, double *coeffs, double *scaleValues, double *delayLine, uint32_t nSections )
{
    uint32_t i, j;
    double y = signal;
    double w = 0.0;

    for ( j = 0; j < nSections; j++ )
    {
        // The denominator
        w = y;
        for ( i = 0; i < 2; i++ )
            w -= coeffs[ ( j << 2 ) + i ] * delayLine[ ( j << 1 ) + i ];

        // The numerator
        y = w;
        for ( i = 0; i < 2; i++ )
            y += coeffs[ ( j << 2 ) + i + 2 ] * delayLine[ ( j << 1 ) + i ];

        y *= scaleValues[ j ];

        // Shift the register values
        delayLine[ ( j << 1 ) + 1 ] = delayLine[ j << 1 ];
        delayLine[ j << 1 ] = w;
    }

    return y;
}

and now it works very well.