home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / FFT_PROC.ZIP / FFT-PROC.PAS
Encoding:
Pascal/Delphi Source File  |  1985-01-15  |  11.4 KB  |  371 lines

  1. Program FOURIER;
  2. { Real FFT with single sine look-up per pass }
  3. {      This is an almost exact copy of the FFT given in "Introduction to Pascal
  4.   for Scientists" by James W. Cooper, John Wiley & Sons, 1981.  The differences
  5.   are that the Procedure POST has been changed  to fix an error in the
  6.   calculation of the DC term and a factor of 2 error in the other terms.
  7.        The results now agree with another FFT I have which uses a different
  8.   algorithm and is written in FORTRAN (FILE-FFT).  The program is
  9.   composed of an actual FFT (Procedure FFT) which finds the complex transform
  10.   of data in the order of ascending real followed by ascending imaginary.
  11.   Shuffl is used in front of FFT to convert data in the form of real(0),
  12.   imag(0),real(1),imag(1),...,imag(N) to the order required.  The post routine
  13.   needs explaination.  An array of all real data can be transformed using
  14.   a half-sized FFT by treating the all-real data as alternate real,complex and
  15.   doing a final fast and simple conversion with Post.  This technique is
  16.   described in "The Fast Fourier Transform" by E. O. Brigman.  For complex
  17.   input data, the procedure FFT is used alone.  It can be used as a forward
  18.   or inverse transform.  For real input only, use as shown.  The example
  19.   used here is an exponential which is also in the book.
  20.     This FFT seems to work, but I encourage you to check it out since
  21.     algorithms like these can be tricky.
  22.  
  23.                     Roger Coleman, Palm Bay, Fl.}
  24.  
  25. Const
  26.   Asize = 4096;           { Size of array goes here }
  27.   PI2   = 1.570796327;    { PI/2 }
  28.   F     = 16;             { Format constants }
  29.   D     = 6;              {   "      " }
  30.  
  31. Type
  32.   Xform = (Fwd,Inverse);  { Transform types }
  33.   Xary  = Array[1..Asize] of Real;
  34.  
  35. Var
  36.   I,j,N,modulo   : Integer;
  37.   F1    : Text;    {File of Real;}
  38.   X     : Xary;           { Array to transform }
  39.   Inv   : Xform;          { Transform type - Forward or Inverse }
  40.   w     : Real;           { Frequency of wave }
  41.  
  42. {*****************************************************************************}
  43.  
  44. Procedure Debug;          { Used to print intermediate results }
  45.  
  46. Var I3 : Integer;
  47.  
  48. Begin
  49.   For I3 := 1 to N Do
  50.     Writeln((I3-1):3,X[I3]:F:D);
  51. End;    { Debug }
  52.  
  53. {*****************************************************************************}
  54.  
  55. Function Ibitr(j,nu : Integer) : Integer;
  56.   { Function to bit invert the number j by nu bits }
  57.  
  58. Var
  59.   i,j2,ib    : Integer;
  60.  
  61. Begin
  62.   ib := 0;   { Default return integer }
  63.   For i := 1 to nu do
  64.   Begin
  65.     j2 := j Div 2;          { Divide by 2 and compare lowest bits }
  66.     { ib is doubled and bit 0 set to 1 if j is odd }
  67.     ib := ib*2 + (j - 2*j2);
  68.     j := j2;                { For next pass }
  69.   End;  {For}
  70.   ibitr := ib;              { Return bit inverted value }
  71. End;    { Ibitr }
  72.  
  73. {*****************************************************************************}
  74.  
  75. Procedure Expand;
  76.  
  77. Var
  78.   i,nn2,nx2    : Integer;
  79.  
  80. Begin
  81.   nn2 := n div 2;
  82.   nx2 := n + n;
  83.   For i := 1 to nn2 do x[i+n] := x[i+nn2]; { Copy IM to 1st half IM position }
  84.   For i := 2 to nn2 do
  85.   Begin
  86.     x[nx2-i+2] := -x[i+n];    { Replicate 2nd half Imag as negative }
  87.     x[n-i+2] := x[i];         { Replicate 2nd half Real as mirror image }
  88.   End;
  89.   n := nx2;       { We have doubled number of points }
  90. End;
  91.  
  92. {*****************************************************************************}
  93.  
  94. Procedure Post(inv : Xform);
  95.   { Post processing for forward real transforms and pre-processing for inverse
  96.     real transforms, depending on state of the variable inv.
  97.     Note: Lines marked by * are modified from Cooper's orgignal to agree with
  98.           Brigham's algorithm in The Fast Fourier Transform, P.169. Lines
  99.           marked by ** are added for the same reason. }
  100.  
  101. Var
  102.   nn2,nn4,l,i,m,ipn2,mpn2   : Integer;
  103.   arg,rmsin,rmcos,ipcos,ipsin,ic,is1,rp,rm,ip,im   : Real;
  104.  
  105. Begin
  106.   nn2 := n div 2;   { n is global }
  107.   nn4 := n div 4;
  108.   { imax represents PI/2 }
  109.   For l := 1 to nn4 Do
  110.   { Start at ends of array and work towards middle }
  111.   Begin
  112.     i := l+1;       { Point near beginning }
  113.     m := nn2-i+2;   { Point near end }
  114.     ipn2 := i+nn2;  { Avoids recalculation each time }
  115.     mpn2 := m+nn2;  { Calcs ptrs to imaginary part }
  116.     rp := x[i]+x[m];
  117.     rm := x[i]-x[m];
  118.     ip := x[ipn2]+x[mpn2];
  119.     im := x[ipn2]-x[mpn2];
  120.     { Take cosine of PI/2 }
  121.     arg := (Pi2/nn4)*(i-1);
  122.     ic := Cos(arg);
  123.     { Cosine term is minus if inverse }
  124.     If inv = Inverse Then ic := -ic;
  125.     Is1 := Sin(arg);
  126.     ipcos := ip*ic;  { Avoid remultiplication below }
  127.     ipsin := ip*is1;
  128.     rmsin := rm*is1;
  129.     rmcos := rm*ic;
  130.     x[i] := (rp + ipcos - rmsin)/2.0;    {* Combine for real-1st pt }
  131.     x[ipn2] := (im - ipsin - rmcos)/2.0; {* Imag-1st point }
  132.     x[m] := (rp - ipcos + rmsin)/2.0;    {* Real - last pt }
  133.     x[mpn2] := -(im +ipsin +rmcos)/2.0;  {* Imag, last pt }
  134.   End;  { For }
  135.   x[1] := x[1]+x[nn2+1];    {** For first complex pair}
  136.   x[nn2+1] := 0.0;          {**}
  137. End;    { Post }
  138.  
  139. {*****************************************************************************}
  140.  
  141. Procedure Shuffl(inv : Xform);
  142.   { This procedure shuffels points from alternate real-imaginary to
  143.     1st-half real, 2nd-half imaginary order if inv=Fwd, and reverses the
  144.     process if inv=Inverse.  Algorithm is much like Cooley-Tukey:
  145.       Starts with large cells and works to smaller ones for Fwd
  146.       Starts with small cells and increases if inverse }
  147.  
  148. Var
  149.   i,j,k,ipcm1,celdis,celnum,parnum  : Integer;
  150.   xtemp                             : Real;
  151.  
  152. Begin
  153.   { Choose whether to start with large cells and go down or start with small
  154.     cells and increase }
  155.  
  156.   Case inv Of
  157.  
  158.   Fwd: Begin
  159.     celdis := n div 2;     { Distance between cells }
  160.     celnum := 1;           { One cell in first pass }
  161.     parnum := n div 4;     { n/4 pairs per cell in 1st pass }
  162.     End;   { Fwd case }
  163.  
  164.   Inverse: Begin
  165.     celdis := 2;           { Cells are adjacent }
  166.     celnum := n div 4;     { n/4 cells in first pass }
  167.     parnum := 1;
  168.     End;  { Inverse case }
  169.  
  170.   End;  { Case }
  171.  
  172.   Repeat      { Until cells large if Fwd or small if Inverse }
  173.     i := 2;
  174.     For j:= 1 to celnum do
  175.     Begin
  176.       For k := 1 to parnum do  { Do all pairs in each cell }
  177.       Begin
  178.         Xtemp := x[i];
  179.         ipcm1 := i+celdis-1;   { Index of other pts }
  180.         x[i] := x[ipcm1];
  181.         x[ipcm1] := xtemp;
  182.         i := i+2;
  183.       End;  { For k }
  184.  
  185.       { End of cell, advance to next one }
  186.       i := i+celdis;
  187.     End;    { For j }
  188.  
  189.     { Change values for next pass }
  190.  
  191.     Case inv Of
  192.     Fwd:    Begin
  193.       celdis := celdis div 2;         { Decrease cell distance }
  194.       celnum := celnum * 2;           { More cells }
  195.       parnum := parnum div 2;         { Less pairs per cell }
  196.       End;   { Case Fwd }
  197.  
  198.     Inverse: Begin
  199.       celdis := celdis * 2;           { More distance between cells }
  200.       celnum := celnum div 2;         { Less cells }
  201.       parnum := parnum * 2;           { More pairs per cell }
  202.       End;   { Case Inverse }
  203.     End;  { Case }
  204.  
  205.   Until  (((inv = Fwd) And (Celdis < 2)) Or ((inv=Inverse) And (celnum = 0)));
  206.  
  207. End;  { Shuffl }
  208.  
  209. {*****************************************************************************}
  210.  
  211. Procedure FFT(inv : Xform);
  212.   { Fast Fourier transform procedure operating on data in 1st half real,
  213.     2nd half imaginary order and produces a complex result }
  214.  
  215. Var
  216.   n1,n2,nu,celnum,celdis,parnum,ipn2,kpn2,jpn2,
  217.   i,j,k,l,i2,imax,index      : Integer;
  218.   arg,cosy,siny,r2cosy,r2siny,i2cosy,i2siny,picons,
  219.   y,deltay,k1,k2,tr,ti,xtemp  : Real;
  220.  
  221. Begin
  222.   { Calculate nu = log2(n) }
  223.   nu := 0;
  224.   n1 := n div 2;
  225.   n2 := n1;
  226.   While n1 >= 2 Do
  227.   Begin
  228.     nu := nu + 1;            { Increment power-of-2 counter }
  229.     n1 := n1 div 2;          { divide by 2 until zero }
  230.   End;
  231.   { Shuffel the data into bit-inverted order }
  232.   For i := 1 to n2 Do
  233.   Begin
  234.     k := ibitr(i-1,nu)+1;  { Calc bit-inverted position in array }
  235.     If i>k Then            { Prevent swapping twice }
  236.     Begin
  237.       ipn2 := i+n2;
  238.       kpn2 := k+n2;
  239.       tr := x[k];         { Temp storage of real }
  240.       ti := x[kpn2];      { Temp imag }
  241.       x[k] := x[i];
  242.       x[kpn2] := x[ipn2];
  243.       x[i] := tr;
  244.       x[ipn2] := ti;
  245.     End;  { If }
  246.   End;  { For }
  247.  
  248.   { Do first pass specially, since it has no multiplications }
  249.   i := 1;
  250.   While i <= n2 Do
  251.   Begin
  252.     k := i+1;
  253.     kpn2 := k+n2;
  254.     ipn2 := i+n2;
  255.     k1 := x[i]+x[k];        { Save this sum }
  256.     x[k] := x[i]-x[k];      { Diff to k's }
  257.     x[i] := k1;             { Sum to I's }
  258.     k1 := x[ipn2]+x[kpn2];  { Sum of imag }
  259.     x[kpn2] := x[ipn2]-x[kpn2];
  260.     x[ipn2] := k1;
  261.     i := i+2;
  262.   End;  { While }
  263.  
  264.   { Set up deltay for 2nd pass, deltay=PI/2 }
  265.     deltay := PI2;   { PI/2 }
  266.     celnum := n2 div 4;
  267.     parnum := 2;     { Number of pairs between cell }
  268.     celdis := 2;     { Distance between cells }
  269.  
  270.  
  271.   { Each pass after 1st starts here }
  272.   Repeat             { Until number of cells becomes zero }
  273.  
  274. { Writeln(Lst,'After Nth Pass:');  ### }
  275. { Debug; }
  276.  
  277.     { Each new cell starts here }
  278.     index := 1;
  279.     y := 0;      { Exponent of w }
  280.     { Do the number of pairs in each cell }
  281.     For i2 := 1 To parnum Do
  282.     Begin
  283.       If y <> 0 Then
  284.       Begin                { Use sines and cosines if y is not zero }
  285.         cosy := cos(y);    { Calc sine and cosine }
  286.         siny := sin(y);
  287.         { Negate sine terms if transform is inverse }
  288.         If inv = Inverse then siny := -siny;
  289.       End;   { If }
  290.       { These are the fundamental equations of the FFT }
  291.       For l := 0 to celnum-1 Do
  292.       Begin
  293.         i := (celdis*2)*l + index;
  294.         j := i+celdis;
  295.         ipn2 := i + n2;
  296.         jpn2 := j + n2;
  297.         If y = 0 Then   { Special case for y=0 -- No sine or cosine terms }
  298.         Begin
  299.           k1 := x[i]+x[j];
  300.           k2 := x[ipn2]+x[jpn2];
  301.           x[j] := x[i]-x[j];
  302.           x[jpn2] := x[ipn2]-x[jpn2];
  303.         End   { If-Then }
  304.         Else
  305.         Begin
  306.           r2cosy := x[j]*cosy;   { Calc intermediate constants }
  307.           r2siny := x[j]*siny;
  308.           i2cosy := x[jpn2]*cosy;
  309.           i2siny := x[jpn2]*siny;
  310.           { Butterfly }
  311.           k1 := x[i] + r2cosy + i2siny;
  312.           k2 := x[ipn2] - r2siny + i2cosy;
  313.           x[j] := x[i] - r2cosy - i2siny;
  314.           x[jpn2] := x[ipn2] + r2siny - i2cosy;
  315.         End;   { Else }
  316.         { Replace the i terms }
  317.         x[i] := k1;
  318.         x[ipn2] := k2;
  319.  
  320.       { Advance angle for next pair }
  321.       End;  { For l }
  322.  
  323.       Y := y + deltay;
  324.       index := index + 1;
  325.     End;  { For i2 }
  326.  
  327.     { Pass done - change cell distance and number of cells }
  328.     celnum := celnum div 2;
  329.     parnum := parnum * 2;
  330.     celdis := celdis * 2;
  331.     deltay := deltay/2;
  332.  
  333.   Until celnum = 0;
  334.  
  335. End;  { FFT }
  336.  
  337. {*****************************************************************************}
  338.  
  339.  
  340. Begin    { * Main program * }
  341.   For i := 0 To Asize-1 Do
  342.   Begin
  343.     x[i] := 0.0;
  344.   End;
  345.  
  346. {  Write('Enter number of points: ');
  347.   Readln(n);}
  348.   n := 32;
  349.   If (n > Asize) Then
  350.   Begin
  351.     Writeln('Too large, will use maximum');
  352.     n := Round(asize/2.0);
  353.   End;
  354.   For i := 2 to n Do x[i] := Exp((1-i)*0.25); { Create Real array }
  355.   x[1] := 0.5;
  356.  
  357.   Writeln('Input Array:');
  358.   Debug;
  359.  
  360.   Shuffl(Fwd);
  361.  
  362.   FFT(Fwd);
  363.  
  364.   Post(Fwd);
  365.  
  366.   For i:= 1 to n Do x[i] := x[i]*8/n;
  367.   Writeln('Forward FFT with real array first:');
  368.   Debug;
  369.  
  370. End.
  371.