Search code examples
c#matlabfiltersignal-processingtransfer-function

Meaning of rational transfer function underlying MATLAB filter or Scipy.signal filter


I have some MATLAB code that filters an input signal using filter:

CUTOFF = 0.05;
FS = 5000;
[b, a] = butter(1, CUTOFF / (FS / 2), 'high');
% b = [0.99996859, -0.99996859]
% a = [1.0, -0.99993717]
dataAfter = filter(b, a, dataBefore);

I'm trying to convert this code to C#. I have already got the butter function to work pretty fast, but now I'm stuck converting the filter function.

I have read the MATLAB filter documentation and Python Scipy.signal filter documentation, but there is a term present in the transfer function definition that I don't understand.

Here is the "rational transfer function" definition from the linked documentation:

        b[0] + b[1]z^(-1)  + ... + b[M]z^(-M)
Y(z) = _______________________________________ X(z)
        a[0] + a[1]z^(-1)  + ... + a[N]z^(-N)

Correct me if i'm wrong, but z is the current element of input data, and Y(z) is the output?

If the above this is true, what is X(z) in this equation?

I want to understand this to implement it in C#, if there is an equivalent option then please enlighten me.


Solution

  • In the More About section of the matlab docs as you pointed out, they describe:

    The input-output description of the filter operation on a vector in the Z-transform domain is a rational transfer function. A rational transfer function is of the form,

            b[0] + b[1]z^(-1)  + ... + b[M]z^(-M)
    Y(z) = _______________________________________ X(z)
            a[0] + a[1]z^(-1)  + ... + a[N]z^(-N)
    

    Rearranging:

           Y(z)   b[0] + b[1]z^(-1)  + ... + b[M]z^(-M)
    H(z) = ____ = _______________________________________
           X(z)   a[0] + a[1]z^(-1)  + ... + a[N]z^(-N)
    

    Thus, X(z) is the z-domain transform of the input vector x (seeDigital Filter). It is important to mention that, also in the docs they give an alternate representation of the transfer function as a difference equation

    Which lends itself better to be ported into code. One possible implementation in C#, could be (using this answer as reference)

    public static double[] Filter(double[] b, double[] a, double[] x)
    {
       // normalize if a[0] != 1.0. TODO: check if a[0] == 0
       if(a[0] != 1.0)
       {
           a = a.Select(el => el / a[0]).ToArray();
           b = b.Select(el => el / a[0]).ToArray();
       }
    
       double[] result = new double[x.Length];
       result[0] = b[0] * x[0];
       for (int i = 1; i < x.Length; i++)
       {
           result[i] = 0.0;
           int j = 0;
           if ((i < b.Length) && (j < x.Length))
           {
               result[i] += (b[i] * x[j]);
           }
           while(++j <= i)
           {
                int k = i - j;
                if ((k < b.Length) && (j < x.Length))
                {
                    result[i] += b[k] * x[j];
                }
                if ((k < x.Length) && (j < a.Length))
                {
                    result[i] -= a[j] * result[k];
                }
            }
        }
        return result;
    }
    

    Driver:

    static void Main(string[] args)
    {
        double[] dataBefore = { 1, 2, 3, 4 };
        double[] b = { 0.99996859, -0.99996859 };
        double[] a = { 1.0, -0.99993717 };
    
        var dataAfter = Filter(b1, a, dataBefore);
    }
    

    Output

    Matlab dataAfter = [0.99996859 1.999874351973491  2.999717289867956  3.999497407630634]
    CSharp dataAfter = [0.99996859 1.9998743519734905 2.9997172898679563 3.999497407630634]
    

    UPDATE
    If the coefficient vectors a and b have a fixed length of 2 the filtering function can be simplified to:

    public static double[] Filter(double[] b, double[] a, double[] x)
    {
        // normalize if a[0] != 1.0. TODO: check if a[0] == 0
        if (a[0] != 1.0)
        {
            a = a.Select(el => el / a[0]).ToArray();
            b = b.Select(el => el / a[0]).ToArray();
        }
    
        int length = x.Length;
        double z = 0.0;
        double[] y = new double[length];    // output filtered signal
    
        double b0 = b[0];
        double b1 = b[1];
        double a1 = a[1];
        for (int i = 0; i < length; i++)
        {
            y[i] = b0 * x[i] + z;
            z = b1 * x[i] - a1 * y[i];
        }
        return y;
    }