
/*
 By Daniel Klostermann
 Iowa Hills Software, LLC  IowaHills.com
 If you find a problem, please leave a note at:
 http://www.iowahills.com/feedbackcomments.html
 June 6, 2016

 ShowMessage is a C++ Builder function, and it usage has been commented out.
 If you are using C++ Builder, include vcl.h for ShowMessage.
 Otherwise replace ShowMessage with something appropriate for your compiler.
*/

#include "FFTCode.h"
// #include <new.h>   // For the new operator.
#include <math.h>
#include <stdlib.h>

//---------------------------------------------------------------------------
// This calculates the required FFT size for a given number of points.
int RequiredFFTSize(int NumPts)
{
 int N = MINIMUM_FFT_SIZE;
 while(N < NumPts && N < MAXIMUM_FFT_SIZE)
  {
   N *= 2;
  }
 return N;
}

//---------------------------------------------------------------------------

// This verifies that the FFT Size N = 2^M.   M is returned
// N must be >= 8 for the Twiddle calculations
int IsValidFFTSize(int N)
{
 if(N < MINIMUM_FFT_SIZE || N > MAXIMUM_FFT_SIZE || (N & (N - 1)) != 0)return(0);   // N & (N - 1) ensures a power of 2
 return ( (int)( log((double)N) / M_LN2 + 0.5 ) );         // return M where N = 2^M
}

//---------------------------------------------------------------------------

// Fast Fourier Transform
// This code puts DC in bin 0 and scales the output of a forward transform by 1/N.
// InputR and InputI are the real and imaginary input arrays of length N.
// The output values are returned in the Input arrays.
// TTransFormType is either FORWARD or INVERSE (defined in the header file)
// 256 pts in 50 us
void FFT(double *InputR, double *InputI, int N, TTransFormType Type)
{
 int j, LogTwoOfN, *RevBits;
 double *BufferR, *BufferI, *TwiddleR, *TwiddleI;
 double OneOverN;

 // Verify the FFT size and type.
 LogTwoOfN = IsValidFFTSize(N);
 if(LogTwoOfN == 0 || (Type != FORWARD && Type != INVERSE) )
 {
  // ShowMessage("Invalid FFT type or size.");
  return;
 }

/*
 // Memory allocation for all the arrays.
 BufferR  = new(std::nothrow) double[N];
 BufferI  = new(std::nothrow) double[N];
 TwiddleR = new(std::nothrow) double[N/2];
 TwiddleI = new(std::nothrow) double[N/2];
 RevBits  = new(std::nothrow) int[N];
*/

 BufferR = (double *) malloc(sizeof(double) * (N));
 BufferI = (double *) malloc(sizeof(double) * (N));
 TwiddleR = (double *) malloc(sizeof(double) * (N/2));
 TwiddleI = (double *) malloc(sizeof(double) * (N/2));
 RevBits = (int *) malloc(sizeof(int) * (N));

 if(BufferR == NULL  || BufferI == NULL  ||
	TwiddleR == NULL || TwiddleI == NULL || RevBits == NULL)
  {
   // ShowMessage("FFT Memory Allocation Error");
   return;
  }

 ReArrangeInput(InputR, InputI, BufferR, BufferI, RevBits, N);
 FillTwiddleArray(TwiddleR, TwiddleI, N, Type);
 Transform(InputR, InputI, BufferR, BufferI, TwiddleR, TwiddleI, N);


 // The ReArrangeInput function swapped Input[] and Buffer[]. Then Transform()
 // swapped them again, LogTwoOfN times. Ultimately, these swaps must be done
 // an even number of times, or the pointer to Buffer gets returned.
 // So we must do one more swap here, for N = 16, 64, 256, 1024, ...
 OneOverN = 1.0;
 if(Type == FORWARD) OneOverN = 1.0 / (double)N;

 if(LogTwoOfN % 2 == 1)
  {
   for(j=0; j<N; j++) InputR[j] = InputR[j] * OneOverN;
   for(j=0; j<N; j++) InputI[j] = InputI[j] * OneOverN;
  }
 else // if(LogTwoOfN % 2 == 0) then the results are still in Buffer.
  {
   for(j=0; j<N; j++) InputR[j] = BufferR[j] * OneOverN;
   for(j=0; j<N; j++) InputI[j] = BufferI[j] * OneOverN;
  }
/*
 free(BufferR;
 free(BufferI;
 free(TwiddleR;
 free(TwiddleI;
 free(RevBits;
*/
 free(BufferR);
 free(BufferI);
 free(TwiddleR);
 free(TwiddleI);
 free(RevBits);
}
//---------------------------------------------------------------------------

