/***************************************************************************
* 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;
}