[sci.electronics] A C program to compute Fourier Transforms.

dtynan@sultra.UUCP (Der Tynan) (10/01/88)

Hi!
I received enough responses (and enough bounced mail!), that I'm hereby
posting my Fast Fourier Transform (FFT) program.  Do with it what you
want.  I don't really care.  However, if you really *must* send huge
amounts of money to ease your conscience, my postal address is in the
maps :-)  Just kidding.  It's free.  A word of caution, however.  This
program was NOT written to be public domain.  It was written for a client,
to check the fidelity of a real-time FFT system.  For this reason, it
focuses more on high fidelity, than it does on speed, or portability.  I'd
appreciate peoples comments on how they got on with this thing.  What little
inline documentation there is, is poor - like I said, it was written to
serve one unique purpose (which it did!).  The TWIDDLE factors are computed
at the beginning of the program, to save some time.  Also, the maximum
transform size is extremely limited (on this system it only goes to 8K).
The reason for this, is that it uses Singletons algorithm, which is an
out-of-place algorithm (twice as much memory).  Also, each data sample
(complex) is 'double', plus the storage for the TWIDDLE factors...
If you don't have complex data (and don't know what a HILBERT transform is),
then just zero out the imaginary component (this causes aliasing).
				Have fun!
				- Der

-----cut here-----cut here-----cut here-----cut here-----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh to create the files:
#	compute.c
# This archive created: Thu Sep 29 13:36:12 1988
export PATH; PATH=/bin:$PATH
echo shar: extracting "'compute.c'" '(5372 characters)'
if test -f 'compute.c'
then
	echo shar: will not over-write existing file "'compute.c'"
