2
$\begingroup$

I am trying to port some code from MATLAB to python. The goal is to use Butterworth filter (4th order, bandpass) API and convert it to second-order sections. I get the same output between MATLAB and python for the Butterworth filter coefficients but not for zp2sos/zpk2sos.

Using MATLAB R2019b, python 3.7, scipy 1.6.3.

Below is MATLAB code with output:

fs = 8000;
f1 = 200;
f2 = 400;

[z,p,k] = butter(4, [f1/(fs/2) f2/(fs/2)]);
[sos,gain] = zp2sos(z,p,k);

MATLAB Output

z = 1 1 1 1 -1 -1 -1 -1

p = 0.9173+0.2885i  0.9173-0.2885i 0.8934+0.2214i 0.8934-0.2214i
    0.9237+0.1704i  0.9237-0.1704i 0.9671+0.1555i 0.9671-0.1555i

k = 3.1239e-05

sos = 1.0000    2.0000    1.0000    1.0000   -1.7867    0.8471
      1.0000    2.0000    1.0000    1.0000   -1.8475    0.8823
      1.0000   -2.0000    1.0000    1.0000   -1.8345    0.9246
      1.0000   -2.0000    1.0000    1.0000   -1.9341    0.9594

My python code looks like this:

from scipy import signal

fs = 8000
f1 = 200
f2 = 400

z, p, k = signal.butter(4, [f1/(fs/2), f2/(fs/2)], 'bandpass', output='zpk')
sos = signal.zpk2sos(z, p, k)

Python Output:

z= [ 1.+0.j  1.+0.j  1.+0.j  1.+0.j -1.+0.j -1.+0.j -1.+0.j -1.+0.j]

p = [0.967057-0.15545929j   0.92374003-0.17042575j  0.92374003+0.17042575j 0.967057+0.15545929j 
     0.91727345+0.28846481j 0.89335425+0.22144318j  0.89335425-0.22144318j 0.91727345-0.28846481j]

Output ordering for 'p' is different than MATLAB but all values account for. Not sure if it matters?

k = 3.123897691708261e-05

sos=
[[ 3.12389769e-05  6.24779538e-05  3.12389769e-05  1.00000000e+00 -1.78670849e+00  8.47118894e-01]
 [ 1.00000000e+00  2.00000000e+00  1.00000000e+00  1.00000000e+00 -1.84748005e+00  8.82340575e-01]
 [ 1.00000000e+00 -2.00000000e+00  1.00000000e+00  1.00000000e+00 -1.83454690e+00  9.24602526e-01]
 [ 1.00000000e+00 -2.00000000e+00  1.00000000e+00  1.00000000e+00 -1.93411400e+00  9.59366836e-01]]

I am not sure why the first 3 coefficients in python are always different than that of MATLAB. I have tried few different inputs. Unfortunately, I do not have experience with DSP or with scipy to understand what this means or how to debug this. I cannot change the MATLAB code but need something equivalent in python. Any help is appreciated.

$\endgroup$
1
  • $\begingroup$ In python the scaling constant k is just incorporated into the first second-order section, which is just k*[1,2,1]. $\endgroup$
    – Matt L.
    May 22 at 12:29
2
$\begingroup$

I am not sure why the first 3 coefficients in python are always different than that of MATLAB.

It's just a scaling. If you call Matlab's zp2sos without the second output argument, you will get the same three values as Python. If you need Python to match Matlab, use the first value of the sos as your "k" and divide the first three values in the sos by k (in your example this yields 1 2 1).

Output ordering for 'p' is different than MATLAB but all values account for. Not sure if it matters?

Depending on your application it actually does matter. For rather complicated reasons, Python's is better and will in most cases generate less numerical noise and often significantly so.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.