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

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

#include <stdio.h>
#include <math.h>

#define	LP	1
#define	HP	2
#define	BP	3
#define	BS	4

#define BUTT	1
#define	CHEB	2
#define	INVCH	3
#define	ELLIP	4

#define	MAXPOLES	30

#define invcosh(x)	log( (x) + sqrt((x)*(x) - 1) )
#define invsinh(x)	log( (x) + sqrt((x)*(x) + 1) )
#define order(x)	( (double) ( (int) (x) ) == (x) ? (int) (x) : (int) ((x) + 1) )
#define sgn(x)		( (x) < 0 ? -1 : 1)

#define	PI	3.1415927
#define	BIG	999999999

struct cmplx_num        {               /* The Complex number structure */
                        double real;
                        double imag;
                        };

#define         sqr(X)          ( X )*( X )

#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 cmplx_tmp; \
                                cmplx_tmp.real = X.real * Y.real - \
						 X.imag * Y.imag ; \
                                cmplx_tmp.imag = X.real * Y.imag + \
						 X.imag * Y.real ; \
                                Z.real = cmplx_tmp.real; \
                                Z.imag = cmplx_tmp.imag; \
                                }
#define         cdiv(X, Y, Z)   { \
                                struct cmplx_num cmplx_tmp; \
                                double den; \
                                den = sqr( Y.real ) + sqr( Y.imag ); \
                                cmplx_tmp.real = X.real * Y.real + \
						 X.imag * Y.imag ; \
                                cmplx_tmp.imag = X.imag * Y.real - \
						 X.real * Y.imag ; \
                                Z.imag = cmplx_tmp.imag / den; \
                                Z.real = cmplx_tmp.real / den; \
                                }
#define         cmag(X)         sqrt( sqr(X.real) + sqr(X.imag) )

