home *** CD-ROM | disk | FTP | other *** search
- /* -------------------------------------------------------------------- */
- /* fft.c by Carl W. Moreland Version 2.3 04/03/92 */
- /* -------------------------------------------------------------------- */
- /* */
- /* Fast Fourier Transform */
- /* */
- /* The following program is intended for Fourier analysis of circuit */
- /* simulations. It can be invoked from the command line with no inter- */
- /* action so it is ideal for batch files. The syntax is: */
- /* */
- /* fft infile.ext <outfile.ext </c> <'Comments'>> </n=x> </e> </s> */
- /* */
- /* infile.ext is the name of the file containing data. There must be */
- /* 2^n data points and must represent an integer number of cycles. */
- /* */
- /* outfile.ext is an optional file to store the harmonic information. */
- /* */
- /* /c allows the user to enter comments for each analysis. This is in */
- /* addition to the command line comment. Each comment line is limi- */
- /* ted to 80 characters; there is no line limit. */
- /* */
- /* /n=x specifies the highest order harmonic to be printed. By default */
- /* the program will print up to the 10th, or numpts/2-1, whatever */
- /* is larger. If x is larger than numpts/2-1 it will be ignored. */
- /* */
- /* /e specifies exponential format instead of decimal. This is for */
- /* those working on 14+ bit projects. */
- /* */
- /* /s suppresses screen output */
- /* */
- /* Version 2 now supports comments from the source file. They must */
- /* begin with the ""#"" character. DC offset and fundamental amplitude */
- /* are now printed. */
- /* */
- /* Version 2.1 adds dynamic memory allocation for the data array so max */
- /* number of points is now limited by RAM. (05/04/90) */
- /* */
- /* Version 2.2 fixes a bug that gives incorrect results when the DC */
- /* offset is greater than the fundamental amplitude. Harmonic bins are */
- /* now sorted wrt the fundamental. (04/17/91) */
- /* */
- /* Version 2.3 uses the C++ complex class. (04/03/92) */
- /* */
- /* Sun compiler: cc fft.c -lm -offt (Version 2.2 or before) */
- /* Note: Sun C does not recognize # conditionals or modern parameter */
- /* passing. */
- /* -------------------------------------------------------------------- */
-
- #include <math.h>
- #include <ctype.h>
- #include <malloc.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include "complex.h"
-
- void get_args(int argc, char *argv[]);
- int get_data(void);
- void put_data(complex x[]);
- void fft(int N, complex *x);
-
- struct arg_struct /* Holds the command line arguments */
- {
- char input[20];
- char output[20];
- int num_harm;
- int com_flag;
- int exp_flag;
- int sup_flag;
- int out_flag;
- char comment[81];
- } args;
-
- char *message[] =
- {
- "+----------------------------------------------------------------------+",
- "| Fast Fourier Transform Version 2.3 04/03/92 Carl W. Moreland |",
- "| |",
- "| Syntax: |",
- "| |",
- "| fft infile.ext <outfile.ext </c> <'Comments'>> </n=x> </e> </s> |",
- "| |",
- "| infile.ext is the name of the file containing data. There must be |",
- "| 2^n data points and must represent an integer number of cycles. |",
- "| |",
- "| outfile.ext is an optional file to store the harmonic information. |",
- "| |",
- "| /c allows interactive comments, 80 characters max. |",
- "| /n=x specifies the highest order harmonic to be printed. By default |",
- "| the program will print up to the 10th or numpts/2-1. |",
- "| /e specifies exponential format instead of decimal for the output. |",
- "| /s suppresses screen output |",
- "+----------------------------------------------------------------------+"
- };
-
- complex *x;
-
- /* ----- main --------------------------------------------------------- */
-
- main(int argc, char *argv[])
- {
- register int i;
- int numpts;
-
- if(argc < 2) /* Check for proper syntax */
- {
- for(i=0; i<18; i++)
- printf("%s\n", message[i]);
- exit(0);
- }
-
- get_args(argc, argv); /* Parse the command line arguments */
-
- x = (complex *)malloc(8*sizeof(complex));
- if(x == NULL)
- {
- printf("Insufficient memory for 8 data points\n");
- exit(0);
- }
-
- numpts = get_data(); /* Read the input file */
- if(numpts/2 < args.num_harm) /* See if there's less than 10 harmonics*/
- args.num_harm = numpts/2 - 1;
-
- fft(numpts, x); /* Perform FFT on data, return Mag & Ph */
- put_data(x); /* Write the output to screen/file */
- free(x);
-
- return 0;
- }
-
- /* ----- get_args ----------------------------------------------------- */
-
- void get_args(int argc, char *argv[])
- {
- register int i, j, k;
- char temp[81]; /* Holds the comment string */
-
- args.output[0] = '\0'; /* Initialize all parameters */
- args.num_harm = 10;
- args.com_flag = 0;
- args.exp_flag = 0;
- args.sup_flag = 0;
- args.out_flag = 0;
- args.comment[0] = '\0';
-
- strcpy(args.input, argv[1]); /* store the input filename */
-
- for(i=2; i<argc; i++) /* Parse thru each argument */
- {
- /* Check for "/" or "-" switch */
- if(argv[i][0] == '/' || argv[i][0] == '-')
- {
- if(argv[i][1] == 'c' || argv[i][1] == 'C') args.com_flag = 1;
- if(argv[i][1] == 'e' || argv[i][1] == 'E') args.exp_flag = 1;
- if(argv[i][1] == 's' || argv[i][1] == 'S') args.sup_flag = 1;
- if(argv[i][1] == 'n' || argv[i][1] == 'N')
- {
- k = 0;
- for(j=2;;j++)
- {
- if(argv[i][j] == '\0')
- {
- temp[k++] = argv[i][j];
- break;
- }
- if(isdigit(argv[i][j])) temp[k++] = argv[i][j];
- }
- args.num_harm = atoi(temp);
- }
- continue;
- }
-
- if(argv[i][0] == 0x27) /* Check for "'" for comment */
- {
- j = 1;
- for(k=0; k<81; k++) /* up to 80 characters */
- {
- if(argv[i][j] == 0x27) /* Check for "'" end of comment */
- {
- temp[k] = '\0';
- break;
- }
- if(argv[i][j] == '\0') /* Check for new word */
- {
- temp[k] = ' ';
- i++;
- j=0;
- continue;
- }
- if(k == 80) /* 80 is the max so terminate */
- {
- temp[k] = '\0';
- break;
- }
- temp[k] = argv[i][j++]; /* Copy character to temp string*/
- }
- strcpy(args.comment, temp);
- continue;
- }
- /* If it's nothing else then it */
- strcpy(args.output, argv[i]); /* must be the output file name */
- args.out_flag = 1;
- }
- }
-
- /* ----- get_data ----------------------------------------------------- */
-
- int get_data()
- {
- register int i;
- int points = 8; /* start with 8 data points */
- char temp[81];
- FILE *infile, *outfile;
-
- if((infile = fopen(args.input, "r")) == NULL) /* Open input file */
- {
- puts(" File not found\n");
- free(x);
- exit(0);
- }
-
- if(args.out_flag) outfile = fopen(args.output, "a");
-
- i = 0;
-
- /* Read file until EOF */
-
- while(fgets(temp, 81, infile) != NULL)
- {
- if(temp[0] == '#')
- {
- if(args.out_flag)
- {
- temp[0] = 0x20;
- fprintf(outfile, "%s", temp);
- }
- continue;
- }
- if(i == points)
- {
- points *= 2;
- x = (complex *)realloc(x, points*sizeof(complex));
- if(x == NULL)
- {
- printf("Insufficient memory for %d data points\n", points);
- exit(0);
- }
- }
- x[i].re = atof(temp);
- x[i++].im = 0;
- }
-
- fclose(infile);
- if(args.out_flag) fclose(outfile);
- return(i);
- }
-
- /* ----- put_data ----------------------------------------------------- */
-
- void put_data(complex x[])
- {
- register int i;
- char comment[81];
- double db, bits;
- double sum = 0;
- FILE *outfile;
-
- if(args.out_flag) /* Write data to an output file */
- {
- outfile = fopen(args.output, "a"); /* Open file for appending */
-
- if(args.com_flag) /* Allow interactive comments */
- {
- printf("Enter comment(s), <RET> when through.\n");
- for(;;)
- {
- gets(comment);
- if(comment[0] == NULL) break;
- fprintf(outfile, " %s\n", comment);
- }
- }
- }
-
- if(!args.sup_flag) /* Write data to the screen */
- {
- printf(" DC offset is %.2f\n", x[0].re);
- printf(" Fundamental amplitude is %.4f\n\n", x[1].re);
- printf(" # Magnitude Phase dB Bits \n");
- printf(" --- --------- -------- -------- ------\n");
- }
- if(args.out_flag)
- {
- fprintf(outfile, "\n");
- fprintf(outfile, " DC offset is %.2f\n", x[0].re);
- fprintf(outfile, " Fundamental amplitude is %.4f\n\n", x[1].re);
- fprintf(outfile, " # Magnitude Phase dB Bits \n");
- fprintf(outfile, " --- --------- -------- -------- ------\n");
- }
-
- for(i=2; i<=args.num_harm; i++) /* Loop thru each harmonic */
- {
- if(x[i].re == 0)
- {
- db = -999;
- bits = 999;
- }
- else
- {
- db = 20*log10(x[i].re); /* Convert to dB */
- bits = -db/6.0206; /* Calculate number of bits */
- }
-
- if(args.exp_flag) /* Check for /e switch */
- {
- if(!args.sup_flag)
- printf(" %2d %.4e %8.3f %8.3f %6.3f\n", i, x[i].re, x[i].im, db, bits);
- if(args.out_flag)
- fprintf(outfile, " %2d %.4e %8.3f %8.3f %6.3f\n", i, x[i].re, x[i].im, db, bits);
- }
- else
- {
- if(!args.sup_flag)
- printf(" %2d %9.7f %8.3f %8.3f %6.3f\n", i, x[i].re, x[i].im, db, bits);
- if(args.out_flag)
- fprintf(outfile, " %2d %9.7f %8.3f %8.3f %6.3f\n", i, x[i].re, x[i].im, db, bits);
- }
-
- sum += x[i].re*x[i].re; /* Keep a running total */
- }
-
- sum = sqrt(sum); /* Calculate THD */
- if(sum == 0)
- {
- bits = 99;
- }
- else
- {
- db = 20*log10(sum); /* Convert to dB */
- bits = -db/6.0206; /* Calculate number of bits */
- }
-
- if(!args.sup_flag)
- printf(" ----------------------------------------------\n");
- if(args.out_flag)
- fprintf(outfile, " ----------------------------------------------\n");
-
- if(args.exp_flag)
- {
- if(!args.sup_flag)
- printf(" THD = %.4e%% or %6.3f bits\n\n", 100*sum, bits);
- if(args.out_flag)
- fprintf(outfile, " THD = %.4e%% or %6.3f bits\n\n\n", 100*sum, bits);
- }
- else
- {
- if(!args.sup_flag)
- printf(" THD = %7.4f%% or %6.3f bits\n\n", 100*sum, bits);
- if(args.out_flag)
- fprintf(outfile, " THD = %7.4f%% or %6.3f bits\n\n\n", 100*sum, bits);
- }
-
- if(args.out_flag) fclose(outfile);
-
- return;
- }
-
- /* -------------------------------------------------------------------- */
- /* */
- /* >>>>>>>>>>>>>>>>> Decimation in Time FFT Algorithm <<<<<<<<<<<<<<<<< */
- /* */
- /* The following subroutine is taken from Digital Signal Processing by */
- /* A. Oppenheim and R. Schafer, p.331. The original FORTRAN code was */
- /* translated into C by Carl Moreland. Since FORTRAN arrays are indexed */
- /* from 1 to N several modifications were made to accomodate C's 0 to */
- /* N-1 index. Usage: */
- /* */
- /* void fft(N, x) */
- /* */
- /* integer N = # of samples */
- /* complex x is a pointer to an array of complex of size N, where */
- /* complex is defined as a structure of double re, double im. */
- /* */
- /* -------------------------------------------------------------------- */
-
- void fft(int N, complex *x)
- {
- register int i, j, k;
- int M, LE, LE1, ip, fund=1, num;
- complex u, w, t, tm;
- double PI = 3.14159265358;
-
- M = log((double)N)/log(2.0); /* Convert numpts to log base 2 */
- j = -N/2; /* Initial value of j for bit reversal */
-
- for(i=0; i<N; i++)
- {
-
- k = N/2; /* The next few lines calculate the */
- /* proper j value for bit reversing */
- while(k<=j) /* x[i] and x[j]. Some modifications */
- { /* were made here to deal with C's */
- j = j - k; /* array indices such as the initial */
- k = k/2; /* j value above. A Polish way do it */
- } /* but it works... */
- j = j + k;
-
-
- if(i<j) /* If the time is right swap x[i] and */
- { /* x[j]. The complex number t is a tem- */
- t = x[j]; /* porary holder. */
- x[j] = x[i];
- x[i] = t;
- }
- }
-
- for(k=1; k<=M; k++) /* Run through M stages of computations */
- {
- LE = pow(2.0,(double)k);
- LE1 = LE/2;
- u = Z1;
- w = complex(cos(PI/(double)LE1), -sin(PI/(double)LE1));
-
- for(j=0; j<LE1; j++) /* Do N/2 butterfly computations */
- {
-
- for(i=j; i<N; i+=LE) /* x[i] is one of the butterfly terms...*/
- {
- ip = i+LE1; /* x[ip] is the other term... */
-
- /* Butterfly algorithm calculates x[i]' and x[ip]' from x[i] */
- /* and x[ip] as follows: */
- /* x[i]' = x[i] + x[ip]*w */
- /* x[ip]' = x[i] - x[ip]*w */
- /* where w = exp(j2cr/LE) = W(LE)^r */
-
- t = x[ip] * u; /* Temporary holder */
- x[ip] = x[i] - t;
- x[i] += t;
- }
-
- u *= w;
- }
- }
-
- Complex.SetArgMode(Z_DEGREES);
-
- x[0].topolar();
-
- for(i=1; i<N/2; i++)
- {
- x[i].topolar(); /* Convert to magnitude & phase */
-
- if(x[i].re > x[fund].re) /* Find the fundamental */
- fund = i;
- }
-
- x[N/2] = x[0]; /* Sort the bins. Use the upper half of */
- /* of the fft array to temporarily */
- for(i=1; i<N/2; i++) /* hold the harmonics. */
- {
- num = fund*i;
- while(num > N/2)
- num = abs(N - num);
- x[N/2+i] = x[num];
- }
-
- x[0] = x[N/2];
- x[1] = x[N/2+1]; /* Now copy the newly sorted harmonics */
- /* back down to the lower half of the */
- for(i=2; i<N/2; i++) /* array and calculate the relative */
- {
- x[i] = x[N/2+i]; /* magnitudes. */
- x[i].re /= x[1].re;
- }
-
- x[0].re /= N;
- x[1].re /= N/2;
- }
-
-