// This puts the input arrays in bit reversed order.
// The while loop generates an array of bit reversed numbers. e.g.
// e.g. N=8: RevBits = 0,4,2,6,1,5,3,7   N=16: RevBits = 0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15
void ReArrangeInput(double *InputR, double *InputI, double *BufferR, double *BufferI, int *RevBits, int N)
{
 int j, k, J, K;

 J = N/2;
 K = 1;
 RevBits[0] = 0;
 while(J >= 1)
  {
   for(k=0; k<K; k++)
	{
	 RevBits[k+K] = RevBits[k] + J;
	}
   K *= 2;
   J /= 2;
  }


 // Move the rearranged input values to Buffer.
 // Take note of the pointer swaps at the top of the transform algorithm.
 for(j=0; j<N; j++)
  {
   BufferR[j] = InputR[ RevBits[j] ];
   BufferI[j] = InputI[ RevBits[j] ];
  }

}

//---------------------------------------------------------------------------

/*
 The Pentium takes a surprising amount of time to calculate the sine and cosine.
 You may want to make the twiddle arrays static if doing repeated FFTs of the same size.
 This uses 4 fold symmetry to calculate the twiddle factors. As a result, this function
 requires a minimum FFT size of 8.
*/
void FillTwiddleArray(double *TwiddleR, double *TwiddleI, int N, TTransFormType Type)
{
 int j;
 double Theta, TwoPiOverN;

 TwoPiOverN = M_2PI / (double) N;

 if(Type == FORWARD)
  {
   TwiddleR[0] = 1.0;
   TwiddleI[0] = 0.0;
   TwiddleR[N/4] = 0.0;
   TwiddleI[N/4] = -1.0;
   TwiddleR[N/8] = M_SQRT_2;
   TwiddleI[N/8] = -M_SQRT_2;
   TwiddleR[3*N/8] = -M_SQRT_2;
   TwiddleI[3*N/8] = -M_SQRT_2;
   for(j=1; j<N/8; j++)
	{
	 Theta = (double)j * -TwoPiOverN;
	 TwiddleR[j] = cos(Theta);
	 TwiddleI[j] = sin(Theta);
	 TwiddleR[N/4-j] = -TwiddleI[j];
	 TwiddleI[N/4-j] = -TwiddleR[j];
	 TwiddleR[N/4+j] = TwiddleI[j];
	 TwiddleI[N/4+j] = -TwiddleR[j];
	 TwiddleR[N/2-j] = -TwiddleR[j];
	 TwiddleI[N/2-j] = TwiddleI[j];
	}
  }

 else
  {
   TwiddleR[0] = 1.0;
   TwiddleI[0] = 0.0;
   TwiddleR[N/4] = 0.0;
   TwiddleI[N/4] = 1.0;
   TwiddleR[N/8] = M_SQRT_2;
   TwiddleI[N/8] = M_SQRT_2;
   TwiddleR[3*N/8] = -M_SQRT_2;
   TwiddleI[3*N/8] = M_SQRT_2;
   for(j=1; j<N/8; j++)
	{
	 Theta = (double)j * TwoPiOverN;
	 TwiddleR[j] = cos(Theta);
	 TwiddleI[j] = sin(Theta);
	 TwiddleR[N/4-j] = TwiddleI[j];
	 TwiddleI[N/4-j] = TwiddleR[j];
	 TwiddleR[N/4+j] = -TwiddleI[j];
	 TwiddleI[N/4+j] = TwiddleR[j];
	 TwiddleR[N/2-j] = -TwiddleR[j];
	 TwiddleI[N/2-j] = TwiddleI[j];
	}
  }

}

//---------------------------------------------------------------------------

