/// <summary> /// 高速フーリエ変換を行います /// </summary> /// <param name="sampleN"></param> /// <param name="srcArr">データバッファ、データの並び替えは不要</param> /// <param name="dstArr">スペクトラムの配列</param> private static void fft2(int sampleN, Complex[] srcArr, ref Complex[] dstArr, int intWidth = 24, int decWidth = 8) { var srcReArr = srcArr.Select(x => new SignedFixedPoint(intWidth, decWidth) { DoubleValue = x.Real }).ToArray(); var ps = new SignedFixedPoint[sampleN]; fftImpl2(sampleN, intWidth, decWidth, srcReArr, ref ps); for (int i = 0; i < sampleN; ++i) { dstArr[i] = new Complex(ps[i].DoubleValue, 0); } }
/// <summary> /// 組み込み向けのFFT実装 /// </summary> /// <param name="sampleN"></param> /// <param name="intWidth"></param> /// <param name="decWidth"></param> /// <param name="srcReArr"></param> /// <param name="powerSpectrum"></param> private static void fftImpl2(int sampleN, int intWidth, int decWidth, SignedFixedPoint[] srcReArr, ref SignedFixedPoint[] powerSpectrum) { //元データをビット反転してコピー var bitWidth = (int)Math.Log(sampleN, 2); var addressingArr = generateBitReverseArr(bitWidth).ToArray();//アドレッシングテーブル var dstReArr = new SignedFixedPoint[sampleN]; var dstImArr = new SignedFixedPoint[sampleN]; foreach (var pair in addressingArr.Select((y, x) => new { SrcIndex = x, DstIndex = y })) { dstReArr[pair.DstIndex] = srcReArr[pair.SrcIndex]; dstImArr[pair.DstIndex] = new SignedFixedPoint(intWidth, decWidth) { RawData = 0x0 }; } //バタフライ演算回数 int stageN = (int)Math.Log(sampleN, 2);//定数 //回転子の事前計算 var wMax = sampleN / 2; var wTable = Enumerable.Range(0, wMax).Select(n => Complex.Exp(-Complex.ImaginaryOne * 2 * Math.PI * n / sampleN)).ToArray(); var wReTable = wTable.Select(x => x.Real).Select(x => new SignedFixedPoint(intWidth, decWidth) { DoubleValue = x }).ToArray(); var wImTable = wTable.Select(x => x.Imaginary).Select(x => new SignedFixedPoint(intWidth, decWidth) { DoubleValue = x }).ToArray(); /* バタフライ演算をする */ for (int stage = 0; stage < stageN; ++stage) { //0 ~ sampleN / 2まで2個ずつ処理する for (int i = 0; i < sampleN / 2; ++i) { //対象データのインデックス+サブインデックス(2次元配列等価) int index1 = (i >> stage) << 1; int index2 = index1 + 1; int subIndex = i & ~(0xffff << stage); //実データへのアドレスは {index,subIndex} int addr1 = (index1 << stage) + subIndex; //元の配列の位置 int addr2 = (index2 << stage) + subIndex; //元の配列の位置 //回転子決定 int wIndex = (((subIndex) & ~(0xffff << stage)) << (stageN - stage - 1)); //Debug.WriteLine($"i:{i}\twIndex:{wIndex}\tdata1[{index1}, {subIndex}](addr:{addr1}) \t data2[{index2}, {subIndex}](addr:{addr2})"); //計算 var srcDataRe1 = dstReArr[addr1]; var srcDataIm1 = dstImArr[addr1]; var srcDataRe2 = dstReArr[addr2]; var srcDataIm2 = dstImArr[addr2]; //w * srcData2 var wRe = wReTable[wIndex]; var wIm = wImTable[wIndex]; var mulRe1Re2 = wRe * srcDataRe2; var mulIm1Im2 = wIm * srcDataIm2; var mulRe1Im2 = wRe * srcDataIm2; var mulRe2Im1 = wIm * srcDataRe2; var mulRe = mulRe1Re2 - mulIm1Im2; var mulIm = mulRe1Im2 + mulRe2Im1; //srcData1 + w * srcData2, srcData1 - w * srcData2 var dstDataRe1 = srcDataRe1 + mulRe; var dstDataIm1 = srcDataIm1 + mulIm; var dstDataRe2 = srcDataRe1 - mulRe; var dstDataIm2 = srcDataIm1 - mulIm; dstReArr[addr1] = dstDataRe1; dstImArr[addr1] = dstDataIm1; dstReArr[addr2] = dstDataRe2; dstImArr[addr2] = dstDataIm2; } } /* 計算結果をパワースペクトルに直す */ for (int i = 0; i < sampleN; ++i) { //最終的な値を符号無しにすれば終わり powerSpectrum[i] = dstReArr[i].IsSigned ? dstReArr[i].TwoComplementary : dstReArr[i]; } }