ftp.nice.ch/pub/next/developer/hardware/dsp/drbub/analog/pzsim.c

This is pzsim.c in view mode; [Download] [Up]

/**********************************************************************
*       This program calculates the magnitude of a transfer function
*       given all the pole and zero locations.  A scalar constant
*       K can also be entered. 
*                                               Steven Punte
*                                               Sept. 24 1987
***********************************************************************/

#include        <stdio.h>
#include        <math.h>
#define         TRUE            1
#define         FALSE           0
#define         MAX_ARRAY       50      /* Max number of poles or zeros */
#define         PIE             3.141592653
#define         STR_LNGTH       20
#define         scanf           my_scanf

#define         abs(X)          ( X > 0 ? X : -X )
#define         sqr(X)          ( X )*( X )
#define         scal(X, Y)      while( abs( X ) > 1000) { X *= 0.0001; Y += 4; } \
                                while( abs( X ) < 0.0001) { X *= 10000; Y -= 4; }       
#define         clear(X)        X.real = 0; X.imag = 0
#define         cmov(X, Y)      Y.real = X.real; Y.imag = X.imag
#define         cset(X, Y, Z)   X.real = Y; X.imag = Z
#define         cmpl(X)         X.imag = - X.imag
#define         cadd(X, Y, Z)   Z.real = X.real + Y.real;\
                                Z.imag = X.imag + Y.imag
#define         csub(X, Y, Z)   Z.real = X.real - Y.real;\
                                Z.imag = X.imag - Y.imag
#define         cmul(X, Y, Z)   { \
                                struct cmplx_num tmp; \
                                tmp.real = X.real * Y.real - X.imag * Y.imag ; \
                                tmp.imag = X.real * Y.imag + X.imag * Y.real ; \
                                Z.real = tmp.real; \
                                Z.imag = tmp.imag; \
                                }
#define         cdiv(X, Y, Z)   { \
                                struct cmplx_num tmp; \
                                double den; \
                                den = sqr( Y.real ) + sqr( Y.imag ); \
                                tmp.real = X.real * Y.real + X.imag * Y.imag ; \
                                tmp.imag = X.imag * Y.real - X.real * Y.imag ; \
                                Z.imag = tmp.imag / den; \
                                Z.real = tmp.real / den; \
                                }
#define         cmag(X)         sqrt( sqr( X.real) + sqr( X.imag))
                                

struct cmplx_num        {
                        double real;
                        double imag;
                        };

int flag_S      =       FALSE;
int flag_E      =       FALSE;
int flag_PT     =       FALSE;
int flag_LOG    =       FALSE;
int flag_RADS   =       FALSE;
int flag_PHASE  =       FALSE;
int flag_STEP   =       FALSE;

double start;   /* Starting Frequency */
double endd;    /* Ending Frequency */
int points;             /* Number of partitions */
double K_coeff = 1.0;   /* Front Scaler coeffficient (1.0 default value) */
int K_exp = 0;

int numb_poles = 0;     /* Number of poles (either real or complex) */
int numb_zeros = 0;     /* Number of zeros (either real or complex) */

int i;                  /* Index for summation calculations */
char str[ STR_LNGTH ] ; /* String buffer for input reading */
int data_set    = 0;    /* Number of sets of data to be processed */
double ctemp;           /* Used for complex divide */

struct cmplx_num poles[ MAX_ARRAY ];
struct cmplx_num zeros[ MAX_ARRAY ];
struct {
        double x[500];
        double y[500];
        int numb_point;
        char name[ STR_LNGTH ];
        } output[ 5 ];

main()
{
int done_chk;

do {
        done_chk = read_set();
        check_input();

        if( flag_STEP == TRUE ) step_response();
        else transfer_respones();

        data_set++;
        } while( done_chk != EOF );

display();

} /* End of main */