// The Fast Fourier Transform.
void Transform(double *InputR, double *InputI, double *BufferR, double *BufferI, double *TwiddleR, double *TwiddleI, int N)
{
 int j, k, J, K, I, T;
 double *TempPointer;
 double TempR, TempI;

 J = N/2;     // J increments down to 1
 K = 1;       // K increments up to N/2
 while(J > 0) // Loops Log2(N) times.
  {
   // Swap pointers, instead doing this: for(j=0; j<N; j++) Input[j] = Buffer[j];
   // We start with a swap because of the swap in ReArrangeInput.
   TempPointer = InputR;
   InputR = BufferR;
   BufferR = TempPointer;
   TempPointer = InputI;
   InputI = BufferI;
   BufferI = TempPointer;

   I = 0;
   for(j=0; j<J; j++)
	{
	 T = 0;
	 for(k=0; k<K; k++) // Loops N/2 times for every value of J and K
	  {
	   TempR = InputR[K+I] * TwiddleR[T] - InputI[K+I] * TwiddleI[T];
	   TempI = InputR[K+I] * TwiddleI[T] + InputI[K+I] * TwiddleR[T];
	   BufferR[I]   = InputR[I] + TempR;
	   BufferI[I]   = InputI[I] + TempI;
	   BufferR[I+K] = InputR[I] - TempR;
	   BufferI[I+K] = InputI[I] - TempI;
	   I++;
	   T += J;
	  }
	 I += K;
	}
   K *= 2;
   J /= 2;
  }

}

//-----------------------------------------------------------------------------------------------