else
sed 's/^X//' << \SHAR_EOF > 'compute.c'
X#ifndef lint
Xstatic	char	*sccsid = "@(#)compute.c	1.0	4/10/87";
X#endif
X
X/*
X * This program is Public Domain.  It may be freely distributed, both as
X * source- and object-code.  No warranties, either implied or expressed
X * are provided with this code.  It is purely on an "AS-IS" basis.
X * Inclusion of this source (or related object) in any commercial product
X * is permitted.  This notice must remain intact, however.
X *
X * Copyright (C) 1987, 1988, Tynan Computers.  All rights reserved.
X */
X
X#include <stdio.h>
X#include <math.h>
X
X#define MAXITER		13
X#define MAXXFORM	8192
X#define OUTPUT		"fft.out"
X
Xstruct	sample	{
X	double	s_real;					/* REAL value */
X	double	s_imag;					/* IMAGINARY value */
X};
X
Xint	nsample;					/* No. of samples */
Xint	niter;						/* No. of iterations */
Xint	amask;						/* sin() address mask */
Xint	pageno;
Xdouble	xsin(), xcos();					/* Local functions */
Xdouble	*twp;						/* Pointer to twiddle */
Xstruct	sample	samplea[MAXXFORM], *dsrc;		/* Actual dataset (a) */
Xstruct	sample	sampleb[MAXXFORM], *ddst;		/* Actual dataset (b) */
X
X/*
X *		***** FFT Computation Program *****
X *
X *		Written by: Dermot Tynan, 10-Apr-87	(dtynan@zorba.uucp)
X *
X * This program uses Singletons Algorithm, Radix-2, Out-of-Place, Decimation-
X * in-Time.
X *
X * Compile with;	cc -O -o compute compute.c -lm
X *
X */
Xmain(argc, argv)
Xchar *argv[];
X{
X	register i, j;
X	register struct sample *sp;
X	double twd;
X	double xm;
X	FILE *fp;
X
X	/*
X	 * After each iteration, the source and destination banks are
X	 * swapped.  Hence, two pointers are used, which alternate
X	 * between banks of data.
X	 */
X	dsrc = samplea;
X	ddst = sampleb;
X	printf("FFT Computation Program - Written by Dermot Tynan.\n\n");
X	if (argc != 2) {
X		fprintf(stderr, "usage: compute filename\n");
X		exit(2);
X	}
X	if ((fp = fopen(argv[1], "r")) == NULL) {
X		perror(argv[1]);
X		exit(1);
X	}
X	printf("Input phase...\n");
X	for (i = 0, sp = dsrc; i < MAXXFORM; i++, sp++) {
X		if (feof(fp))
X			break;
X		fscanf(fp, "%lf %lf\n", &sp->s_real, &sp->s_imag);
X	}
X	fclose(fp);
X	nsample = i;
X	for (i = 0, niter = 1; i < MAXITER; i++) {
X		if (niter == nsample)
X			break;
X		if (niter > nsample) {
X			fprintf(stderr, "?Invalid # of samples.\n");
X			exit(1);
X		}
X		niter <<= 1;
X	}
X	niter = i;
X	printf("[%d samples - %d iterations]\n", nsample, niter);
X	/*
X	 * As an optimization, instead of constantly re-referencing the
X	 * sin() and cosine(), we generate a table with all the values
X	 * we will use.  For example, a 64pt FFT only uses 64 discrete
X	 * sine angles.  Some of these, however, are accessed up to six
X	 * times.  A table is thus built, holding all such values.
X	 */
X	printf("Twiddle generation phase...\n");
X	if ((twp = (double *)malloc(nsample * sizeof(double))) == NULL) {
X		fprintf(stderr, "?Error - insufficient memory.\n");
X		exit(1);
X	}
X	for (i = 0; i < nsample; i++) {
X		twd = (2.0 * M_PI * (double )i)/(double )nsample;
X		twp[i] = sin(twd);
X	}
X	/*
X	 * Open the output file now, just in case there's a problem.
X	 */
X	if ((fp = fopen(OUTPUT, "w")) == NULL) {
X		perror(OUTPUT);
X		exit(1);
X	}
X	printf("Transform phase...\n");
X	amask = 0;
X	for (i = 0; i < niter; i++) {
X		printf("--- iteration #%d\n", i + 1);
X		compute();				/* compute an iter. */
X		amask <<= 1;				/* Open up the mask */
X		amask  |= 1;
X		sp   = dsrc;				/* Swap banks */
X		dsrc = ddst;
X		ddst = sp;
X	}
X	printf("Output phase...\n");
X	for (i = 0; i < nsample; i++) {
X		/*
X		 * Do bit-reversal, and output.
X		 */
X		sp = &dsrc[bitrev(i)];
X		fprintf(fp, "%26.15lf\t%26.15lf\n", sp->s_real, sp->s_imag);
X	}
X	printf("Done!\n");
X	fclose(fp);
X	exit(0);
X}
X
X/*
X * This routine performs one iteration of an FFT.  The data is read from
X * dsrc, through the butterfly computation, and written to ddst (this is
X * singletons out-of-place algorithm).
X */
Xcompute()
X{
X	register n, k, th;
X	register struct sample *sp;
X	double ar, ai;
X	double br, bi;
X	double wr, wi;
X
X	k = nsample / 2;				/* k = N/2 */
X	for (n = 0; n < k; n++) {
X		/*
X		 * Read x(n) and x(n + N/2)
X		 * x(n)       = A(n) = (ar, ai).
X		 * x(n + N/2) = B(n) = (br, bi).
X		 */
X		sp = &dsrc[n];
X		ar = sp->s_real;
X		ai = sp->s_imag;
X		sp = &dsrc[n+k];
X		br = sp->s_real;
X		bi = sp->s_imag;
X		/*
X		 * Calculate TWIDDLE factors.
X		 */
X		th = bitrev(n & amask) >> 1;
X		wr = xcos(th);
X		wi = xsin(th);
X		/*
X		 * Do butterfly calculation.
X		 * A'(n) = A + BWk
X		 * B'(n) = A - BWk
X		 */
X		sp = &ddst[n << 1];
X		sp->s_real = ar + br * wr + bi * wi;
X		sp->s_imag = ai + bi * wr - br * wi;
X		sp++;
X		sp->s_real = ar - br * wr - bi * wi;
X		sp->s_imag = ai - bi * wr + br * wi;
X	}
X}
X
X/*
X * These two functions replicate the actual math library routines.  They
X * use an internal memory image, instead of computing the actual values.
X */
Xdouble
Xxcos(th)
Xregister th;
X{
X	th += (nsample / 4);
X	return(xsin(th));
X}
X
Xdouble
Xxsin(th)
Xregister th;
X{
X	register i, j;
X	th &= (nsample - 1);
X	return(twp[th]);
X}
X
X/*
X * In the FFT world, bit reversals are a fact of life.  For example, assume
X * a seven bit word: 0x65.  In binary, this is 1100101.  When bit reversed,
X * the result is 1010011 (see the difference?).  Giving a result of 0x53.
X * This is further complicated by the fact that the no. of bits is a function
X * of the transform size.
X */
Xbitrev(n)
Xregister n;
X{
X	register i, r;
X
X	for (i = r = 0; i < niter; i++, r <<= 1, n >>= 1)
X		if (n & 1)
X			r |= 1;
X	r >>= 1;				/* We shifted once too often */
X	return(r);
X}
SHAR_EOF
if test 5372 -ne "`wc -c < 'compute.c'`"
then
	echo shar: error transmitting "'compute.c'" '(should have been 5372 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Reply:	dtynan@sultra.UUCP		(Der Tynan @ Tynan Computers)
	{mips,pyramid}!sultra!dtynan
	Cast a cold eye on life, on death.  Horseman, pass by...    [WBY]