transfer_respones()
{
double delta;           /* Frequency increment in hertz */
double x;               /* Frequency being analysized in hertz */
double ampl;
double angle;
double gamma;
int pnt;
struct cmplx_num cgamma, tmp;
struct cmplx_num  K_scale;
double scale_data();

gamma = scale_data();
cset( K_scale, K_coeff, 0);

delta = (endd - start) / points;

/* Main loop; Calculates magnitude at each frequency */

pnt = 0;
for( x = start; x < endd ; x += delta ) {

        struct cmplx_num  numerator;    /* Numerator of magnitude calculation */
        struct cmplx_num  denominator;  /* Denominator of mangitude calculation */
        struct cmplx_num  magnitude;    /* Final resulting magnitude */
        struct cmplx_num  temp1, temp2;
        struct cmplx_num  omega;

        if( flag_RADS == FALSE ) { cset( omega, 0, (2*PIE*x / gamma ) ); }
        else { cset( omega, 0, ( x / gamma )); }

        cset( numerator, 1, 0 );
        cset( denominator, 1, 0 );
        
        /* Main calculations here */
        for( i = 0; i < numb_zeros ; i++ ) {
                csub( omega, zeros[ i ], temp1 );
                cmul( temp1, numerator, numerator); 
                }
        for( i = 0; i < numb_poles ; i++ ) { 
                csub( omega, poles[ i ], temp1 );
                cmul( temp1, denominator, denominator); 
                }

        /* In case of underflow */
        if( cmag( denominator) == 0.0 ) {
                cset( magnitude, 100000000000000.0, 0.0 );
                }
        else {
                cdiv( numerator, denominator, magnitude );
                cmul( magnitude, K_scale, magnitude );
                }

        if( flag_PHASE == TRUE ) {
                angle = 180 / PIE * atan2( magnitude.imag, magnitude.real );
/*
                printf( "%15.5f  %15.5f \n", x, angle );
*/
                output[ data_set ].x[ pnt ] = x;
                output[ data_set ].y[ pnt ] = angle;
                pnt++;

                }

        else {
                ampl = cmag( magnitude );

                if( flag_LOG == TRUE ) {
                        if( ampl == 0.0 ) ampl = -1000; 
                        else ampl = -20 * log10( ampl );
                        }
        
/*
                printf( "%15.5f  %15.5f \n", x, ampl );
*/
                output[ data_set ].x[ pnt ] = x;
                output[ data_set ].y[ pnt ] = ampl;
                pnt++;
                }
        }
if( flag_PHASE == TRUE ) strcpy( output[ data_set ].name, "Phase" );
else strcpy( output[ data_set ].name, "Magnitude" );

output[ data_set ].numb_point = pnt;

} /* End of transfer response */

step_response()
{
double delta;           /* Frequency increment in hertz */
double x;               /* Frequency being analysized in hertz */
double gamma ;
struct cmplx_num C[ MAX_ARRAY ];
int k;
int pnt;

gamma = scale_data();

for( k = 0; k < numb_poles ; k++) {
        struct cmplx_num  numerator;    /* Numerator of magnitude calculation */
        struct cmplx_num  denominator;  /* Denominator of mangitude calculation */
        struct cmplx_num  magnitude;    /* Final resulting magnitude */
        struct cmplx_num  temp1, temp2;

        cset( numerator, K_coeff, 0 );
        cset( denominator, 1, 0);

        for( i = 0; i < numb_zeros ; i++ ) {
                csub( poles[k], zeros[i], temp1 );
                cmul( temp1, numerator, numerator );
                }

        for( i = 0; i < numb_poles ; i++ ) {
                if( i != k) {
                        csub( poles[k], poles[i], temp1 );
                        cmul( temp1, denominator, denominator );
                        }
                }
        cdiv( numerator, denominator, C[k]);
        }
        
delta = (endd - start) / points;

/* Main loop; Calculates magnitude at each frequency */

for( x = start; x < endd ; x += delta ) {

        struct cmplx_num  temp1, temp2, temp3, sum;
        cset( sum, 0, 0);

        for( k = 0; k < numb_poles ; k++) {

                cset( temp1, ( x * gamma ), 0);
                cmul( poles[k], temp1, temp2);
                cset( temp1, cos( temp2.imag), sin( temp2.imag) );
                cset( temp2, exp( temp2.real), 0);
                cmul( temp1, temp2, temp3 );
                cset( temp1, 1, 0);
                csub( temp3, temp1, temp2);
                cmul( temp2, C[k], temp3);
                cdiv( temp3, poles[k], temp1);

                cadd( sum, temp1, temp2);
                cmov( temp2, sum);

                }
        /*
        printf( "%15.5f  %15.5f  %f15.5 \n", x, sum.real, sum.imag );
        printf( "%15.5f  %15.5f  \n", x, sum.real);
        */
        output[ data_set ].x[ pnt ] = x;
        output[ data_set ].y[ pnt ] = sum.real;
        pnt++;
        }
strcpy( output[ data_set ].name, "Step" );
output[ data_set ].numb_point = pnt;
}