/*
 The only difficulty in writing an FFT is figuring out how to calculate the various array indexes.
 This shows how the index values change when doing a 16 pt FFT.
 This print only has value if you compare it to a butterfly chart. Then you can
 see how an FFT works. Use a 16 point decimation in time butterfly chart. We have one here:
 http://www.iowahills.com/FFTCode.html

 Note: The code above uses real variables. This print out came from code using complex variables as shown here.
 Buffer[I]   = Input[I] + Input[K+I] * Twiddle[T];
 Buffer[I+K] = Input[I] - Input[K+I] * Twiddle[T];

 N = 16
 J = 8    K = 1
 Buffer[0]  = Input[0]  + Input[1]  * Twiddle[0]   I = 0
 Buffer[1]  = Input[0]  - Input[1]  * Twiddle[0]   I = 0
 Buffer[2]  = Input[2]  + Input[3]  * Twiddle[0]   I = 2
 Buffer[3]  = Input[2]  - Input[3]  * Twiddle[0]   I = 2
 Buffer[4]  = Input[4]  + Input[5]  * Twiddle[0]   etc.
 Buffer[5]  = Input[4]  - Input[5]  * Twiddle[0]
 Buffer[6]  = Input[6]  + Input[7]  * Twiddle[0]
 Buffer[7]  = Input[6]  - Input[7]  * Twiddle[0]
 Buffer[8]  = Input[8]  + Input[9]  * Twiddle[0]
 Buffer[9]  = Input[8]  - Input[9]  * Twiddle[0]
 Buffer[10] = Input[10] + Input[11] * Twiddle[0]
 Buffer[11] = Input[10] - Input[11] * Twiddle[0]
 Buffer[12] = Input[12] + Input[13] * Twiddle[0]
 Buffer[13] = Input[12] - Input[13] * Twiddle[0]
 Buffer[14] = Input[14] + Input[15] * Twiddle[0]
 Buffer[15] = Input[14] - Input[15] * Twiddle[0]

 J = 4    K = 2
 Buffer[0]  = Input[0]  + Input[2]  * Twiddle[0]
 Buffer[2]  = Input[0]  - Input[2]  * Twiddle[0]
 Buffer[1]  = Input[1]  + Input[3]  * Twiddle[4]
 Buffer[3]  = Input[1]  - Input[3]  * Twiddle[4]
 Buffer[4]  = Input[4]  + Input[6]  * Twiddle[0]
 Buffer[6]  = Input[4]  - Input[6]  * Twiddle[0]
 Buffer[5]  = Input[5]  + Input[7]  * Twiddle[4]
 Buffer[7]  = Input[5]  - Input[7]  * Twiddle[4]
 Buffer[8]  = Input[8]  + Input[10] * Twiddle[0]
 Buffer[10] = Input[8]  - Input[10] * Twiddle[0]
 Buffer[9]  = Input[9]  + Input[11] * Twiddle[4]
 Buffer[11] = Input[9]  - Input[11] * Twiddle[4]
 Buffer[12] = Input[12] + Input[14] * Twiddle[0]
 Buffer[14] = Input[12] - Input[14] * Twiddle[0]
 Buffer[13] = Input[13] + Input[15] * Twiddle[4]
 Buffer[15] = Input[13] - Input[15] * Twiddle[4]

 J = 2    K = 4
 Buffer[0]  = Input[0]  + Input[4]  * Twiddle[0]
 Buffer[4]  = Input[0]  - Input[4]  * Twiddle[0]
 Buffer[1]  = Input[1]  + Input[5]  * Twiddle[2]
 Buffer[5]  = Input[1]  - Input[5]  * Twiddle[2]
 Buffer[2]  = Input[2]  + Input[6]  * Twiddle[4]
 Buffer[6]  = Input[2]  - Input[6]  * Twiddle[4]
 Buffer[3]  = Input[3]  + Input[7]  * Twiddle[6]
 Buffer[7]  = Input[3]  - Input[7]  * Twiddle[6]
 Buffer[8]  = Input[8]  + Input[12] * Twiddle[0]
 Buffer[12] = Input[8]  - Input[12] * Twiddle[0]
 Buffer[9]  = Input[9]  + Input[13] * Twiddle[2]
 Buffer[13] = Input[9]  - Input[13] * Twiddle[2]
 Buffer[10] = Input[10] + Input[14] * Twiddle[4]
 Buffer[14] = Input[10] - Input[14] * Twiddle[4]
 Buffer[11] = Input[11] + Input[15] * Twiddle[6]
 Buffer[15] = Input[11] - Input[15] * Twiddle[6]

 J = 1    K = 8
 Buffer[0]  = Input[0]  + Input[8]  * Twiddle[0]
 Buffer[8]  = Input[0]  - Input[8]  * Twiddle[0]
 Buffer[1]  = Input[1]  + Input[9]  * Twiddle[1]
 Buffer[9]  = Input[1]  - Input[9]  * Twiddle[1]
 Buffer[2]  = Input[2]  + Input[10] * Twiddle[2]
 Buffer[10] = Input[2]  - Input[10] * Twiddle[2]
 Buffer[3]  = Input[3]  + Input[11] * Twiddle[3]
 Buffer[11] = Input[3]  - Input[11] * Twiddle[3]
 Buffer[4]  = Input[4]  + Input[12] * Twiddle[4]
 Buffer[12] = Input[4]  - Input[12] * Twiddle[4]
 Buffer[5]  = Input[5]  + Input[13] * Twiddle[5]
 Buffer[13] = Input[5]  - Input[13] * Twiddle[5]
 Buffer[6]  = Input[6]  + Input[14] * Twiddle[6]
 Buffer[14] = Input[6]  - Input[14] * Twiddle[6]
 Buffer[7]  = Input[7]  + Input[15] * Twiddle[7]
 Buffer[15] = Input[7]  - Input[15] * Twiddle[7]

*/

//-----------------------------------------------------------------------------------------------