main()
{
	int	type,  design;
	double	fp,  fs,  f1,  f2,  f3,  f4;
	double	wp,  ws,  w1,  w2,  w3,  w4,  wx,  dw;
	double	Op,  Os,  O1,  O2,  O3,  O4;
	double	ap,  as;
	double	ap1, as1;
	double	M,   eps,  a;
	double	phi[MAXPOLES],  start_angle,  angle_incr;
	double	r,   theta;
	int	n,   i;

	struct cmplx_num	pole[MAXPOLES*2],  zero[MAXPOLES*2];
	struct cmplx_num	tmp1,  tmp2;
	struct cmplx_num	k,  knum,  kden;


	fprintf(stderr, "Filter Design\n\n");

					/* get filter type */
	do
	{
		fprintf(stderr, "1) Low Pass\n");
		fprintf(stderr, "2) High Pass\n");
		fprintf(stderr, "3) Band Pass\n");
		fprintf(stderr, "4) Band Stop\n\n");

		fprintf(stderr, "Enter filter type: ");

		scanf("%d", &type);
	} while (type < 1 || type > 4);

					/* get filter design type */
	do
	{
		fprintf(stderr, "\n1) Butterworth\n");
		fprintf(stderr, "2) Chebyshev\n");
		fprintf(stderr, "3) Inverse Chebyshev\n");
		fprintf(stderr, "4) Elliptical\n\n");

		fprintf(stderr, "Enter filter design type: ");

		scanf("%d", &design);
	} while (design < 1 || design > 4);

					/* get cutoff frequencies */
	if (type == LP || type == HP)
	{
		fprintf(stderr, "\nEnter fp, fs: ");
		scanf("%lf,%lf", &fp, &fs);
		wp = 2*PI * fp;
		ws = 2*PI * fs;
	}
	else
	{
		fprintf(stderr, "\nEnter f1, f2, f3, f4: ");
		scanf("%lf,%lf,%lf,%lf", &f1, &f2, &f3, &f4);
		w1 = 2*PI * f1;
		w2 = 2*PI * f2;
		w3 = 2*PI * f3;
		w4 = 2*PI * f4;
	}

					/* get attenuation */
	fprintf(stderr, "\nEnter ap, as: ");
	scanf("%lf,%lf", &ap, &as);

	printf("\nTodd Day's Filter Design Program\n");
	printf(  "--------------------------------\n");
	printf("Designing a");
	switch(design)
	{
		case BUTT:
			printf(" Butterworth");
			break;
		case CHEB:
			printf(" Chebyshev");
			break;
		case INVCH:
			printf("n Inverse Chebyshev");
			break;
		case ELLIP:
			printf("n Elliptical");
			break;
	}
	switch(type)
	{
		case LP:
			printf(" Low Pass");
			break;
		case HP:
			printf(" High Pass");
			break;
		case BP:
			printf(" Band Pass");
			break;
		case BS:
			printf(" Band Stop");
			break;
	}
	printf("filter\n");
	switch(type)
	{
		case LP:
		case HP:
			printf("fp = %f, fs = %f", fp, fs);
			break;
		case BP:
		case BS:
			printf("f1 = %f, f2 = %f, f3 = %f, f4 = %f",
				f1, f2, f3, f4);
			break;
	}
	printf("\nap = %f, as = %f", ap, as);

					/* these value are used a lot */
	ap1 = pow( (double) 10, ap / (double) 10);
	as1 = pow( (double) 10, as / (double) 10);

					/* find "special pt" wx */
	switch (type)
	{
		case LP:
		case HP:
			switch (design)
			{
				case BUTT:
					wx = sqrt(wp * ws);
					break;
				case CHEB:
					wx = wp;
					break;
				case INVCH:
					wx = ws;
					break;
				case ELLIP:
					wx = sqrt(wp * ws);
					break;
			}
			break;
		case BP:
		case BS:
			wx = sqrt(w1 * w4);
			break;
	}

	printf("\n\nwx = %f\n", wx);
					/* find Op, Os for LP baseband */
	switch (type)
	{
		case LP:
			Op = wp / wx;
			Os = ws / wx;
			break;
		case HP:
			Op = wx / wp;
			Os = wx / ws;
			break;
		case BP:
			Os = w4 - w1;
			O2 = fabs( (w2*w2 - wx*wx) / w2);
			O3 = fabs( (w3*w3 - wx*wx) / w3);
			if (O2 > O3)
				Op = O2;
			else
				Op = O3;
			break;
		case BS:
			Op = 1 / (w4 - w1);
			O2 = fabs( w2 / (w2*w2 - wx*wx) );
			O3 = fabs( w3 / (w3*w3 - wx*wx) );
			if (O2 < O3)
				Os = O2;
			else
				Os = O3;
			break;

	}
	printf("\nOp = %f, Os = %f\n", Op, Os);

					/* now, filter is in LP' form */
					/* but first, we must find order (M) */
					/* also, find epsilon and a */

	switch (design)
	{
		case BUTT:
			M = log10( (as1 - 1) / (ap1 - 1) ) /
			    ( 2 * log10(Os/Op) );
			n = order(M);
			break;
		case CHEB:
			M = invcosh( sqrt((as1 - 1) / (ap1 - 1)) ) /
			    ( invcosh(Os/Op) );
			eps = sqrt(ap1 - 1);
			n = order(M);
			a = 1/(double)n * invsinh( 1/eps );
			break;
		case INVCH:
			M = invcosh( sqrt((as1 - 1) / (ap1 - 1)) ) /
			    ( invcosh(Os/Op) );
			eps = 1 / sqrt(as1 - 1 );
			n = order(M);
			a = 1/(double)n * invsinh( 1/eps );
			break;
		case ELLIP:
			printf("\nSorry, not Implemented Yet!\n");
			exit(1);
	}
	if (n > MAXPOLES)
	{
		printf("\nSorry, order too high (%d)!\n", n);
		exit(1);
	}

	printf("\nOrder = %d, %f\n", n, M);
	printf("eps = %f, a = %f\n", eps, a);

					/* find dw for BP, BS filters */
	if (type == BP || type == BS)
	{
		switch (design)
		{
			case BUTT:
			{
				double	Oop,  Oos;
				Oop = wp / pow(ap1 - 1, 1 / (2*n));
				Oos = ws / pow(as1 - 1, 1 / (2*n));
				dw = sqrt(Oop * Oos);
			}
			case CHEB:
				dw = Op;
				break;
			case INVCH:
				dw = Os;
				break;
			case ELLIP:
				dw = sqrt(Op * Os);
				break;
		}
		if (type == BS)
			dw = 1 / dw;
	}
	printf("\ndw = %f\n", dw);

					/* find Butterworth angles */

	angle_incr = PI / n;
	start_angle = PI/2 - angle_incr/2;

	printf("\nButterworth angles (degrees)\n");
	for (i = 0; i < n; i++)
	{
		phi[i] = start_angle - i*angle_incr;
		printf("%f\n", phi[i] / PI * 180);
	}

					/* now find pole and zero positions */
	if (design == BUTT)
	{
		for (i = 0; i < n; i++)
		{
			pole[i].real = -wx * cos(phi[i]);
			pole[i].imag =  wx * sin(phi[i]);
			zero[i].real = 0;
			zero[i].imag = BIG;
		}
	}
	else if (design == CHEB || design == INVCH)
	{
		for (i = 0; i < n; i++)
		{			/* find the poles */
			pole[i].real = -cos(phi[i]) * sinh(a);
			pole[i].imag =  sin(phi[i]) * cosh(a);

			if (design == INVCH)
			{		/* invert the poles */
				invert(&pole[i]);
			}
		}
					/* calculate the zeros */
		if (design == CHEB)
		{
			for (i = 0; i < n; i++)
			{
				zero[i].real = 0;
				zero[i].imag = BIG;
			}
		}
		else if (design == INVCH)
		{
			for (i = 1; i < n; i += 2)	/* 1,3,5... */
			{
				zero[i].real = 0;
				zero[i-1].real = 0;
				zero[i].imag = 1 / cos(i*PI / (2*n) );
				zero[i-1].imag = -zero[i].imag;
			}
			if (n % 2)	/* if n odd,fake it for calc purposes */
			{
				zero[n-1].real = 0;
				zero[n-1].imag = BIG;
			}
		}
	}

	printf("\nLP Baseband Poles\n");
	for (i = 0; i < n; i++)
		printf("%g %g\n", pole[i].real, pole[i].imag);

	printf("\nLP Baseband Zeroes\n");
	for (i = 0; i < n; i++)
		printf("%g %g\n", zero[i].real, zero[i].imag);

					/* frequency transform */
	if (type == LP)
	{				/* s = S * wx */
		for (i = 0; i < n; i++)
		{
			pole[i].real *= wx;
			pole[i].imag *= wx;

			if (design == INVCH || design == ELLIP)
			{
				zero[i].real *= wx;
				zero[i].imag *= wx;
			}
		}
	}
	else if (type == HP)
	{				/* s = wx / S */
		for (i = 0; i < n; i++)
		{
			invert(&pole[i]);
			pole[i].real *= wx;
			pole[i].imag *= wx;

			if (design == INVCH || design == ELLIP)
			{
				invert(&zero[i]);
				zero[i].real *= wx;
				zero[i].imag *= wx;
			}
		}
	}
	else if (type == BP)
	{
		for (i = 0; i < n; i++)
		{
			bpconvert(pole, dw, wx, i, n);
			bpconvert(zero, dw, wx, i, n);
		}
	}
	else if (type == BS)
	{
		for (i = 0; i < n; i++)
		{
			bsconvert(pole, dw, wx, i, n);
			bsconvert(zero, dw, wx, i, n);
		}
	}

	if (type == BP || type == BS)
		n *= 2;

					/* find k */
	knum.real = 1;
	knum.imag = 0;
	kden.real = 1;
	kden.imag = 0;
	for (i = 0; i < n; i++)
	{
		cmul(knum, zero[i], knum);
		cmul(kden, pole[i], kden);
	}
	cdiv(kden, knum, k);

	printf("\n\nK multiplier\n");
	printf("%g %g\n", k.real, k.imag);

	printf("\nPoles\n");
	for (i = 0; i < n; i++)
		printf("%g %g\n", pole[i].real, pole[i].imag);

	printf("\nZeros\n");
	for (i = 0; i < n; i++)
		printf("%g %g\n", zero[i].real, zero[i].imag);
}

