1: package fourier;
  2: 
  3: import java.util.Arrays;
  4: 
  5: /**
  6:  * 離散フーリエ1次元変換(discrete fourier 1D transformation)。
  7:  * サンプル数(離散データ数)が2のN乗のときにはCooley-Tukeyのアルゴリズム(蝶(Butterfly)演算)を用いて高速化する。
  8:  * J.W.Cooley, J.W.Tukey: An Algorithm for the Machine Calculation of Complex Fourier Series, Mathematics of Computation, Vol.19 1965.
  9:  */
 10: public class DiscreteFourier1dTransformation extends DiscreteFourierTransformation
 11: {
 12:     /**
 13:      * 元データの実部を保持するフィールド。
 14:      */
 15:     protected double[] sourceRealPart;
 16:     
 17:     /**
 18:      * 元データの虚部を保持するフィールド。
 19:      */
 20:     protected double[] sourceImaginaryPart;
 21:     
 22:     /**
 23:      * フーリエ変換を施した実部を保持するフィールド。
 24:      */
 25:     protected double[] destinationRealPart;
 26:     
 27:     /**
 28:      * フーリエ変換を施した虚部を保持するフィールド。
 29:      */
 30:     protected double[] destinationImaginaryPart;
 31: 
 32:     
 33:     /**
 34:      * 元データの実部だけを指定して離散フーリエ1次元変換のインスタンスを作るコンストラクタ。
 35:      */
 36:     public DiscreteFourier1dTransformation(double[] sourceRealData)
 37:     {
 38:         super();
 39:         int size = sourceRealData.length;
 40:         double[] sourceImaginaryData = new double[size];
 41:         Arrays.fill(sourceImaginaryData, 0.0d);
 42:         this.initialize(sourceRealData, sourceImaginaryData);
 43:     }
 44: 
 45:     /**
 46:      * 元データの実部と虚部を指定して離散フーリエ1次元変換のインスタンスを作るコンストラクタ。
 47:      */
 48:     public DiscreteFourier1dTransformation(double[] sourceRealData, double[] sourceImaginaryData)
 49:     {
 50:         super();
 51:         this.initialize(sourceRealData, sourceImaginaryData);
 52:     }
 53:     
 54:     /**
 55:      * 離散フーリエ変換の結果を水に流す。
 56:      */
 57:     protected void flush()
 58:     {
 59:         destinationRealPart = null;
 60:         destinationImaginaryPart = null;
 61:         return;
 62:     }
 63:     
 64:     /**
 65:      * 離散フーリエ変換(順変換)を施した虚部を応答する。
 66:      */
 67:     public double[] imaginaryPart()
 68:     {
 69:         if (destinationImaginaryPart == null) { this.transform(); }
 70:         return destinationImaginaryPart;
 71:     }
 72:     
 73:     /**
 74:      * 離散フーリエ変換を初期化する。
 75:      */
 76:     protected void initialize()
 77:     {
 78:         sourceRealPart = null;
 79:         sourceImaginaryPart = null;
 80:         this.flush();
 81:         return;
 82:     }
 83: 
 84:     /**
 85:      * 離散フーリエ変換を初期化する。
 86:      */
 87:     protected void initialize(double[] sourceRealData, double[] sourceImaginaryData)
 88:     {
 89:         sourceRealPart = sourceRealData;
 90:         sourceImaginaryPart = sourceImaginaryData;
 91:         this.flush();
 92:         return;
 93:     }
 94: 
 95:     /**
 96:      * 逆離散フーリエ変換(逆変換)を施した虚部を応答する。
 97:      */
 98:     public double[] inverseImaginaryPart()
 99:     {
100:         if (destinationImaginaryPart == null) { this.inverseTransform(); }
101:         return destinationImaginaryPart;
102:     }
103:     
104:     /**
105:      * 逆離散フーリエ変換(逆変換)を施した実部を応答する。
106:      */
107:     public double[] inverseRealPart()
108:     {
109:         if (destinationRealPart == null) { this.inverseTransform(); }
110:         return destinationRealPart;
111:     }
112:     
113:     /**
114:      * 逆離散フーリエ変換(逆変換)を施す。
115:      */
116:     public DiscreteFourier1dTransformation inverseTransform()
117:     {
118:         return this.transform(-1);
119:     }
120:     
121:     /**
122:      * ある整数(n)が2のN乗であるかどうかを応答する。
123:      */
124:     protected boolean isPowerOfTwo(int n)
125:     {
126:         return this.powerOfTwo(n) > 0;
127:     }
128: 
129:     /**
130:      * 配列(anArray)の対数を取って応答する。
131:      */
132:     public double[] logarithmicArray(double[] anArray)
133:     {
134:         int n = anArray.length;
135:         double[] logarithmicArray = new double[n];
136:         for (int i = 0; i < n; i++)
137:         {
138:             double aValue = anArray[i];
139:             aValue = Math.log(aValue);
140:             logarithmicArray[i] = aValue;
141:         }
142:         return logarithmicArray;
143:     }
144:     
145:     /**
146:      * 配列(anArray)を正規化して応答する。
147:      */
148:     public double[] normalizedArray(double[] anArray)
149:     {
150:         double maximum = 0.0d;
151:         int n = anArray.length;
152:         for (int i = 0; i < n; i++)
153:         {
154:             maximum = Math.max(anArray[i], maximum);
155:         }
156:         double[] normalizedArray = new double[n];
157:         for (int i = 0; i < n; i++)
158:         {
159:             double aValue = anArray[i] / maximum;
160:             aValue = Math.max(aValue, 0.0d);
161:             aValue = Math.min(aValue, 1.0d);
162:             normalizedArray[i] = aValue;
163:         }
164:         return normalizedArray;
165:     }
166:     
167:     /**
168:      * 離散フーリエ変換を施した結果の正規化対数パワースペクトルを応答する。
169:      */
170:     public double[] normalizedLogarithmicPowerSpectrum()
171:     {
172:         return normalizedArray(logarithmicArray(this.powerSpectrum()));
173:     }
174:     
175:     /**
176:      * 離散フーリエ変換を施した結果の正規化対数スペクトルを応答する。
177:      */
178:     public double[] normalizedLogarithmicSpectrum()
179:     {
180:         return normalizedArray(logarithmicArray(this.spectrum()));
181:     }
182:     
183:     /**
184:      * 離散フーリエ変換を施した結果の正規化パワースペクトルを応答する。
185:      */
186:     public double[] normalizedPowerSpectrum()
187:     {
188:         return normalizedArray(this.powerSpectrum());
189:     }
190: 
191:     /**
192:      * 離散フーリエ変換を施した結果の正規化スペクトルを応答する。
193:      */
194:     public double[] normalizedSpectrum()
195:     {
196:         return normalizedArray(this.spectrum());
197:     }
198:     
199:     /**
200:      * ある整数(n)が2のN乗であるとき、そのNを応答する。それ以外のときは、0を応答する。
201:      */
202:     protected int powerOfTwo(int n)
203:     {
204:         int powerOfTwo = 1;
205:         for (int nth = 1; powerOfTwo < n; nth++)
206:         {
207:             powerOfTwo = 1 << nth;
208:             if (powerOfTwo == n) { return nth; }
209:         }
210:         return 0;
211:     }
212:     
213:     /**
214:      * 離散フーリエ変換を施した結果のパワースペクトルを応答する。
215:      */
216:     public double[] powerSpectrum()
217:     {
218:         double[] realValues = this.realPart();
219:         double[] imaginaryValues = this.imaginaryPart();
220:         int n = realValues.length;
221:         double[] powerSpectrum = new double[n];
222:         for (int i = 0; i < n; i++)
223:         {
224:             double realValue = realValues[i];
225:             double imaginaryValue = imaginaryValues[i];
226:             double aValue = (realValue * realValue) + (imaginaryValue * imaginaryValue);
227:             powerSpectrum[i] = aValue;
228:         }
229:         return powerSpectrum;
230:     }
231:     
232:     /**
233:      * 離散フーリエ変換(順変換)を施した実部を応答する。
234:      */
235:     public double[] realPart()
236:     {
237:         if (destinationRealPart == null) { this.transform(); }
238:         return destinationRealPart;
239:     }
240:     
241:     /**
242:      * 離散フーリエ変換を施した結果のスペクトルを応答する。
243:      */
244:     public double[] spectrum()
245:     {
246:         return squareRootArray(this.powerSpectrum());
247:     }
248:     
249:     /**
250:      * 配列(anArray)の平方根を取って応答する。
251:      */
252:     public double[] squareRootArray(double[] anArray)
253:     {
254:         int n = anArray.length;
255:         double[] squareRootArray = new double[n];
256:         for (int i = 0; i < n; i++)
257:         {
258:             double aValue = anArray[i];
259:             aValue = Math.sqrt(aValue);
260:             squareRootArray[i] = aValue;
261:         }
262:         return squareRootArray;
263:     }
264:     
265:     /**
266:      * 離散フーリエ変換(順変換)を施す。
267:      */
268:     public DiscreteFourier1dTransformation transform()
269:     {
270:         return this.transform(1);
271:     }
272: 
273:     /**
274:      * 離散フーリエ変換を施す。flagが1の時は順変換。flagが-1の時は逆変換。
275:      */
276:     protected DiscreteFourier1dTransformation transform(int flag)
277:     {
278:         int n = sourceRealPart.length;
279:         if (this.isPowerOfTwo(n)) { return this.turboTransform(flag); }
280:         double[] x = this.sourceRealPart;
281:         double[] y = this.sourceImaginaryPart;
282:         double[] c = new double[n];
283:         double[] s = new double[n];
284:         double q = 2.0d * Math.PI / (double)n * (double)flag;
285:         for (int i = 0; i < n; i++)
286:         {
287:             c[i] = 0.0d;
288:             s[i] = 0.0d;
289:             for (int j = 0; j < n; j++)
290:             {
291:                 double xj = x[j];
292:                 double yj = y[j];
293:                 double qij = q * i * j;
294:                 double cos = Math.cos(qij);
295:                 double sin = Math.sin(qij);
296:                 c[i] += (xj * cos) - (yj * sin);
297:                 s[i] += (xj * sin) + (yj * cos);
298:             }
299:             if (flag == -1)
300:             {
301:                 c[i] /= (double)n;
302:                 s[i] /= (double)n;
303:             }
304:         }
305:         destinationRealPart = c;
306:         destinationImaginaryPart = s;
307:         return this;
308:     }
309:     
310:     /**
311:      * 高速離散フーリエ変換を施す。flagが1の時は順変換。flagが-1の時は逆変換。
312:      * Cooley-Tukeyのアルゴリズム(蝶(Butterfly)演算)で高速化する。
313:      * J.W.Cooley, J.W.Tukey: An Algorithm for the Machine Calculation of Complex Fourier Series, Mathematics of Computation, Vol.19 1965.
314:      */
315:     protected DiscreteFourier1dTransformation turboTransform(int flag)
316:     {
317:         int n = sourceRealPart.length;
318:         int power = this.powerOfTwo(n);
319:         double[] x = new double[n];
320:         double[] y = new double[n];
321:         System.arraycopy(this.sourceRealPart, 0, x, 0, n);
322:         System.arraycopy(this.sourceImaginaryPart, 0, y, 0, n);
323:         double[] c = new double[n];
324:         double[] s = new double[n];
325:         if (flag == -1)
326:         {
327:             for (int i = 0; i < n; i++)
328:             {
329:                 x[i] /= (double)n;
330:                 y[i] /= (double)n;
331:             }
332:         }
333:         this.turboTransformInitialize(x, y, power);
334:         double unitAngle = 2.0d * Math.PI / (double)n;
335:         int dft = 2;
336:         for (int i = 0; i < power; i++)
337:         {
338:             int dftN = n / dft;
339:             double stepAngle = unitAngle * (double)dftN;
340:             for (int j = 0; j < dftN; j++)
341:             {
342:                 double angle = 0.0d;
343:                 for (int k = 0; k < dft; k++)
344:                 {
345:                     int toIndex = j * dft + k;
346:                     int fromIndex1;
347:                     int fromIndex2;
348:                     int dftHalf = dft / 2;
349:                     if (k < dftHalf)
350:                     {
351:                         fromIndex1 = toIndex;
352:                         fromIndex2 = fromIndex1 + dftHalf;
353:                     }
354:                     else
355:                     {
356:                         fromIndex2 = toIndex;
357:                         fromIndex1 = toIndex - dftHalf;
358:                     }
359:                     double xr = x[fromIndex2];
360:                     double yi = y[fromIndex2];
361:                     double cos = Math.cos(angle);
362:                     double sin = Math.sin(angle);
363:                     c[toIndex] = x[fromIndex1] + (xr * cos) - ((double)flag * yi * sin);
364:                     s[toIndex] = y[fromIndex1] + ((double)flag * xr * sin) + (yi * cos);
365:                     angle += stepAngle;
366:                 }
367:             }
368:             for (int j = 0; j < n; j++)
369:             {
370:                 x[j] = c[j];
371:                 y[j] = s[j];
372:             }
373:             dft *= 2;
374:         }
375:         destinationRealPart = x;
376:         destinationImaginaryPart = y;
377:         return this;
378:     }
379: 
380:     /**
381:      * 高速離散フーリエ変換のための置き換え。
382:      * Cooley-Tukeyのアルゴリズムを適用するための準備。
383:      */
384:     private void turboTransformInitialize(double[] x, double[] y, int power)
385:     {
386:         int n = x.length;
387:         double[] xr = new double[n];
388:         double[] yi = new double[n];
389:         int dftTimes = n;
390:         for (int i = 0; i < power - 1; i++)
391:         {
392:             int toIndex = 0;
393:             int offset = 0;
394:             while (toIndex < n)
395:             {
396:                 int fromIndex = 0;
397:                 while (fromIndex < dftTimes)
398:                 {
399:                     xr[toIndex] = x[fromIndex + offset];
400:                     yi[toIndex] = y[fromIndex + offset];
401:                     toIndex++;
402:                     fromIndex += 2;
403:                     if (fromIndex == dftTimes) { fromIndex = 1; }
404:                 }
405:                 offset += dftTimes;
406:             }
407:             for (int j = 0; j < n; j++)
408:             {
409:                 x[j] = xr[j];
410:                 y[j] = yi[j];
411:             }
412:             dftTimes /= 2;
413:         }
414:     }
415: }

This document was generated by KSU.TextDoclet on 2012/05/28 at 06:08:28.