// Discrete Fourier Transform ( textbook code )
// This takes the same arguments as the FFT function.
// 256 pts in 1.720 ms
void DFT(double *InputR, double *InputI, int N, int Type)
{
 int j, k, n;
 double *SumR, *SumI, Sign, Arg;
 double *TwiddleReal, *TwiddleImag;

 // SumR  = new(std::nothrow) double[N];
 // SumI  = new(std::nothrow) double[N];
 SumR = (double *) malloc(sizeof(double) * (N));
 SumI = (double *) malloc(sizeof(double) * (N));
 
 // TwiddleReal = new(std::nothrow) double[N];
 // TwiddleImag = new(std::nothrow) double[N];
 TwiddleReal = (double *) malloc(sizeof(double) * (N));
 TwiddleImag = (double *) malloc(sizeof(double) * (N));

 if(SumR == NULL || SumI == NULL ||
    TwiddleReal == NULL || TwiddleImag == NULL ||
    (Type != FORWARD && Type != INVERSE) )
  {
   // ShowMessage("Incorrect DFT Type or unable to allocate memory");
   return;
  }

 // Calculate the twiddle factors and initialize the Sum arrays.
 if(Type == FORWARD)Sign = -1.0;
 else               Sign =  1.0;

 for(j=0; j<N; j++)
  {
   Arg = M_2PI * (double)j / (double)N;
   TwiddleReal[j] = cos(Arg);
   TwiddleImag[j] = Sign * sin(Arg);
   SumR[j] = SumI[j] = 0.0;
  }


 //Calculate the DFT
 for(j=0; j<N; j++) // Sum index
 for(k=0; k<N; k++) // Input index
  {
   n = (j*k) % N;
   SumR[j] += TwiddleReal[n] * InputR[k] - TwiddleImag[n] * InputI[k];
   SumI[j] += TwiddleReal[n] * InputI[k] + TwiddleImag[n] * InputR[k];
  }

 // Scale the result if doing a forward DFT, and move the result to the input arrays.
 if(Type == FORWARD)
  {
   for(j=0; j<N; j++)InputR[j] = SumR[j] / (double)N;
   for(j=0; j<N; j++)InputI[j] = SumI[j] / (double)N;
  }
 else  // Inverse DFT
  {
   for(j=0; j<N; j++)InputR[j] = SumR[j];
   for(j=0; j<N; j++)InputI[j] = SumI[j];
  }

/* free(SumR;
 free(SumI;
 free(TwiddleReal;
 free(TwiddleImag;
*/
 free(SumR);
 free(SumI);
 free(TwiddleReal);
 free(TwiddleImag);
}

//-----------------------------------------------------------------------------------------------

// This is a DFT for real valued samples. Since Samples is real, it can only do a forward DFT.
// The results are returned in OutputR  OutputI
// 256 pts in 700 us    30 us to calc the twiddles
void RealSigDFT(double *Samples, double *OutputR, double *OutputI, int N)
{
 int j, k;
 double Arg, *TwiddleReal, *TwiddleImag;

 // TwiddleReal = new(std::nothrow) double[N];
 // TwiddleImag = new(std::nothrow) double[N];
 TwiddleReal = (double *) malloc(sizeof(double) * (N));
 TwiddleImag = (double *) malloc(sizeof(double) * (N));
 
 if(TwiddleReal == NULL || TwiddleImag == NULL)
  {
   // ShowMessage("Failed to allocate memory in RealSigDFT");
   return;
  }

 for(j=0; j<N; j++)
  {
   Arg = M_2PI * (double)j / (double)N;
   TwiddleReal[j] = cos(Arg);
   TwiddleImag[j] = -sin(Arg);
  }


 // Compute the DFT.
 // We have a real input, so only do the pos frequencies. i.e. j<N/2
 for(j=0; j<=N/2; j++)
  {
   OutputR[j] = 0.0;
   OutputI[j] = 0.0;
   for(k=0; k<N; k++)
	{
     OutputR[j] += Samples[k] * TwiddleReal[(j*k) % N];
     OutputI[j] += Samples[k] * TwiddleImag[(j*k) % N];
	}

   // Scale the result
   OutputR[j] /= (double)N;
   OutputI[j] /= (double)N;
  }

 // The neg freq components are the conj of the pos components because the input signal is real.
 for(j=1; j<N/2; j++)
  {
   OutputR[N-j] = OutputR[j];
   OutputI[N-j] = -OutputI[j];
  }

 free(TwiddleReal);
 free(TwiddleImag);
}

//---------------------------------------------------------------------------

// This is a single frequency DFT.
// This code uses iteration to calculate the Twiddle factors.
// To evaluate the frequency response of an FIR filter at Omega, set
// Samples[] = FirCoeff[]   N = NumTaps  0.0 <= Omega <= 1.0
// 256 pts in 15.6 us
double SingleFreqDFT(double *Samples, int N, double Omega)
{
 int k;
 double SumR, SumI, zR, zI, TwiddleR, TwiddleI, Temp;

 TwiddleR =  cos(Omega * M_PI);
 TwiddleI = -sin(Omega * M_PI);
 zR = 1.0;    // z, as in e^(j*omega)
 zI = 0.0;
 SumR = 0.0;
 SumI = 0.0;

 for(k=0; k<N; k++)
  {
   SumR += Samples[k] * zR;
   SumI += Samples[k] * zI;

   // Calculate the complex exponential z by taking it to the kth power.
   Temp = zR * TwiddleR - zI * TwiddleI;
   zI =   zR * TwiddleI + zI * TwiddleR;
   zR = Temp;
  }

 /*
 // This is the more conventional implementation of the loop above.
 // It is a bit more accurate, but slower.
 for(k=0; k<N; k++)
  {
   SumR += Samples[k] *  cos((double)k * Omega * M_PI);
   SumI += Samples[k] * -sin((double)k * Omega * M_PI);
  }
 */

 return( sqrt(SumR*SumR + SumI*SumI) );
 // return( ComplexD(SumR, SumI) );// if phase is needed.
}

