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