/Users/lyon/j4p/src/math/transforms/fft/FFT1d.java
|
1 package math.transforms.fft;
2
3 //Title: 1-d mixed radix FFT.
4 //Version:
5 //Copyright: Copyright (c) 1998
6 //Author: Dongyan Wang
7 //Company: University of Wisconsin-Milwaukee.
8 //Description:
9 // The number of DFT is factorized.
10 //
11 // Some short FFTs, such as length 2, 3, 4, 5, 8, 10, are used
12 // to improve the speed.
13 //
14 // Prime factors are processed using DFT. In the future, we can
15 // improve this part.
16 // Note: there is no limit how large the prime factor can be,
17 // because for a set of data of an image, the length can be
18 // random, ie. an image can have size 263 x 300, where 263 is
19 // a large prime factor.
20 //
21 // A permute() function is used to make sure FFT can be calculated
22 // in place.
23 //
24 // A triddle() function is used to perform the FFT.
25 //
26 // This program is for FFT of complex data, if the input is real,
27 // the program can be further improved. Because I want to use the
28 // same program to do IFFT, whose input is often complex, so I
29 // still use this program.
30 //
31 // To save the memory and improve the speed, float data are used
32 // instead of double, but I do have a double version transforms.fft.
33 //
34 // Factorize() is done in constructor, transforms.fft() is needed to be
35 // called to do FFT, this is good for use in fft2d, then
36 // factorize() is not needed for each row/column of data, since
37 // each row/column of a matrix has the same length.
38 //
39
40
41 public class FFT1d extends TempVars {
42 // Maximum numbers of factors allowed.
43 //private static final int MaxFactorsNumber = 30;
44 private static final int MaxFactorsNumber = 37;
45
46 // cos2to3PI = cos(2*pi/3), using for 3 point FFT.
47 // cos(2*PI/3) is not -1.5
48 private static final float cos2to3PI = -1.5000f;
49 // sin2to3PI = sin(2*pi/3), using for 3 point FFT.
50 private static final float sin2to3PI = 8.6602540378444E-01f;
51
52 // TwotoFivePI = 2*pi/5.
53 // c51, c52, c53, c54, c55 are used in fft5().
54 // c51 =(cos(TwotoFivePI)+cos(2*TwotoFivePI))/2-1.
55 private static final float c51 = -1.25f;
56 // c52 =(cos(TwotoFivePI)-cos(2*TwotoFivePI))/2.
57 private static final float c52 = 5.5901699437495E-01f;
58 // c53 = -sin(TwotoFivePI).
59 private static final float c53 = -9.5105651629515E-01f;
60 // c54 =-(sin(TwotoFivePI)+sin(2*TwotoFivePI)).
61 private static final float c54 = -1.5388417685876E+00f;
62 // c55 =(sin(TwotoFivePI)-sin(2*TwotoFivePI)).
63 private static final float c55 = 3.6327126400268E-01f;
64
65 // OnetoSqrt2 = 1/sqrt(2), used in fft8().
66 private static final float OnetoSqrt2 = 7.0710678118655E-01f;
67
68 private static int lastRadix = 0;
69
70 int N; // length of N point FFT.
71 int NumofFactors; // Number of factors of N.
72 static final int maxFactor = 20; // Maximum factor of N.
73
74 int factors[]; // Factors of N processed in the current stage.
75 int sofar[]; // Finished factors before the current stage.
76 int remain[]; // Finished factors after the current stage.
77
78 float inputRe[], inputIm[]; // Input of FFT.
79 float temRe[], temIm[]; // Intermediate result of FFT.
80 float outputRe[], outputIm[]; // Output of FFT.
81 static boolean factorsWerePrinted = false;
82
83 // Constructor: FFT of Complex data.
84 public FFT1d(int N) {
85 this.N = N;
86 outputRe = new float[N];
87 outputIm = new float[N];
88
89 factorize();
90 printFactors();
91
92 // Allocate memory for intermediate result of FFT.
93 temRe = new float[maxFactor];
94 temIm = new float[maxFactor];
95 }
96
97 public void fft(float inputRe[], float inputIm[]) {
98 // First make sure inputRe & inputIm are of the same length.
99 if (inputRe.length != N || inputIm.length != N) {
100 System.out.println("Error: the length of real part & imaginary part " +
101 "of the input to 1-d FFT are different");
102 return;
103 } else {
104 this.inputRe = inputRe;
105 this.inputIm = inputIm;
106
107 permute();
108 //System.out.println("ready to twiddle");
109
110 for (int factorIndex = 0; factorIndex < NumofFactors; factorIndex++)
111 twiddle(factorIndex);
112 //System.out.println("ready to copy");
113
114 // Copy the output[] data to input[], so the output can be
115 // returned in the input array.
116 for (int i = 0; i < N; i++) {
117 inputRe[i] = outputRe[i];
118 inputIm[i] = outputIm[i];
119 }
120
121 }
122 }
123
124 public void printFactors() {
125 if (factorsWerePrinted) return;
126 factorsWerePrinted = true;
127 for (int i = 0; i < factors.length; i++)
128 System.out.println("factors[i] = " + factors[i]);
129 }
130
131 private void factorize() {
132 int radices[] = {2, 3, 4, 5, 8, 10};
133 int temFactors[] = new int[MaxFactorsNumber];
134
135 // 1 - point FFT, no need to factorize N.
136 if (N == 1) {
137 temFactors[0] = 1;
138 NumofFactors = 1;
139 }
140
141 // N - point FFT, N is needed to be factorized.
142 int n = N;
143 int index = 0; // index of temFactors.
144 int i = radices.length - 1;
145
146 while ((n > 1) && (i >= 0)) {
147 if ((n % radices[i]) == 0) {
148 n /= radices[i];
149 temFactors[index++] = radices[i];
150 } else
151 i--;
152 }
153
154 // Substitute 2x8 with 4x4.
155 // index>0, in the case only one prime factor, such as N=263.
156 if ((index > 0) && (temFactors[index - 1] == 2))
157 for (i = index - 2; i >= 0; i--)
158 if (temFactors[i] == 8) {
159 temFactors[index - 1] = temFactors[i] = 4;
160 // break out of for loop, because only one '2' will exist in
161 // temFactors, so only one substitutation is needed.
162 break;
163 }
164
165 if (n > 1) {
166 for (int k = 2; k < Math.sqrt(n) + 1; k++)
167 while ((n % k) == 0) {
168 n /= k;
169 temFactors[index++] = k;
170 }
171 if (n > 1) {
172 temFactors[index++] = n;
173 }
174 }
175 NumofFactors = index;
176 //if(temFactors[NumofFactors-1] > 10)
177 // maxFactor = n;
178 //else
179 // maxFactor = 10;
180
181 // Inverse temFactors and store factors into factors[].
182 factors = new int[NumofFactors];
183 for (i = 0; i < NumofFactors; i++) {
184 factors[i] = temFactors[NumofFactors - i - 1];
185 }
186
187 // Calculate sofar[], remain[].
188 // sofar[] : finished factors before the current stage.
189 // factors[]: factors of N processed in the current stage.
190 // remain[] : finished factors after the current stage.
191 sofar = new int[NumofFactors];
192 remain = new int[NumofFactors];
193
194 remain[0] = N / factors[0];
195 sofar[0] = 1;
196 for (i = 1; i < NumofFactors; i++) {
197 sofar[i] = sofar[i - 1] * factors[i - 1];
198 remain[i] = remain[i - 1] / factors[i];
199 }
200 } // End of function factorize().
201
202 private void permute() {
203 int count[] = new int[MaxFactorsNumber];
204 int j;
205 int k = 0;
206
207 for (int i = 0; i < N - 1; i++) {
208 outputRe[i] = inputRe[k];
209 outputIm[i] = inputIm[k];
210 j = 0;
211 k = k + remain[j];
212 count[0] = count[0] + 1;
213 while (count[j] >= factors[j]) {
214 count[j] = 0;
215 k = k - (j == 0?N:remain[j - 1]) + remain[j + 1];
216 j++;
217 count[j] = count[j] + 1;
218 }
219 }
220 outputRe[N - 1] = inputRe[N - 1];
221 outputIm[N - 1] = inputIm[N - 1];
222 } // End of function permute().
223
224 private void twiddle(int factorIndex) {
225 // Get factor data.
226 int sofarRadix = sofar[factorIndex];
227 int radix = factors[factorIndex];
228 int remainRadix = remain[factorIndex];
229
230 float tem; // Temporary variable to do data exchange.
231
232 float W = 2 * (float) Math.PI / (sofarRadix * radix);
233 float cosW = (float) Math.cos(W);
234 float sinW = -(float) Math.sin(W);
235
236 float twiddleRe[] = new float[radix];
237 float twiddleIm[] = new float[radix];
238 float twRe = 1.0f, twIm = 0f;
239
240 //Initialize twiddle addBk.address variables.
241 int dataOffset = 0, groupOffset = 0, address = 0;
242
243 for (int dataNo = 0; dataNo < sofarRadix; dataNo++) {
244 //System.out.println("datano="+dataNo);
245 if (sofarRadix > 1) {
246 twiddleRe[0] = 1.0f;
247 twiddleIm[0] = 0.0f;
248 twiddleRe[1] = twRe;
249 twiddleIm[1] = twIm;
250 for (int i = 2; i < radix; i++) {
251
252
253 twiddleRe[i] = twRe * twiddleRe[i - 1] - twIm * twiddleIm[i - 1];
254 twiddleIm[i] = twIm * twiddleRe[i - 1] + twRe * twiddleIm[i - 1];
255 }
256 tem = cosW * twRe - sinW * twIm;
257 twIm = sinW * twRe + cosW * twIm;
258 twRe = tem;
259 }
260 for (int groupNo = 0; groupNo < remainRadix; groupNo++) {
261 //System.out.println("groupNo="+groupNo);
262 if ((sofarRadix > 1) && (dataNo > 0)) {
263 temRe[0] = outputRe[address];
264 temIm[0] = outputIm[address];
265 int blockIndex = 1;
266 do {
267 address = address + sofarRadix;
268 temRe[blockIndex] = twiddleRe[blockIndex] * outputRe[address] -
269 twiddleIm[blockIndex] * outputIm[address];
270 temIm[blockIndex] = twiddleRe[blockIndex] * outputIm[address] +
271 twiddleIm[blockIndex] * outputRe[address];
272 blockIndex++;
273 } while (blockIndex < radix);
274 } else
275 for (int i = 0; i < radix; i++) {
276 //System.out.println("temRe.length="+temRe.length);
277 //System.out.println("i = "+i);
278 temRe[i] = outputRe[address];
279 temIm[i] = outputIm[address];
280 address += sofarRadix;
281 }
282 //System.out.println("radix="+radix);
283 switch (radix) {
284
285
286 case 2:
287 tem = temRe[0] + temRe[1];
288 temRe[1] = temRe[0] - temRe[1];
289 temRe[0] = tem;
290 tem = temIm[0] + temIm[1];
291 temIm[1] = temIm[0] - temIm[1];
292 temIm[0] = tem;
293 break;
294 case 3:
295 float t1Re = temRe[1] + temRe[2];
296 float t1Im = temIm[1] + temIm[2];
297 temRe[0] = temRe[0] + t1Re;
298 temIm[0] = temIm[0] + t1Im;
299
300 float m1Re = cos2to3PI * t1Re;
301 float m1Im = cos2to3PI * t1Im;
302 float m2Re = sin2to3PI * (temIm[1] - temIm[2]);
303 float m2Im = sin2to3PI * (temRe[2] - temRe[1]);
304 float s1Re = temRe[0] + m1Re;
305 float s1Im = temIm[0] + m1Im;
306
307 temRe[1] = s1Re + m2Re;
308 temIm[1] = s1Im + m2Im;
309 temRe[2] = s1Re - m2Re;
310 temIm[2] = s1Im - m2Im;
311 break;
312 case 4:
313 fft4(temRe, temIm);
314 break;
315 case 5:
316 fft5(temRe, temIm);
317 break;
318 case 8:
319 fft8();
320 break;
321 case 10:
322 fft10();
323 break;
324 default :
325 fftPrime(radix);
326 break;
327 }
328 address = groupOffset;
329 for (int i = 0; i < radix; i++) {
330 outputRe[address] = temRe[i];
331 outputIm[address] = temIm[i];
332 address += sofarRadix;
333 }
334 groupOffset += sofarRadix * radix;
335 address = groupOffset;
336 }
337 groupOffset = ++dataOffset;
338 address = groupOffset;
339 }
340 } // End of function twiddle().
341
342 // The two arguments dataRe[], dataIm[] are mainly for using in fft8();
343 private void fft4(float dataRe[], float dataIm[]) {
344 float t1Re,t1Im, t2Re,t2Im;
345 float m2Re,m2Im, m3Re,m3Im;
346
347 t1Re = dataRe[0] + dataRe[2];
348 t1Im = dataIm[0] + dataIm[2];
349 t2Re = dataRe[1] + dataRe[3];
350 t2Im = dataIm[1] + dataIm[3];
351
352 m2Re = dataRe[0] - dataRe[2];
353 m2Im = dataIm[0] - dataIm[2];
354 m3Re = dataIm[1] - dataIm[3];
355 m3Im = dataRe[3] - dataRe[1];
356
357 dataRe[0] = t1Re + t2Re;
358 dataIm[0] = t1Im + t2Im;
359 dataRe[2] = t1Re - t2Re;
360 dataIm[2] = t1Im - t2Im;
361 dataRe[1] = m2Re + m3Re;
362 dataIm[1] = m2Im + m3Im;
363 dataRe[3] = m2Re - m3Re;
364 dataIm[3] = m2Im - m3Im;
365 } // End of function fft4().
366
367 // The two arguments dataRe[], dataIm[] are mainly for using in fft10();
368 private void fft5(float dataRe[], float dataIm[]) {
369 float t1Re,t1Im, t2Re,t2Im, t3Re,t3Im, t4Re,t4Im, t5Re,t5Im;
370 float m1Re,m1Im, m2Re,m2Im, m3Re,m3Im, m4Re,m4Im, m5Re,m5Im;
371 float s1Re,s1Im, s2Re,s2Im, s3Re,s3Im, s4Re,s4Im, s5Re,s5Im;
372
373 t1Re = dataRe[1] + dataRe[4];
374 t1Im = dataIm[1] + dataIm[4];
375 t2Re = dataRe[2] + dataRe[3];
376 t2Im = dataIm[2] + dataIm[3];
377 t3Re = dataRe[1] - dataRe[4];
378 t3Im = dataIm[1] - dataIm[4];
379 t4Re = dataRe[3] - dataRe[2];
380 t4Im = dataIm[3] - dataIm[2];
381 t5Re = t1Re + t2Re;
382 t5Im = t1Im + t2Im;
383
384 dataRe[0] = dataRe[0] + t5Re;
385 dataIm[0] = dataIm[0] + t5Im;
386
387 m1Re = c51 * t5Re;
388 m1Im = c51 * t5Im;
389 m2Re = c52 * (t1Re - t2Re);
390 m2Im = c52 * (t1Im - t2Im);
391 m3Re = -c53 * (t3Im + t4Im);
392 m3Im = c53 * (t3Re + t4Re);
393 m4Re = -c54 * t4Im;
394 m4Im = c54 * t4Re;
395 m5Re = -c55 * t3Im;
396 m5Im = c55 * t3Re;
397
398 s3Re = m3Re - m4Re;
399 s3Im = m3Im - m4Im;
400 s5Re = m3Re + m5Re;
401 s5Im = m3Im + m5Im;
402 s1Re = dataRe[0] + m1Re;
403 s1Im = dataIm[0] + m1Im;
404 s2Re = s1Re + m2Re;
405 s2Im = s1Im + m2Im;
406 s4Re = s1Re - m2Re;
407 s4Im = s1Im - m2Im;
408
409 dataRe[1] = s2Re + s3Re;
410 dataIm[1] = s2Im + s3Im;
411 dataRe[2] = s4Re + s5Re;
412 dataIm[2] = s4Im + s5Im;
413 dataRe[3] = s4Re - s5Re;
414 dataIm[3] = s4Im - s5Im;
415 dataRe[4] = s2Re - s3Re;
416 dataIm[4] = s2Im - s3Im;
417 } // End of function fft5().
418
419 private void fft8() {
420 float data1Re[] = new float[4];
421 float data1Im[] = new float[4];
422 float data2Re[] = new float[4];
423 float data2Im[] = new float[4];
424 float tem;
425
426 // To improve the speed, use direct assaignment instead for loop here.
427 data1Re[0] = temRe[0];
428 data2Re[0] = temRe[1];
429 data1Re[1] = temRe[2];
430 data2Re[1] = temRe[3];
431 data1Re[2] = temRe[4];
432 data2Re[2] = temRe[5];
433 data1Re[3] = temRe[6];
434 data2Re[3] = temRe[7];
435
436 data1Im[0] = temIm[0];
437 data2Im[0] = temIm[1];
438 data1Im[1] = temIm[2];
439 data2Im[1] = temIm[3];
440 data1Im[2] = temIm[4];
441 data2Im[2] = temIm[5];
442 data1Im[3] = temIm[6];
443 data2Im[3] = temIm[7];
444
445 fft4(data1Re, data1Im);
446 fft4(data2Re, data2Im);
447
448 tem = OnetoSqrt2 * (data2Re[1] + data2Im[1]);
449 data2Im[1] = OnetoSqrt2 * (data2Im[1] - data2Re[1]);
450 data2Re[1] = tem;
451 tem = data2Im[2];
452 data2Im[2] = -data2Re[2];
453 data2Re[2] = tem;
454 tem = OnetoSqrt2 * (data2Im[3] - data2Re[3]);
455 data2Im[3] = -OnetoSqrt2 * (data2Re[3] + data2Im[3]);
456 data2Re[3] = tem;
457
458 temRe[0] = data1Re[0] + data2Re[0];
459 temRe[4] = data1Re[0] - data2Re[0];
460 temRe[1] = data1Re[1] + data2Re[1];
461 temRe[5] = data1Re[1] - data2Re[1];
462 temRe[2] = data1Re[2] + data2Re[2];
463 temRe[6] = data1Re[2] - data2Re[2];
464 temRe[3] = data1Re[3] + data2Re[3];
465 temRe[7] = data1Re[3] - data2Re[3];
466
467 temIm[0] = data1Im[0] + data2Im[0];
468 temIm[4] = data1Im[0] - data2Im[0];
469 temIm[1] = data1Im[1] + data2Im[1];
470 temIm[5] = data1Im[1] - data2Im[1];
471 temIm[2] = data1Im[2] + data2Im[2];
472 temIm[6] = data1Im[2] - data2Im[2];
473 temIm[3] = data1Im[3] + data2Im[3];
474 temIm[7] = data1Im[3] - data2Im[3];
475 } // End of function fft8().
476
477 private void fft10() {
478 float data1Re[] = new float[5];
479 float data1Im[] = new float[5];
480 float data2Re[] = new float[5];
481 float data2Im[] = new float[5];
482
483 // To improve the speed, use direct assaignment instead for loop here.
484 data1Re[0] = temRe[0];
485 data2Re[0] = temRe[5];
486 data1Re[1] = temRe[2];
487 data2Re[1] = temRe[7];
488 data1Re[2] = temRe[4];
489 data2Re[2] = temRe[9];
490 data1Re[3] = temRe[6];
491 data2Re[3] = temRe[1];
492 data1Re[4] = temRe[8];
493 data2Re[4] = temRe[3];
494
495 data1Im[0] = temIm[0];
496 data2Im[0] = temIm[5];
497 data1Im[1] = temIm[2];
498 data2Im[1] = temIm[7];
499 data1Im[2] = temIm[4];
500 data2Im[2] = temIm[9];
501 data1Im[3] = temIm[6];
502 data2Im[3] = temIm[1];
503 data1Im[4] = temIm[8];
504 data2Im[4] = temIm[3];
505
506 fft5(data1Re, data1Im);
507 fft5(data2Re, data2Im);
508
509 temRe[0] = data1Re[0] + data2Re[0];
510 temRe[5] = data1Re[0] - data2Re[0];
511 temRe[6] = data1Re[1] + data2Re[1];
512 temRe[1] = data1Re[1] - data2Re[1];
513 temRe[2] = data1Re[2] + data2Re[2];
514 temRe[7] = data1Re[2] - data2Re[2];
515 temRe[8] = data1Re[3] + data2Re[3];
516 temRe[3] = data1Re[3] - data2Re[3];
517 temRe[4] = data1Re[4] + data2Re[4];
518 temRe[9] = data1Re[4] - data2Re[4];
519
520 temIm[0] = data1Im[0] + data2Im[0];
521 temIm[5] = data1Im[0] - data2Im[0];
522 temIm[6] = data1Im[1] + data2Im[1];
523 temIm[1] = data1Im[1] - data2Im[1];
524 temIm[2] = data1Im[2] + data2Im[2];
525 temIm[7] = data1Im[2] - data2Im[2];
526 temIm[8] = data1Im[3] + data2Im[3];
527 temIm[3] = data1Im[3] - data2Im[3];
528 temIm[4] = data1Im[4] + data2Im[4];
529 temIm[9] = data1Im[4] - data2Im[4];
530 } // End of function fft10().
531
532 public double sqrt(double d) {
533 return Math.sqrt(d);
534 }
535
536 private void fftPrime(int radix) {
537 // Initial WRe, WIm.
538 float W = 2 * (float) Math.PI / radix;
539 float cosW = (float) Math.cos(W);
540 float sinW = -(float) Math.sin(W);
541 float WRe[] = new float[radix];
542 float WIm[] = new float[radix];
543
544 WRe[0] = 1;
545 WIm[0] = 0;
546 WRe[1] = cosW;
547 WIm[1] = sinW;
548
549 for (int i = 2; i < radix; i++) {
550 WRe[i] = cosW * WRe[i - 1] - sinW * WIm[i - 1];
551 WIm[i] = sinW * WRe[i - 1] + cosW * WIm[i - 1];
552 }
553
554 // FFT of prime length data, using DFT, can be improved in the future.
555 float rere, reim, imre, imim;
556 int j, k;
557 int max = (radix + 1) / 2;
558
559 float tem1Re[] = new float[max];
560 float tem1Im[] = new float[max];
561 float tem2Re[] = new float[max];
562 float tem2Im[] = new float[max];
563
564 for (j = 1; j < max; j++) {
565 tem1Re[j] = temRe[j] + temRe[radix - j];
566 tem1Im[j] = temIm[j] - temIm[radix - j];
567 tem2Re[j] = temRe[j] - temRe[radix - j];
568 tem2Im[j] = temIm[j] + temIm[radix - j];
569 }
570
571 for (j = 1; j < max; j++) {
572 temRe[j] = temRe[0];
573 temIm[j] = temIm[0];
574 temRe[radix - j] = temRe[0];
575 temIm[radix - j] = temIm[0];
576 k = j;
577 for (int i = 1; i < max; i++) {
578 rere = WRe[k] * tem1Re[i];
579 imim = WIm[k] * tem1Im[i];
580 reim = WRe[k] * tem2Im[i];
581 imre = WIm[k] * tem2Re[i];
582
583 temRe[radix - j] += rere + imim;
584 temIm[radix - j] += reim - imre;
585 temRe[j] += rere - imim;
586 temIm[j] += reim + imre;
587
588 k = k + j;
589 if (k >= radix)
590 k = k - radix;
591 }
592 }
593 for (j = 1; j < max; j++) {
594 temRe[0] = temRe[0] + tem1Re[j];
595 temIm[0] = temIm[0] + tem2Im[j];
596 }
597 } // End of function fftPrime().
598
599 }
600