invert(x)
struct cmplx_num *x;
{
	double	r,  theta;

	r = 1 / sqrt( sqr(x->real) + sqr(x->imag) );
	if (x->real == 0)
	{
		if (x->imag < 0)
			theta = 3*PI/2;
		else
			theta = PI/2;
	}
	else
		theta = atan2(x->imag, x->real);

	x->real = r * cos(theta);
	x->imag = r * sin(theta);
}

imsqrt(x)
struct cmplx_num *x;
{
	double	r,  theta;

	r = sqrt( sqrt( sqr(x->real) + sqr(x->imag) ) );
	if (x->real == 0)
	{
		if (x->imag < 0)
			theta = 3*PI/2;
		else
			theta = PI/2;
	}
	else
	{
		theta = atan2(x->imag, x->real);
		if (theta < 0)
			theta += (2*PI);
	}
	theta /= 2;

	x->real = r * cos(theta);
	x->imag = r * sin(theta);
}

bpconvert(x, dw, wx, i, n)
struct cmplx_num	x[];
double			dw, wx;
int			i,  n;
{
	struct cmplx_num	tmp1,  tmp2;

					/* s^2 - dw*S*s + wx^2 = 0 */

					/* -b */
	tmp1.real = tmp2.real = dw * x[i].real;
	tmp1.imag = tmp2.imag = dw * x[i].imag;
					/* b*b */
	cmul(tmp2, tmp2, tmp2);
					/* b*b - 4*a*c */
	tmp2.real -= (4*wx*wx);
					/* sqrt (b*b - 4*a*c) */
	imsqrt(&tmp2);
					/* div by 2*a */
	tmp1.real /= 2;
	tmp1.imag /= 2;
	tmp2.real /= 2;
	tmp2.imag /= 2;
					/* +/- */
	x[i].real = tmp1.real + tmp2.real;
	x[i].imag = tmp1.imag + tmp2.imag;
	x[i+n].real = tmp1.real - tmp2.real;
	x[i+n].imag = tmp1.imag - tmp2.imag;
}

