Metropoli BBS
VIEWER: rfft.c MODE: TEXT (ASCII)
/***************************************************************************
 *		  Copyright (C) 1994  Charles P. Peterson                  *
 *	     4007 Enchanted Sun, San Antonio, Texas 78244-1254             *
 *              Email: Charles_P_Peterson@fcircus.sat.tx.us                *
 *                                                                         *
 *		  This is free software with NO WARRANTY.                  *
 *	      See gfft.c, or run program itself, for details.              *
 *		      Support is available for a fee.                      *
 ***************************************************************************
 *
 * Program:     gfft--General FFT analysis
 * File:        rfft.c
 * Purpose:     fast Fourier analysis function with real input array
 * Author:      Charles Peterson (CPP)
 * History:     12-July-1993 CPP; Created.
 *              8-Feb-95 CPP (1.31); Progress Requester
 * Comment:
 *   Inspired by the algorithm expressed in realft, page 417-418 in
 *   Numerical Recipes in C  (William H. Press, Brian P. Flannery, Saul A.
 *   Teukolsky, William T. Vetterling.  Cambridge University Press, 1988.)
 *   This, however, is an original implementation without any copied code.
 *
 *   To fully appreciate this routine, read cfft.c, and the book, first.
 *   cfft.c is where the transforming is done; here, we just use some
 *   tricks to transform twice as many datapoints in (almost) the same
 *   amount of time, because we are starting with real (not complex) data.
 *   We start by pretending that the odd data points are the imaginary
 *   values for the even data points (0, 2, ...).  Then we use a
 *   symmetry modified version of the Danielson-Lanczos equation to
 *   unfold the data into the positive half of the full transform.
 *
 *   F[k] = 1/2 * (H[k] + conj (H[N/2-k])) - 
 *          i/2 * (H[k] + conj (H[N/2-k])) * e ** (2 * PI * i * k / N)
 *   e ** (i * theta) = cos (theta) + i * sin (theta)
 *
 *   The negative half of the transform is simply related to the positive
 *   half by the equation F[n] = conj (F[-n]) (because h(t) is real).
 *
 *   LEGEND
 *   = here denotes equality
 *   * denotes multiply, ** denotes power (familiar & easy to read; sorry)
 *   cconj is the complex conjugate: conj( {x, y} ) = {x, -y}
 *   i = sqr (-1)
 *   e and PI are the named transcendental numbers
 *   cos and sin are the named trigonometric functions
 *   N is the total number of samples input; k is a number 0..N-1
 *   F is the output complex transform; H is the pseudo-transform returned
 *       by sending cfft the packed samples
 *
 *   Single precision floating point, except for trigonometric values
 *   which are computed in double precision.
 *
 *   The values F[0] and F[N/2] are real and independent.  In order to
 *   pack all data into the original space, Press, et. al., chose to
 *   put the value for F[N/2] into the space for the imaginary part of
 *   F[0].  I didn't like this idea at first, but as the higher level
 *   procedures are written to be general, creating and maintaining an
 *   array 1 unit larger for the 'real' case gets to be quite complicated.
 *   And, it's so nice when input and output arrays are exactly the same
 *   size always.  (I'm breathing easier already.)
 *
 *   (-: CRACKPOT OPINION AND RATIONALIZATION MODE ON
 *
 *   Heck, I shouldn't be programming stuff like this in the psuedo-
 *   assembler known as C.  I want a nice high level OO language with
 *   dynamic arrays and such.  On the other hand, I hear this little voice
 *   saying I'm a fool not to use an assembler--particularly one tailored
 *   to a particular DSP.  I argue critical parts can always be recoded,
 *   and right now I want to get the algorithms and structure correct, and
 *   make this a shining example of readable, understandable, and
 *   learnable--yes, literate--code so that even if people don't use it,
 *   some may learn from it.  Unfortunately, we've not yet been delivered
 *   to the Promised Land of perfect languages, with high and low levels
 *   seemlessly integrated and unified by intuitive concepts and syntax
 *   expanding to infinity.  Hmmn, maybe God didn't want that to happen
 *   yet.  Anyway, right now C is the closest thing to UNIversal, and a
 *   fair compromise language too.  And compromises one has to keep on
 *   making in C as well--they go with the turf.  Dammit.
 *
 *   CRACKPOT OPINION AND RATIONALIZATION MODE OFF :-)
 *
 *   Another catch...the input data is real, and the output is (mostly)
 *   complex.  And, since we don't want to allocate another array and copy
 *   data around, what do we do?  Well, Press, et. al., simply always use
 *   float arrays and use a 'convention' as to how the complex data is
 *   stored therein.  (They did that for four1--the equivalent of my
 *   cfft--as well, which I do consider Blasphemy, since all data there is
 *   complex.)  I use Complex_float arrays there and here as well, so at
 *   least I am correct 3 out of 4 times.  Not only that, but it makes the
 *   code much more traceable to the algorithms, I believe.  (Check it out
 *   for yourself.  I went to a lot of trouble writing cfft.c and rfft.c,
 *   so I hope somebody reads them, even if they don't bother to read the
 *   comments.)  But how do you call this function, then?  Well, you could
 *   allocate a Complex_float data array to begin with, and pack the real
 *   input data into it initially.  Or, you could try declaring rfft()
 *   differently in the caller--using a float data array for i/o.  (This
 *   passed my current SAS 6.0 'ANSI' compiler and linker--without even a
 *   warning--until I put a prototype for realft in complex.h.)  Or, you
 *   could cast the data array/pointer to Complex_float at the point of
 *   call in the caller.  That worked for SAS 6.0 as well, with an
 *   appropriate warning.  I like that approach; I hope they don't change
 *   the compiler to make it not work, and I hope GCC and other compilers
 *   are equally forgiving.  Of course, I take no responsibility for any of
 *   this, or anything.  I know nothing, nuuthing.
 *
 */

