2

Came accross a real head scratcher this week. I am implementing an IIR filter in C# so I copied directly from the matlab source to filter their time domain filter function (direct form II transpose):

        // direct form ii transposed
        for (int i = 0; i < data.Length; i++)
        {
            Xi = data[i];
            Yi = b[0] * Xi + Z[0];

            for (int j = 1; j < order; j++)
                Z[j - 1] = b[j] * Xi + Z[j] - a[j] * Yi;

            Z[order - 1] = b[order] * Xi - a[order] * Yi;

            output[i] = Yi;
        }
       return output;

What is odd is that when I test the filter with an impulse, I get slightly different values from those reported by Matlab. I am getting the filter coefficients from Matlab as well. Here is the code:

[b,a] = butter(3, [.0360, .1160], 'bandpass');
x = zeros(50,1);
x(1) = 1.0;
y = filter(b,a,x)

I use the values in b and a as the coeffients in my C# code.

The first few values for y as reported by Matlab:

    >> y(1:13)

    ans =

    0.0016
    0.0084
    0.0216
    0.0368
    0.0487
    0.0537
    0.0501
    0.0382
    0.0194
   -0.0038
   -0.0286
   -0.0519
   -0.0713

Since this was different from my C# port, I directly copied the code from filter to a C file and ran it there using the same coefficients. The output was exactly the same, slightly off version of the impulse response that I got in my C# implementation:

[0] 0.0016  double
[1] 0.0086161600000000012   double
[2] 0.022182403216000009    double
[3] 0.038161063110481647    double
[4] 0.051323531488129848    double
[5] 0.05827273642334313 double
[6] 0.057456579295617483    double
[7] 0.048968543791003127    double
[8] 0.034196988694833064    double
[9] 0.015389667539999874    double
[10]    -0.0048027826346631469  double
[11]    -0.023749640330880527   double
[12]    -0.039187648694732236   double
[13]    -0.04946710058803272    double

I looked carefully at the source to filter and I don't see any evidence of massaging the coefficients prior to calculating the filter output. filter normalizes the feed forward coefficients only in the case that the a[0] does not equal 1 (which in this case it most certainly does). Other than that, I expect to see exactly the same filter output from Matlab's C code as I do from Matlab.

I would really like to know where this discrepency is coming from because I need to be confident that my filter is exactly correct (don't we all...). I have checked and rechecked my filter coefficients. They are identical between C/C# and Matlab.

The full C file I used to get these 'wrong' values follows. I tried both the filter implemented as a fixed number of states (6 in this case) and the general case of N states (commented out here). Both come from the Matlab source code and both produce identical, 'wrong' outputs:

# define order 6
# define len 50

int main(void){

    // filter coeffs    
    float a[7] = { 1.0000, -5.3851, 12.1978, -14.8780, 10.3077, -3.8465, 0.6041 };
    float b[7] = { 0.0016, 0.0, -0.0047, 0.0, 0.0047, 0, -0.0016 };
    float a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4], a5 = a[5], a6 = a[6];

    // input, output, and state arrays
    float X[len];
    float Y[len];
    float Z[order];
    float Xi;
    float Yi;
    float z0, z1, z2, z3,z4, z5; 

    // indeces
    int i,j;

    // initialize arrays
    for(i=0;i<len;i++) {
        X[i] = 0.0;
        Y[i] = 0.0;
    }
    X[0] = 1.0;

    for(i=0;i<order;i++)
        Z[i] = 0.0;

     z0 = Z[0];
     z1 = Z[1];
     z2 = Z[2];
     z3 = Z[3];
     z4 = Z[4];
     z5 = Z[5];
     i = 0;
     while (i < len) {
        Xi = X[i];
        Yi = b[0] * Xi + z0;
        z0 = b[1] * Xi + z1 - a1 * Yi;
        z1 = b[2] * Xi + z2 - a2 * Yi;
        z2 = b[3] * Xi + z3 - a3 * Yi;
        z3 = b[4] * Xi + z4 - a4 * Yi;
        z4 = b[5] * Xi + z5 - a5 * Yi;
        z5 = b[6] * Xi      - a6 * Yi;
        Y[i++] = Yi;
     }


    //// copied from matlab filter source code
    //i=0;
    //while (i < len) {
 //       Xi = X[i];                       // Get signal
 //       Yi = b[0] * Xi + Z[0];           // Filtered value
 //       for (j = 1; j < order; j++) {    // Update conditions
 //          Z[j - 1] = b[j] * Xi + Z[j] - a[j] * Yi;
 //       }
 //       Z[order - 1] = b[order] * Xi - a[order] * Yi;
 //       
 //       Y[i++] = Yi;                      // Write to output
 //    }


}
dmedine
  • 1,430
  • 8
  • 25

1 Answers1

3

If I use all the digits of the filter coefficients, I get the Matlab answer.

Specifically,

  float a[7] = {1,
                -5.3850853610906082025167052052,
                12.1978301571107792256043467205,
                -14.8779557262737220924009307055,
                10.3076512098041828124905805453,
                -3.84649525959781790618308150442,
                0.604109699507274999774608659209};
  float b[7] = {0.00156701035058826889344307797813, 0,
                -0.0047010310517648064634887994373, 0,
                0.0047010310517648064634887994373,  0,
                -0.00156701035058826889344307797813};

(Filter coefficients obtained using Scipy: [b,a] = scipy.signal.butter(3, [.0360, .1160], 'bandpass') since I’m not made of .)

With this in place, your C code can print out:

[0] = 0.0015670103020966053009033203125
[1] = 0.00843848474323749542236328125
[2] = 0.02162680588662624359130859375
[3] = 0.036844909191131591796875
[4] = 0.048709094524383544921875
[5] = 0.05368389189243316650390625
[6] = 0.05014741420745849609375
[7] = 0.0382179915904998779296875
[8] = 0.0194064676761627197265625
[9] = -0.003834001719951629638671875

which matches your Matlab output.

Nothing to do with float/double. In situations like this, when porting implementations from one language to another, please strive to ensure bit-exact inputs and compare outputs using relative error—not copy-pasting and comparing printouts.

(PS. Notice how, thanks to the symmetry of your passband, every other element of b is 0. This can be used to reduce the number of flops needed!)

Ahmed Fasih
  • 6,458
  • 7
  • 54
  • 95