bsconvert(x, dw, wx, i, n)
struct cmplx_num	x[];
double			dw, wx;
int			i,  n;
{
	struct cmplx_num	tmp1,  tmp2,  tmp3;

					/* S*s^2 - dw*s + S*wx^2 = 0 */

					/* -b */
	tmp1.real = tmp2.real = dw;
	tmp1.imag = tmp2.imag = 0;
					/* b*b */
	tmp2.real *= tmp2.real;
					/* 4*a*c */
	tmp3.real = x[i].real;
	tmp3.imag = x[i].imag;
	cmul(tmp3, tmp3, tmp3);
	tmp3.real *= (4*wx*wx);
	tmp3.imag *= (4*wx*wx);
					/* b*b - 4*a*c */
	tmp2.real -= tmp3.real;
	tmp2.imag -= tmp3.imag;
					/* sqrt (b*b - 4*a*c) */
	imsqrt(&tmp2);
					/* div by 2*a */
	tmp3.real = 2 * x[i].real;
	tmp3.imag = 2 * x[i].imag;
	cdiv(tmp1, tmp3, tmp1);
	cdiv(tmp2, tmp3, tmp2);
					/* +/- */
	x[i].real = tmp1.real + tmp2.real;
	x[i].imag = tmp1.imag + tmp2.imag;
	x[i+n].real = tmp1.real - tmp2.real;
	x[i+n].imag = tmp1.imag - tmp2.imag;
}

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