/Users/lyon/j4p/src/math/transforms/FFT1d.java
|
1 /***************************************************************************
2 // @(#)vsFFT1D.java
3 //
4 // Perform a non recursive in place FFT using doubles.
5 //
6 // @version 1.0, 4 JULY 1997
7 // @author D. Lyon, V. Silva
8 ****************************************************************************/
9 package math.transforms;
10
11 import math.Mat1;
12 import math.MathUtils;
13 import sound.OscopePanel;
14
15 import javax.swing.JTabbedPane;
16 import java.awt.BorderLayout;
17 import java.awt.Container;
18
19 /**
20 * A 1D FFT for integral power of two FFTs
21 */
22 public class FFT1d {
23 private final int FORWARD_FFT = -1;
24 private final int REVERSE_FFT = 1;
25 private int direction = FORWARD_FFT;
26 private static final float twoPI = (float) (2 * Math.PI);
27 // These arrays hold the complex 1D FFT data.
28 private double realData[] = null;
29 private double imaginaryData[] = null;
30
31 /**
32 * A way to visually test the 1D FFT on a small amount of data.
33 */
34 public static void showSpectrumAnalyzer() {
35 sound.Oscillator o = new sound.Oscillator(440, 1024);
36 javax.swing.JTabbedPane jtp = new JTabbedPane();
37 addPanel(jtp, "getSquareWave", o.getSquareWave());
38 addPanel(jtp, "getSawWave", o.getSawWave());
39 addPanel(jtp, "getSineWave", o.getSineWave());
40 addPanel(jtp, "getTriangleWave", o.getTriangleWave());
41 gui.ClosableJFrame cf = new gui.ClosableJFrame("psd test");
42 Container c = cf.getContentPane();
43 c.setLayout(new BorderLayout());
44 c.add(jtp, BorderLayout.CENTER);
45 cf.setSize(400, 400);
46 cf.show();
47 }
48
49 private static void addPanel(JTabbedPane jtp, String title, double d[]) {
50 jtp.add(title, getSpectrumPanel(d, title));
51 }
52
53 public static OscopePanel getSpectrumPanel(double audioWaveForm[],
54 String title) {
55 return new OscopePanel(getPsd(audioWaveForm));
56 }
57
58 public static double[] getPsd(double audioWaveForm[]) {
59 double i[] = new double[audioWaveForm.length];
60 FFT1d f = new FFT1d();
61 f.computeForwardFFT(audioWaveForm, i);
62 double[] magnitudeSpectrum = f.getMagnitudeSpectrum();
63 return magnitudeSpectrum;
64 }
65
66 public void visuallyTest() {
67 System.out.println("Testing FFT engine in 1D class...");
68 int N = 8;
69 double[] in_r = new double[N];
70 double[] in_i = new double[N];
71 double[] holder_r = new double[N];
72 double[] holder_i = new double[N];
73
74 // Create a test ramp signal.
75 for (int i = 0; i < N; i++) {
76 in_r[i] = (double) i / N;
77 in_i[i] = (double) 0;
78 }
79 computeForwardFFT(in_r, in_i);
80
81 // Copy to new array because IFFT will destroy the FFT results.
82 for (int i = 0; i < N; i++) {
83 holder_r[i] = in_r[i];
84 holder_i[i] = in_i[i];
85 }
86 for (int i = 0; i < N; i++) {
87 in_r[i] = in_r[i];
88 in_i[i] = in_i[i];
89 }
90 computeBackwardFFT(in_r, in_i);
91 double[] mag = getMagnitudeSpectrum(in_r, in_i);
92 System.out.println("i x1[i] re[i] im[i] tv[i]");
93 for (int i = 0; i < N; i++) {
94 System.out.println(i +
95 "\t" + i +
96 "\t" + holder_r[i] +
97 "\t\t" +
98 holder_i[i] +
99 "\t\t" + mag[i]);
100 }
101 }
102
103 public void timeFFT(int n) {
104 // Time a 256K FFT
105 double realPart[] = new double[n];
106 double imaginaryPart[] = new double[n];
107
108 // Create a test ramp signal.
109
110 synthesizeRamp(realPart, imaginaryPart);
111 //MathUtils.print(realPart);
112
113 utils.Timer t1 = new utils.Timer();
114 runExperiment(t1, realPart, imaginaryPart);
115 }
116
117 public static void print(Object o[]) {
118 for (int i = 0; i < o.length; i++)
119 System.out.println(o[i]);
120 }
121
122 /**
123 * Destroy the input data with a linear ramp.
124 *
125 * @param realPart input datas real component
126 * @param imaginaryPart input datas' imaginary component.
127 */
128 public static final void synthesizeRamp(double[] realPart, double[] imaginaryPart) {
129 int n = realPart.length;
130 for (int i = 0; i < n; i++) {
131 realPart[i] = (double) i / n;
132 imaginaryPart[i] = (double) 0;
133 }
134 }
135
136 private void runExperiment(utils.Timer t1,
137 double[] realPart,
138 double[] imaginaryPart) {
139 t1.start();
140 //double w[] = MathUtils.getGauss(realPart.length);
141
142
143 //window(realPart,w);
144 computeForwardFFT(realPart, imaginaryPart);
145 //getMaxPSDLocation(realPart, imaginaryPart);
146 //System.out.println("....real Part...");
147 //MathUtils.print(realPart);
148 //System.out.println("---- Imaginary Part");
149 //MathUtils.print(imaginaryPart);
150 //System.out.println("ifft");
151 //direction = REVERSE_FFT;
152 //MathUtils.print(realPart);
153
154 // Stop the timer and report.
155 t1.stop();
156 t1.print("did an fft-radix 2 of length" + realPart.length +
157 "using double in:");
158 }
159
160 private static final void window(double r[], double w[]) {
161 for (int i = 0; i < r.length; i++)
162 r[i] = r[i] * w[i];
163 }
164
165 /**
166 * 1D FFT utility functions.
167 */
168 public void swap(int i, int numBits) {
169 double tempr;
170 int j = bitr(i, numBits);
171 tempr = realData[j];
172 realData[j] = realData[i];
173 realData[i] = tempr;
174 tempr = imaginaryData[j];
175 imaginaryData[j] = imaginaryData[i];
176 imaginaryData[i] = tempr;
177 }
178
179 // swap Zi with Zj
180 private void swapInt(int i, int j) {
181 double tempr;
182 int ti;
183 int tj;
184 ti = i - 1;
185 tj = j - 1;
186 // System.out.print("swapInt " + ti+","+tj+"\t");
187 // System.out.println(Integer.toBinaryString(ti)+","+Integer.toBinaryString(tj));
188 // System.out.println(Integer.toBinaryString(ti)+"bitr="+Integer.toBinaryString(bitr(ti)));
189 tempr = realData[tj];
190 realData[tj] = realData[ti];
191 realData[ti] = tempr;
192 tempr = imaginaryData[tj];
193 imaginaryData[tj] = imaginaryData[ti];
194 imaginaryData[ti] = tempr;
195 }
196
197 /**
198 * get the location of the maximum partial
199 *
200 * @param realPart
201 * @param imaginaryPart
202 * @return location of max(realPart[i],imaginaryPart[i])
203 */
204 public static final int getMaxPSDLocation(double realPart[],
205 double imaginaryPart[]) {
206 double max;
207 max = -0.99e30;
208 int location = 0;
209 for (int i = 0; i < realPart.length; i++)
210 if (getMagnitude(realPart[i], imaginaryPart[i]) > max) {
211 max = realPart[i];
212 location = i;
213 }
214 return location;
215 }
216
217 /**
218 * Compute the sum of the squares of a complex number
219 *
220 * @param r real part
221 * @param imag imaginary part
222 * @return r*r + imag * imag.
223 */
224 public static final double getMagnitude(double r, double imag) {
225 return r * r + imag * imag;
226 }
227
228 double getMaxValue(double in[]) {
229 double max;
230 max = -0.99e30;
231 for (int i = 0; i < in.length; i++)
232 if (in[i] > max)
233 max = in[i];
234 return max;
235 }
236
237 private void bitReverse2() {
238 /* bit reversal */
239 int n = realData.length;
240 int j = 1;
241 int k;
242 for (int i = 1; i < n; i++) {
243 if (i < j) {
244 swapInt(i, j);
245 } //if
246 k = n / 2;
247 while (k >= 1 && k < j) {
248 j = j - k;
249 k = k / 2;
250 }
251 j = j + k;
252 } // for
253 }
254
255 private int bitr(int j, int numBits) {
256 int ans = 0;
257 for (int i = 0; i < numBits; i++) {
258 ans = (ans << 1) + (j & 1);
259 j = j >> 1;
260 }
261 return ans;
262 }
263
264 public void computeForwardFFT(double[] in_r, double[] in_i) {
265 direction = FORWARD_FFT;
266 fft(in_r, in_i);
267 }
268
269 public void computeBackwardFFT(double[] in_r, double[] in_i) {
270 direction = REVERSE_FFT;
271 fft(in_r, in_i);
272 }
273
274 /**
275 * FFT engine.
276 */
277 public void fft(double _realPart[], double _imaginaryPart[]) {
278 int id;
279 // radix 2 number if sample, 1D of course.
280 int localN;
281 double wtemp, Wjk_r, Wjk_i, Wj_r, Wj_i;
282 double theta, tempr, tempi;
283 int numBits = MathUtils.getLogBase2(_realPart.length);
284 // Truncate input data to a power of two
285 int length = 1 << numBits; // length = 2**nu
286 int n = length;
287
288 // Copy passed references to variables to be used within
289 // transforms.fft routines & utilities
290 realData = _realPart;
291 imaginaryData = _imaginaryPart;
292 bitReverse2();
293 for (int m = 1; m <= numBits; m++) {
294 // localN = 2^m;
295 localN = 1 << m;
296 Wjk_r = 1;
297 Wjk_i = 0;
298 theta = twoPI / localN;
299 Wj_r = Math.cos(theta);
300 Wj_i = direction * Math.sin(theta);
301 int nby2 = localN / 2;
302 for (int j = 0; j < nby2; j++) {
303 // This is the FFT innermost loop
304 // Any optimizations that can be made here will yield
305 // great rewards.
306 for (int k = j; k < n; k += localN) {
307 id = k + nby2;
308 tempr = Wjk_r * realData[id] - Wjk_i * imaginaryData[id];
309 tempi = Wjk_r * imaginaryData[id] + Wjk_i * realData[id];
310
311 // Zid = Zi -C
312 realData[id] = realData[k] - tempr;
313 imaginaryData[id] = imaginaryData[k] - tempi;
314 realData[k] += tempr;
315 imaginaryData[k] += tempi;
316 }
317
318 // (eq 6.23) and (eq 6.24)
319
320 wtemp = Wjk_r;
321 Wjk_r = Wj_r * Wjk_r - Wj_i * Wjk_i;
322 Wjk_i = Wj_r * Wjk_i + Wj_i * wtemp;
323 }
324 }
325 }
326
327 /**
328 * @return gets the real component from FFT.
329 */
330 public double[] getRealData() {
331 return realData;
332 }
333
334 /**
335 * @return gets the imaginary component from FFT.
336 */
337 public double[] getImaginaryData() {
338 return imaginaryData;
339 }
340
341 public double[] getMagnitudeSpectrum() {
342 double magnitudeSpectrum[] =
343 getMagnitudeSpectrum(realData, imaginaryData);
344 Mat1.normalize(magnitudeSpectrum);
345 return magnitudeSpectrum;
346 }
347
348 /**
349 * Compute the power spectral density of the input
350 * arrays
351 *
352 * @param in_r real part of an fft
353 * @param in_i imaginary part of an fft
354 * @return the psd.
355 */
356 public double[] getMagnitudeSpectrum(double in_r[], double in_i[]) {
357 int N = in_r.length;
358 double[] mag = new double[N];
359 for (int i = 0; i < in_r.length; i++)
360 mag[i] = Math.sqrt(in_r[i] * in_r[i] + in_i[i] * in_i[i]);
361 return (mag);
362 }
363
364 public static void testFFT() {
365 FFT1d f = new FFT1d();
366 //f.visuallyTest();
367 for (int i = 0; i < 10; i++)
368 f.timeFFT(1024 * 1024);
369 }
370
371 public static void main(String args[]) {
372 //showSpectrumAnalyzer();
373 testFFT();
374 }
375 }
376