/* Stack overflow error routing and exit */
overflow_error()
{
        fprintf( stderr, "Internal Array Size has been exceeded \n");
        fprintf( stderr, "Recompile with larger \"MAX_ARRAY\" size. \n");
        fprintf( stderr, "Fatal Error \n");
        exit(1);
}



/* Loops over all input lines, and enters data into proper variables */
read_set()
{
double scan_modifier();
int expon;      /* The tens power exponent of the metric scale factor */
*str = EOF;

flag_LOG        =       FALSE;
flag_RADS       =       FALSE;
flag_PHASE      =       FALSE;
flag_STEP       =       FALSE;
K_coeff = 1.0;  /* Front Scaler coeffficient (1.0 default value) */

numb_poles = 0; /* Number of poles (either real or complex) */
numb_zeros = 0; /* Number of poles (either real or complex) */

while( (scanf( "%s", str) != EOF) && ( *str != '#') ) {

        if( !strcmp( str, "FS") || !strcmp( str, "TS") || !strcmp( str, "S" )) {
                scanf( "%f", &start);
                start *= scan_modifier( stdin, &expon );
                flag_S = TRUE;
                }

        else if( !strcmp( str, "FE") || !strcmp( str, "TE") || !strcmp( str, "E" )) {
                scanf( "%f", &endd);
                endd *= scan_modifier( stdin, &expon );
                flag_E = TRUE;
                }

        else if( !strcmp( str, "FP") || !strcmp( str, "TP") || !strcmp( str, "PT" )) {
                scanf( "%d", &points);
                points *= (int)scan_modifier( stdin, &expon );
                flag_PT = TRUE;
                }

        else if( !strcmp( str, "K") ) {
                scanf( "%f", &K_coeff);
                scan_modifier( stdin, &expon );
                K_exp = expon;
                }

        else if( !strcmp( str, "P") ) {
                if( numb_poles >= MAX_ARRAY) overflow_error();
                scanf( "%f", &poles[ numb_poles ].real);
                poles[ numb_poles ].real *= scan_modifier( stdin, &expon );
                poles[ numb_poles++ ].imag = 0.0;
                }

        else if( !strcmp( str, "Z") ) {
                if( numb_poles >= MAX_ARRAY) overflow_error();
                scanf( "%f", &zeros[ numb_zeros ].real);
                zeros[ numb_zeros ].real *= scan_modifier( stdin, &expon );
                zeros[ numb_zeros++ ].imag = 0.0;
                }

        else if( !strcmp( str, "PC") ) {
                double real, imag, temp;
                if( numb_poles >= MAX_ARRAY - 1) overflow_error();
                /* Assumes as complex conjugate pair */
                scanf( "%f", &real);
                scanf( "%f", &imag);
                temp = scan_modifier( stdin, &expon );
                real *= temp;
                imag *= temp;
                poles[ numb_poles ].real = real;
                poles[ numb_poles++ ].imag = imag;
                /* Also enter complex pair */
                poles[ numb_poles ].real = real;
                poles[ numb_poles++ ].imag = - imag;
                }

        else if( !strcmp( str, "ZC") ) {
                double real, imag, temp;
                if( numb_poles >= MAX_ARRAY - 1) overflow_error();
                /* Assumes as complex conjugate pair */
                scanf( "%f", &real);
                scanf( "%f", &imag);
                temp = scan_modifier( stdin, &expon );
                real *= temp;
                imag *= temp;
                zeros[ numb_zeros ].real = real;
                zeros[ numb_zeros++ ].imag = imag;
                /* Also enter complex pair */
                zeros[ numb_zeros ].real = real;
                zeros[ numb_zeros++ ].imag = - imag;
                }

        else if( !strcmp( str, "LOG") ) {
                scan_modifier( stdin, &expon );
                flag_LOG = TRUE;
                }

        else if( !strcmp( str, "RADS") ) {
                scan_modifier( stdin, &expon );
                flag_RADS = TRUE;
                }

        else if( !strcmp( str, "PHASE") ) {
                scan_modifier( stdin, &expon );
                flag_PHASE = TRUE;
                }

        else if( !strcmp( str, "STEP") ) {
                scan_modifier( stdin, &expon );
                flag_STEP = TRUE;
                }

        else {
                fprintf( stderr, "Syntax Error, ");
                fprintf( stderr, "Unrecognized input string \"%s\". ", str );
                fprintf( stderr, "String ignored. \n");
                }
        }

if( *str != '#' ) *str = EOF ;
return( *str );
} /* End of read subroutine */