//---------------------------------------------------------------------------

// Goertzel is essentially a single frequency DFT, but without phase information.
// Its simplicity allows it to run about 3 times faster than a single frequency DFT.
// It is typically used to find a tone embedded in a signal. A DTMF tone for example.
// 256 pts in 6 us
double Goertzel(double *Samples, int N, double Omega)
{
 int j;
 double Reg0, Reg1, Reg2;        // 3 shift registers
 double CosVal, Mag;
 Reg1 = Reg2 = 0.0;

 CosVal = 2.0 * cos(M_PI * Omega );
 for (j=0; j<N; j++)
  {
   Reg0 = Samples[j] + CosVal * Reg1 - Reg2;
   Reg2 = Reg1;  // Shift the values.
   Reg1 = Reg0;
  }
 Mag = Reg2 * Reg2 + Reg1 * Reg1 - CosVal * Reg1 * Reg2;

 if(Mag > 0.0)Mag = sqrt(Mag);
 else Mag = 1.0E-12;

 return(Mag);
}

//---------------------------------------------------------------------------

/*
 These are the window definitions. These windows can be used for either
 FIR filter design or with an FFT for spectral analysis.
 For definitions, see this article:  http://en.wikipedia.org/wiki/Window_function

 This function has 6 inputs
 Data is the array, of length N, containing the data to to be windowed.
 This data is either an FIR filter sinc pulse, or the data to be analyzed by an fft.

 WindowType is an enum defined in the header file.
 e.g. wtKAISER, wtSINC, wtHANNING, wtHAMMING, wtBLACKMAN, ...

 Alpha sets the width of the flat top.
 Windows such as the Tukey and Trapezoid are defined to have a variably wide flat top.
 As can be seen by its definition, the Tukey is just a Hanning window with a flat top.
 Alpha can be used to give any of these windows a partial flat top, except the Flattop and Kaiser.
 Alpha = 0 gives the original window. (i.e. no flat top)
 To generate a Tukey window, use a Hanning with 0 < Alpha < 1
 To generate a Bartlett window (triangular), use a Trapezoid window with Alpha = 0.
 Alpha = 1 generates a rectangular window in all cases. (except the Flattop and Kaiser)


 Beta is used with the Kaiser, Sinc, and Sine windows only.
 These three windows are used primarily for FIR filter design. Then
 Beta controls the filter's transition bandwidth and the sidelobe levels.
 All other windows ignore Beta.

 UnityGain controls whether the gain of these windows is set to unity.
 Only the Flattop window has unity gain by design. The Hanning window, for example, has a gain
 of 1/2.  UnityGain = true  sets the gain to 1, which preserves the signal's energy level
 when these windows are used for spectral analysis.

 Don't use this with FIR filter design however. Since most of the enegy in an FIR sinc pulse
 is in the middle of the window, the window needs a peak amplitude of one, not unity gain.
 Setting UnityGain = true will simply cause the resulting FIR filter to have excess gain.

 If using these windows for FIR filters, start with the Kaiser, Sinc, or Sine windows and
 adjust Beta for the desired transition BW and sidelobe levels (set Alpha = 0).
 While the FlatTop is an excellent window for spectral analysis, don't use it for FIR filter design.
 It has a peak amplitude of ~ 4.7 which causes the resulting FIR filter to have about this much gain.
 It works poorly for FIR filters even if you adjust its peak amplitude.
 The Trapezoid also works poorly for FIR filter design.

 If using these windows with an fft for spectral analysis, start with the Hanning, Gauss, or Flattop.
 When choosing a window for spectral analysis, you must trade off between resolution and amplitude
 accuracy. The Hanning has the best resolution while the Flatop has the best amplitude accuracy.
 The Gauss is midway between these two for both accuracy and resolution. These three were
 the only windows available in the HP 89410A Vector Signal Analyzer. Which is to say, these three
 are the probably the best windows for general purpose signal analysis.
*/