#include <math.h>

#ifdef _AMIGA
#ifdef _M68881
#include <m68881.h>
#endif
#endif

#include "complex.h"

void rfft (Complex_float datac[], long n, int isign)
{
/* ARGUMENTS:
 *
 * datac       input/output data array
 * -----       transformed according to isign (see below)
 *
 *             * (For isign = 1) Input array is a real array packed (?) into
 *             * the complex array.  For i = 0  to  n, a hypothetical input
 *             * array dataf[0..n] is packed as follows:
 *             * datac[i/2].real = dataf[i]
 *             * datac[i/2].imaginary = dataf[i+1]
 *             * Or, if you have a reasonable compiler, you might simply
 *             * be able to cast the real (float) array as a the required
 *             * Complex_float array and be done with it.  (Please check
 *             * that it is working properly if you do that.)
 *
 *             * Output array is the positive frequency half of the
 *             * full transform, i.e. F[0]..F[N/2].  It is complex, so
 *             * the declared type Complex_float applies.
 *
 *             *** NOTE: The value for F[0] is real (not complex) and is
 *             *** returned in datac[0].real.  The value for F[N/2] is
 *             *** real (not complex) and is returned in datac[0].imaginary.
 *
 *             * (For isign = -1, the roles of input and output above are
 *             * reversed.)
 *
 *  n          number of real data elements packed in input
 * ---         (2 less than half the number of complex numbers to output)
 *             *** n MUST BE A POWER OF 2  ***
 *             *** THIS IS NOT CHECKED FOR ***
 *
 * isign	if 1, datac is replaced with its discrete Fourier transform
 * -----        if -1, datac is replaced with n times its INVERSE
 *              discrete Fourier transform
 *
 */

    Complex_double w;
    Complex_double wk;
    Complex_double wktemp;
    Complex_float wkfloat;
    double theta;
    long k;

    long halfn = n / 2;
    long halfhalfn = halfn / 2;
    float shalf = 0.5;

    if (n < 2)
    {
	return;  /* Nothing like satisfying the boundary condition.    */
	         /* This is the correct result too (as long as you     */
	         /* i/o only one value: datac[0].real), and avoids a   */
	         /* nasty divide-by-zero and use of uninitialized data */
    }

    theta = PI / (double) (isign * halfn);

    if (isign == 1)
    {
	cfft (datac, halfn, isign);
    }
    else
    {
    /*
     * Inverse transforming is done near end of routine.
     * For now, just reset this one factor which differs between
     * normal and inverse cases
     * (set explicitly to encourage multiply-by-half optimizations).
     */
	shalf = (-0.5);
    }
/*
 * We're skipping k=0 and so we get right down to trigonometric values
 */
    wkfloat.real = (float) (wk.real = w.real = cos (theta));

    wkfloat.imaginary = (float) (wk.imaginary = w.imaginary = sin (theta));

    for (k = 1; k < halfhalfn; k++)
    {
	int n2k = halfn - k;
	Complex_float hk;
	Complex_float hn2k;
    /*
     * First, we peel the two transforms (for k and N/2-k) apart
     */
        hk.real = 0.5 * (datac[k].real + datac[n2k].real);
	hk.imaginary = 0.5 * (datac[k].imaginary - datac[n2k].imaginary);
	hn2k.real = shalf * (datac[k].imaginary + datac[n2k].imaginary);
	hn2k.imaginary = (-shalf) * (datac[k].real - datac[n2k].real);
    /*
     * Then, we recombine them using the modified D/L equation
     */
        datac[k].real = hk.real + (hn2k.real * wkfloat.real) -
	  (hn2k.imaginary * wkfloat.imaginary);
	datac[k].imaginary = hk.imaginary + (hn2k.imaginary * 
					     wkfloat.real) +
	  (hn2k.real * wkfloat.imaginary);

	datac[n2k].real = hk.real - (hn2k.real * wkfloat.real) +
	  (hn2k.imaginary * wkfloat.imaginary);
	datac[n2k].imaginary = (-hk.imaginary) +
	  (hn2k.imaginary * wkfloat.real) + (hn2k.real * wkfloat.imaginary);
    /*
     * Now, compute the next power of e
     */
        C_MULT (wk, w, wktemp);  /* i/o must not overlap */
	wkfloat.real = (float) (wk.real = wktemp.real);
	wkfloat.imaginary = (float) (wk.imaginary = wktemp.imaginary);
    }
/*
 * Now we compute and store the values for 0 and N/2
 */
    if (isign == 1)
    {
	float tempr = datac[0].real;
	datac[0].real += datac[0].imaginary;
	datac[0].imaginary = tempr - datac[0].imaginary;
    }
    else
    {
    /*
     * Now, here's the completion of the inverse part
     * You'll have to take this part on faith (I am)
     */
	float tempr = datac[0].real;
	datac[0].real = 0.5 * (tempr + datac[0].imaginary);
	datac[0].imaginary = 0.5 * (tempr - datac[0].imaginary);

	cfft (datac, halfn, isign);
    }
}

/*
 * The number of inner loops would be N log2 N where N is # of bins
 * However, thanks to the above function, the number is (N/2) log N
 * That's why fft_inner_loops is defined here
 * 
 */

long fft_inner_loops (long number_bins)
{
    long log2;
    long alog;
    long loopcount;

    for (alog = 1, log2 = 0; 
	 alog < number_bins; 
	 log2++, alog *= 2);

    loopcount = (number_bins / 2) * log2;

    return loopcount;
}      
[ RETURN TO DIRECTORY ]