View Raw SPL
/*****************************************************************************
*                                                                            *
*   ZPFCOEF.SPL   Copyright (C) 2001 DSP Development Corporation             *
*                               All Rights Reserved                          *
*                                                                            *
*   Author:      Randy Race                                                  *
*                                                                            *
*   Synopsis:    Designs digital filter given analog zeros and poles         *
*                                                                            *
*   Revisions:   30 Jan 2001  RRR  Creation                                  *
*                                                                            *
*****************************************************************************/

#if @HELP_ZPFCOEF

    ZPFCOEF

    Purpose: Designs a digital filter from a set of analog (s domain)
             zeros and poles

    Syntax:  ZPFCOEF(z, p, K, Fs, Fp)

                 z - a series consisting of the zeros of the analog
                     filter transfer function in Hertz

                 p - a series consisting of the poles of the analog
                     filter transfer function in Hertz

                 K - an optional real, the filter gain, defaults to 1.0

                Fs - an optional real, the sample rate of the digital filter,
                     defaults to 1.0

                Fp - an optional integer, the warping frequency. The
                     magnitude to the digital filter at Fp matches
                     the magnitude of the analog filter, defaults to
                     Fs.

    Returns: A series, the coefficients of the digital filter in cascade
             form.

    Example:
              f := 0..1..10000
              w := 2*pi*f
              s := i*w

             W1: 5 * (s+40*2*pi)*(s+30*2*pi) / ((s+20*2*pi)*(s+300*2*pi))
             W2: 20 * log10(mag(W1));setxlog(1)
             W3: zpfcoef({-40, -30}, {-20, -300}, 5, 10000, 200)
             W4: clogmag(W3, 4096);setxlog(1)
             W5: integ(gnorm(1000, 1/10000))
             W6: cascade(W5, W3)


             W1 contains the original S domain transfer function. The
             magnitude of the transfer function is displayed in W2.

             W3 contains the resulting digital filter coefficients in
             2nd order cascade form. The sample rate of the digital filter
             is 10000 Hz and the response is set to match the analog filter
             at 200 Hz. The entire frequency response of the digital filter
             is displayed in W4.

             W5 contains synthesized data and W5 filters the data with
             the resulting digital filter in W3.

    Remarks:
             The digital filter conversion is performed using the
             bilinear transform. The cascade format is used for speed
             and accuracy in the digital filtering process.

             The number of poles must equal or exceed the number
             of zeros.

    See Also:
             Bilinear
             Cascade
             DADiSP Filters Module
#endif


zpfcoef(z, p, K, Fs, Fp)
{
        local zD, pD, kD, num, den, coef;

        if (argc < 5)
        {
                if (argc < 4)
                {
                        if (argc < 3)
                        {
                                if (argc < 2)
                                {
                                        error("zpfcoef - zeros and poles series required");
                                }

                                K = 1.0;
                        }

                        Fs = 1.0;
                }

                if (Fs <= 0) Fs = 1.0;

                Fp = Fs;
        }

        /* convert H(s) zeros & poles to H(z) using the bilinear transform */
        (zD, pD, kD) = bilinear((sort(z * 2 * pi, 1)), (sort(p * 2 * pi, 1)), K, Fs, Fp);

        /* expand roots into second order stages */
        num = cascadexp(-zD);
        den = cascadexp(-pD);

        /* break into individual 2nd order stages */
        num = ravel(num, 3);

        /* multiply first numerator stage by Gain so we can set it to 1.0 */
        num[.., 1] *= kD;

        /* remove leading 1 from each demoninator stage - as per cascade form */
        den = extract(ravel(den, 3), 2, 2);

        /* append unity gain and convert to single column */
        coef = {1, unravel({num, den})};

        /* set filter rate */
        setdeltax(coef, 1 / Fs);

        return(coef);
}


/* convert roots of a polynomial to 2nd order stages */
cascadexp(r)
{
        if (length(r) == 1)
        {
                /* b0, b1*z^-1, b2*z^-2 */
                return( {1, r[1], 0});
        }

        if (length(r) == 2)
        {
                return( {1, r[1] + r[2], r[1]*r[2]});
        }
        else
        {
                /* recurse on the remaining stages */
                return(concat( {1, r[1] + r[2], r[1]*r[2]}, cascadexp(extract(r, 3, -1))));
        }
}