void WindowData(double *Data, int N, TWindowType WindowType, double Alpha, double Beta, bool UnityGain)
{
 if(WindowType == wtNONE) return;

 int j, M, TopWidth;
 double dM, *WinCoeff;

 if(WindowType == wtKAISER ||  WindowType == wtFLATTOP )Alpha = 0.0;

 if(Alpha < 0.0)Alpha = 0.0;
 if(Alpha > 1.0)Alpha = 1.0;

 if(Beta < 0.0)Beta = 0.0;
 if(Beta > 10.0)Beta = 10.0;

//  WinCoeff  = new(std::nothrow) double[N+2];
 WinCoeff = (double *) malloc(sizeof(double) * (N+2));

 if(WinCoeff == NULL)
  {
   // ShowMessage("Failed to allocate memory in WindowData() ");
   return;
  }

 TopWidth = (int)( Alpha * (double)N );
 if(TopWidth%2 != 0)TopWidth++;
 if(TopWidth > N)TopWidth = N;
 M = N - TopWidth;
 dM = M + 1;


 // Calculate the window for N/2 points, then fold the window over (at the bottom).
 // TopWidth points will be set to 1.
 if(WindowType == wtKAISER)
  {
   double Arg;
   for(j=0; j<M; j++)
	{
	 Arg = Beta * sqrt(1.0 - pow( ((double)(2*j+2) - dM) / dM, 2.0) );
	 WinCoeff[j] = Bessel(Arg) / Bessel(Beta);
	}
  }

 else if(WindowType == wtSINC)  // Lanczos
  {
   for(j=0; j<M; j++)WinCoeff[j] = Sinc((double)(2*j+1-M)/dM * M_PI );
   for(j=0; j<M; j++)WinCoeff[j] = pow(WinCoeff[j], Beta);
  }

 else if(WindowType == wtSINE)  // Hanning if Beta = 2
  {
   for(j=0; j<M/2; j++)WinCoeff[j] = sin((double)(j+1) * M_PI / dM);
   for(j=0; j<M/2; j++)WinCoeff[j] = pow(WinCoeff[j], Beta);
  }

 else if(WindowType == wtHANNING)
  {
   for(j=0; j<M/2; j++)WinCoeff[j] = 0.5 - 0.5 * cos((double)(j+1) * M_2PI / dM);
  }

 else if(WindowType == wtHAMMING)
  {
   for(j=0; j<M/2; j++)
   WinCoeff[j] = 0.54 - 0.46 * cos((double)(j+1) * M_2PI / dM);
  }

 else if(WindowType == wtBLACKMAN)
  {
   for(j=0; j<M/2; j++)
	{
	 WinCoeff[j] = 0.42
	 - 0.50 * cos((double)(j+1) * M_2PI / dM)
	 + 0.08 * cos((double)(j+1) * M_2PI * 2.0 / dM);
	}
  }


 // Defined at: http://www.bth.se/fou/forskinfo.nsf/0/130c0940c5e7ffcdc1256f7f0065ac60/$file/ICOTA_2004_ttr_icl_mdh.pdf
 else if(WindowType == wtFLATTOP)
  {
   for(j=0; j<=M/2; j++)
	{
	 WinCoeff[j] = 1.0
	 - 1.93293488969227 * cos((double)(j+1) * M_2PI / dM)
	 + 1.28349769674027 * cos((double)(j+1) * M_2PI * 2.0 / dM)
	 - 0.38130801681619 * cos((double)(j+1) * M_2PI * 3.0 / dM)
	 + 0.02929730258511 * cos((double)(j+1) * M_2PI * 4.0 / dM);
	}
  }


 else if(WindowType == wtBLACKMAN_HARRIS)
  {
   for(j=0; j<M/2; j++)
	{
	 WinCoeff[j] = 0.35875
	 - 0.48829 * cos((double)(j+1) * M_2PI / dM)
	 + 0.14128 * cos((double)(j+1) * M_2PI * 2.0 / dM)
	 - 0.01168 * cos((double)(j+1) * M_2PI * 3.0 / dM);
	}
  }

 else if(WindowType == wtBLACKMAN_NUTTALL)
  {
   for(j=0; j<M/2; j++)
	{
	 WinCoeff[j] = 0.3535819
	 - 0.4891775 * cos((double)(j+1) * M_2PI / dM)
	 + 0.1365995 * cos((double)(j+1) * M_2PI * 2.0 / dM)
	 - 0.0106411 * cos((double)(j+1) * M_2PI * 3.0 / dM);
	}
  }

 else if(WindowType == wtNUTTALL)
  {
   for(j=0; j<M/2; j++)
	{
	 WinCoeff[j] = 0.355768
	 - 0.487396 * cos((double)(j+1) * M_2PI / dM)
	 + 0.144232 * cos((double)(j+1) * M_2PI * 2.0 / dM)
	 - 0.012604 * cos((double)(j+1) * M_2PI * 3.0 / dM);
	}
  }

 else if(WindowType == wtKAISER_BESSEL)
  {
   for(j=0; j<=M/2; j++)
	{
	 WinCoeff[j] = 0.402
	 - 0.498 * cos(M_2PI * (double)(j+1) / dM)
	 + 0.098 * cos(2.0 * M_2PI * (double)(j+1) / dM)
	 + 0.001 * cos(3.0 * M_2PI * (double)(j+1) / dM);
	}
  }

 else if(WindowType == wtTRAPEZOID) // Rectangle for Alpha = 1  Triangle for Alpha = 0
  {
   int K = M/2;
   if(M%2)K++;
   for(j=0; j<K; j++)WinCoeff[j] = (double)(j+1) / (double)K;
  }


 // This definition is from http://en.wikipedia.org/wiki/Window_function (Gauss Generalized normal window)
 // We set their p = 2, and use Alpha in the numerator, instead of Sigma in the denominator, as most others do.
 // Alpha = 2.718 puts the Gauss window response midway between the Hanning and the Flattop (basically what we want).
 // It also gives the same BW as the Gauss window used in the HP 89410A Vector Signal Analyzer.
 else if(WindowType == wtGAUSS)
  {
   for(j=0; j<M/2; j++)
    {
     WinCoeff[j] = ((double)(j+1) - dM/2.0) / (dM/2.0) * 2.7183;
     WinCoeff[j] *= WinCoeff[j];
     WinCoeff[j] = exp(-WinCoeff[j]);
    }
  }

 else // Error.
  {
   // ShowMessage("Incorrect window type in WindowFFTData");
   free(WinCoeff);
   return;
  }

 // Fold the coefficients over.
 for(j=0; j<M/2; j++)WinCoeff[N-j-1] = WinCoeff[j];

 // This is the flat top if Alpha > 0. Cannot be applied to a Kaiser or Flat Top.
 if(WindowType != wtKAISER &&  WindowType != wtFLATTOP)
  {
   for(j=M/2; j<N-M/2; j++)WinCoeff[j] = 1.0;
  }


 // UnityGain = true will set the gain of these windows to 1. Don't use this with FIR filter design.
 if(UnityGain)
  {
   double Sum = 0.0;
   for(j=0; j<N; j++)Sum += WinCoeff[j];
   Sum /= (double)N;
   if(Sum != 0.0)for(j=0; j<N; j++)WinCoeff[j] /= Sum;
  }

 // Apply the window to the data.
 for(j=0; j<N; j++)Data[j] *= WinCoeff[j];

 free(WinCoeff);

}

//---------------------------------------------------------------------------

// This gets used with the Kaiser window.
double Bessel(double x)
{
 double Sum=0.0, XtoIpower;
 int i, j, Factorial;
 for(i=1; i<10; i++)
  {
   XtoIpower = pow(x/2.0, (double)i);
   Factorial = 1;
   for(j=1; j<=i; j++)Factorial *= j;
   Sum += pow(XtoIpower / (double)Factorial, 2.0);
  }
 return(1.0 + Sum);
}

//-----------------------------------------------------------------------------

// This gets used with the Sinc window.
double Sinc(double x)
{
 if(x > -1.0E-5 && x < 1.0E-5)return(1.0);
 return(sin(x)/x);
}

//---------------------------------------------------------------------------



