samer@0
|
1 /*
|
samer@0
|
2 * Copyright (c) 2000, Samer Abdallah, King's College London.
|
samer@0
|
3 * All rights reserved.
|
samer@0
|
4 *
|
samer@0
|
5 * This software is provided AS iS and WITHOUT ANY WARRANTY;
|
samer@0
|
6 * without even the implied warranty of MERCHANTABILITY or
|
samer@0
|
7 * FITNESS FOR A PARTICULAR PURPOSE.
|
samer@0
|
8 */
|
samer@0
|
9
|
samer@0
|
10 package samer.units;
|
samer@0
|
11 import samer.maths.*; // uses ComplexVector and Vec
|
samer@0
|
12
|
samer@0
|
13 /**
|
samer@0
|
14 * <h2>Fast Fourier Transform</h2>
|
samer@0
|
15 * <h4>by Samer Abdallah</h4>
|
samer@0
|
16 * <h4>15 December 1999</h4>
|
samer@0
|
17 *
|
samer@0
|
18 * <p>
|
samer@0
|
19 * Mostly from my old C++ version, though I should credit the following
|
samer@0
|
20 * FFT code for the idea of using a lookup table for the bit reversal
|
samer@0
|
21 * and also a complete lookup table of sines:
|
samer@0
|
22 *
|
samer@0
|
23 * <pre>
|
samer@0
|
24 * > From the Unix Version 2.4 by Steve Sampson, Public Domain,
|
samer@0
|
25 * > September 1988.
|
samer@0
|
26 * > Adapted for Java by Ben Stoltz <stoltz@sun.com>, September 1997
|
samer@0
|
27 * >
|
samer@0
|
28 * > Refer to http://www.neato.org/~ben/Fft.html for updates and related
|
samer@0
|
29 * > resources.
|
samer@0
|
30 * </pre>
|
samer@0
|
31 *
|
samer@0
|
32 * <p>
|
samer@0
|
33 * My old version computed the bit-reversed indices on the fly - silly
|
samer@0
|
34 * really - don't know why I didn't think of a lookup table myself.
|
samer@0
|
35 * Also, it had a lookup table of sines and cosines of the form
|
samer@0
|
36 * sin( 2*PI/k) with k going 1, 2, 4, 8 .. N. This version has complete
|
samer@0
|
37 * tables for sin and cosine - which is a bit of a waste of memory,
|
samer@0
|
38 * but frankly, who cares?
|
samer@0
|
39 *
|
samer@0
|
40 * <h3>Usage</h3>
|
samer@0
|
41 * <p>
|
samer@0
|
42 * It is the user's responsibility to fill the arrays real
|
samer@0
|
43 * and imag with data IN THE CORRECT BITREVERSED ORDER. This
|
samer@0
|
44 * is easy enough using the bitrev lookup table - something like this
|
samer@0
|
45 * would do the trick:
|
samer@0
|
46 * <pre>
|
samer@0
|
47 * for (int i=0; i<N; i++) {
|
samer@0
|
48 * int k=fft.bitrev[i]
|
samer@0
|
49 * fft.real[k] = theRealData[i];
|
samer@0
|
50 * fft.imag[k] = 0;
|
samer@0
|
51 * }
|
samer@0
|
52 * </pre>
|
samer@0
|
53 * The arrays are used destructively, so it is also the user's
|
samer@0
|
54 * responsibility, eg, to reset the imaginary parts to zero even
|
samer@0
|
55 * if they are always zero.
|
samer@0
|
56 *
|
samer@0
|
57 * <p>
|
samer@0
|
58 * The results ends up in the arrays real and imag
|
samer@0
|
59 */
|
samer@0
|
60
|
samer@0
|
61
|
samer@0
|
62 public class FFT
|
samer@0
|
63 {
|
samer@0
|
64 public int bitrev[]; // lookup table
|
samer@0
|
65 public double H[]; // window - not yet used internally
|
samer@0
|
66 public double real[];
|
samer@0
|
67 public double imag[];
|
samer@0
|
68
|
samer@0
|
69 protected ComplexVector W; // sines and cosines lookup table
|
samer@0
|
70 protected int N;
|
samer@0
|
71 protected double zeros[];
|
samer@0
|
72
|
samer@0
|
73 public FFT( int n)
|
samer@0
|
74 {
|
samer@0
|
75 N=n;
|
samer@0
|
76 real = new double[N];
|
samer@0
|
77 imag = new double[N];
|
samer@0
|
78 W = new ComplexVector(N);
|
samer@0
|
79 bitrev = new int[N];
|
samer@0
|
80 H = new double[N];
|
samer@0
|
81
|
samer@0
|
82 // calculate bit reversal lookup table
|
samer@0
|
83 int m=N/2, j=0;
|
samer@0
|
84 for (int i=0; i<n; i++) {
|
samer@0
|
85 bitrev[i]=j;
|
samer@0
|
86 int b;
|
samer@0
|
87 for (b=m; (b>1) && (j>=b); b>>=1) j-=b;
|
samer@0
|
88 j+=b;
|
samer@0
|
89 }
|
samer@0
|
90
|
samer@0
|
91 // calculate sines and cosines lookup
|
samer@0
|
92 double th=2*Math.PI/N;
|
samer@0
|
93 for (int i=0; i<n; i++) {
|
samer@0
|
94 W.real[i] = Math.cos(i*th);
|
samer@0
|
95 W.imag[i] = Math.sin(i*th);
|
samer@0
|
96 }
|
samer@0
|
97
|
samer@0
|
98 // table of zeros for fast initialisation
|
samer@0
|
99 zeros = new double[N];
|
samer@0
|
100 for (int i=0; i<N; i++) zeros[i]=0.0;
|
samer@0
|
101 }
|
samer@0
|
102
|
samer@0
|
103 public int size() { return N; }
|
samer@0
|
104 public final void input( double Y[])
|
samer@0
|
105 {
|
samer@0
|
106 System.arraycopy(zeros,0,imag,0,N);
|
samer@0
|
107 for (int i=0; i<N; i++) {
|
samer@0
|
108 real[bitrev[i]] = H[i]*Y[i];
|
samer@0
|
109 }
|
samer@0
|
110 }
|
samer@0
|
111
|
samer@0
|
112 public final void brevcopy( double src[], double dst[] )
|
samer@0
|
113 {
|
samer@0
|
114 for (int i=0; i<src.length; i++) {
|
samer@0
|
115 dst[bitrev[i]] = src[i];
|
samer@0
|
116 }
|
samer@0
|
117 }
|
samer@0
|
118
|
samer@0
|
119 public final void input( Vec.Iterator it)
|
samer@0
|
120 {
|
samer@0
|
121 System.arraycopy(zeros,0,imag,0,N);
|
samer@0
|
122 for (int i=0; i<N; i++) {
|
samer@0
|
123 real[bitrev[i]] = H[i]*it.next();
|
samer@0
|
124 }
|
samer@0
|
125 }
|
samer@0
|
126
|
samer@0
|
127 public final void outputPower( double S[])
|
samer@0
|
128 {
|
samer@0
|
129 // extract power
|
samer@0
|
130 for (int i=0; i<S.length; i++) {
|
samer@0
|
131 S[i] = real[i]*real[i] + imag[i]*imag[i];
|
samer@0
|
132 }
|
samer@0
|
133 }
|
samer@0
|
134
|
samer@0
|
135 public final void calculate()
|
samer@0
|
136 {
|
samer@0
|
137 double a1,a2,b1,b2;
|
samer@0
|
138 int step, jump, i, k;
|
samer@0
|
139 int m=N/2, p;
|
samer@0
|
140 double wReal, wImag;
|
samer@0
|
141
|
samer@0
|
142 // these are cached for performance -
|
samer@0
|
143 // the COMPILER ought to be doing this!
|
samer@0
|
144 double Real[] = real;
|
samer@0
|
145 double Imag[] = imag;
|
samer@0
|
146 double wr[] = W.real;
|
samer@0
|
147 double wi[] = W.imag;
|
samer@0
|
148
|
samer@0
|
149 step=1; jump=2;
|
samer@0
|
150 while (step<N) {
|
samer@0
|
151
|
samer@0
|
152 p = 0;
|
samer@0
|
153 for (k=0; k<step; k++) {
|
samer@0
|
154
|
samer@0
|
155 // look up exp( (2*PI*i/N)*p );
|
samer@0
|
156 wReal = wr[p];
|
samer@0
|
157 wImag = wi[p];
|
samer@0
|
158 p+=m;
|
samer@0
|
159
|
samer@0
|
160 for (i=k; i<N; i+=jump) {
|
samer@0
|
161
|
samer@0
|
162 int j=i+step;
|
samer@0
|
163
|
samer@0
|
164 // complex butterfly between i and j
|
samer@0
|
165 a1 = Real[i];
|
samer@0
|
166 a2 = Imag[i];
|
samer@0
|
167
|
samer@0
|
168
|
samer@0
|
169 b1 = wReal*Real[j] - wImag*Imag[j];
|
samer@0
|
170 b2 = wImag*Real[j] + wReal*Imag[j];
|
samer@0
|
171
|
samer@0
|
172 Real[i] = a1 + b1;
|
samer@0
|
173 Imag[i] = a2 + b2;
|
samer@0
|
174 Real[j] = a1 - b1;
|
samer@0
|
175 Imag[j] = a2 - b2;
|
samer@0
|
176 }
|
samer@0
|
177 }
|
samer@0
|
178 step=jump; jump<<=1; m>>=1;
|
samer@0
|
179 }
|
samer@0
|
180 }
|
samer@0
|
181
|
samer@0
|
182 // make everything conjugate - does inverse fft
|
samer@0
|
183 public void invert() {
|
samer@0
|
184 for (int i=0; i<W.length; i++) W.imag[i] = -W.imag[i];
|
samer@0
|
185 }
|
samer@0
|
186
|
samer@0
|
187 // useful window functions
|
samer@0
|
188 public void setWindow(Function fn) {
|
samer@0
|
189 for (int i=0; i<N; i++) H[i] = (double)i/N;
|
samer@0
|
190 fn.apply(H); Mathx.mul(H,1/Math.sqrt(N));
|
samer@0
|
191 }
|
samer@0
|
192 }
|