home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / TURBOPAS / TP / UTL3 / FFT-PROC.PZS / FFT-PROC.PAS
Pascal/Delphi Source File  |  2000-06-30  |  12KB  |  372 lines

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