/* Checks if essential input informaiton is present */
check_input()
{
if( flag_S == FALSE ) {
        fprintf( stderr, "Starting point unspecified! " );
        fprintf( stderr, "Fatal Error. \n");
        exit(1);
        }

if( flag_E == FALSE ) {
        fprintf( stderr, "Ending point unspecified! " );
        fprintf( stderr, "Fatal Error. \n");
        exit(1);
        }

if( flag_PT == FALSE ) {
        fprintf( stderr, "Number of data points unspecified! \n" );
        fprintf( stderr, "Default of 20 will be used. \n");
        points = 20;
        }
}


/* Looks for some type of value modifie, such as kilo (x1000)
*  or micro (x0.000001).  Then will be expecting a colon as endd
*  of line marker.  Returns a double persion number for
*  scaling purposes.    
*/

double scan_modifier( input, expon )
FILE *input;
int *expon;
{
double scale = 1.0;
char string[ STR_LNGTH ];

while( (fscanf( input, "%s", string) != EOF) && (*string != ';' ) ) {

        switch( *string) {

        case 'K':
                scale = 1000;
                *expon = 3;
                break;

        case 'M':
                scale = 1000000;
                *expon = 6;
                break;

        case 'G':
                scale = 1000000000;
                *expon = 9;
                break;

        case 'T':
                scale = 1000000000000;
                *expon = 12;
                break;

        case 'm':
                scale = 0.001;
                *expon = -3;
                break;

        case 'u':
                scale = 0.000001;
                *expon = -6;
                break;

        case 'n':
                scale = 0.000000001;
                *expon = -9;
                break;

        case 'p':
                scale = 0.000000000001;
                *expon = -12;
                break;

        case 'E':
                {
                int K;
                fscanf( input, "%d", &K);
                *expon = K;
                if( K > 30 ) K = 30;
                if( K < -30) K = -30;
                while( K-- > 0 ) scale *= 10;
                while( K++ < 0 ) scale *= 0.1;
                }
                break;

        default:
                fprintf( stderr, "Unrecognized string \"%s\" \n", string);
                fprintf( stderr, "Metric modifying unit expected. \n" );
                fprintf( stderr, "String Ignored. \n");
                

                }
        }
return( scale );
}

display()
{
int i, j;

/*
printf("%d \n", data_set);
*/

for(i = 0; i < data_set ; i++ ) {
        printf( "%d %s \n", output[i].numb_point, output[i].name );
        printf( "%d \n", output[i].numb_point);
        for(j = 0; j < output[ i ].numb_point ; j++) {
                printf( "%15f, %15f \n", output[i].x[j], output[i].y[j]);
                }
        }
}


double scale_data()
{
double net_mean ;
int mean_order  ;
double gamma, mag;
struct cmplx_num cgamma;

net_mean        = 0.0;
mean_order      = 0;

/* Find geometric mean of all non zeros and poles */
for( i = 0; i < numb_zeros ; i++ ) {
        mag = cmag( zeros[i] );
        if( mag > 0 ) {
                net_mean += log( mag);
                mean_order++;
                }
        }
for( i = 0; i < numb_poles ; i++ ) {
        mag = cmag( poles[i] );
        if( mag > 0 ) {
                net_mean += log( mag);
                mean_order++;
                }
        }
gamma = exp( net_mean / (double)mean_order );

/* Set complex scaling constant, and scale all poles and zeros */
cset( cgamma, (1 / gamma), 0);

for( i = 0; i < numb_zeros ; i++ ) {
        cmul( zeros[i], cgamma, zeros[i]);
        }
for( i = 0; i < numb_poles ; i++ ) {
        cmul( poles[i], cgamma, poles[i]);
        }

/* Rescale K_coeff, note: care for overflow must be observed */
K_coeff = exp( (double)(numb_zeros - numb_poles)*log( gamma ) + log( K_coeff ) + 2.30258509 * (double)K_exp );

return( gamma );
} /* End of data scale routine */


#undef          scanf
/* A lousy fucking patch for micro-soft C.  It doesn't
*  seem to read floating point numbers properly.      
*/
my_scanf( str, reg)
char *str;
union {
        int x;
        double y;
        char t;
        } *reg;
        
{
double atof();
if( !strcmp( str, "%f") ) {
        char temp[40];
        scanf( "%s", temp);
        reg->y = atof( temp);
        }
else scanf( str, reg);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.