public void TestNextPowerOf2() { Assert.AreEqual(1024, SignalExtension.NextPowerOf2(1000)); Assert.AreEqual(2048, SignalExtension.NextPowerOf2(1024)); Assert.AreEqual(1, SignalExtension.NextPowerOf2(0)); Assert.AreEqual(0, SignalExtension.NextPowerOf2(-100)); }
/// <summary> /// Compute the forward or inverse Fourier Transform of data, with data /// containing complex valued data as alternating real and imaginary /// parts. The length must be a power of 2. This method caches values /// and should be slightly faster on repeated uses than then FFT method. /// It is also slightly more accurate. /// </summary> /// <param name="data">The complex data stored as alternating real /// and imaginary parts</param> /// <param name="forward">true for a forward transform, false for /// inverse transform</param> public void TableFFT(ref double[] data, bool forward) { var n = data.Length; // checks n is a power of 2 in 2's complement format if ((n & (n - 1)) != 0) { data = data.SubArray(SignalExtension.NextPowerOf2(n)); n = data.Length; } n /= 2; // n is the number of samples Reverse(ref data, n); // bit index data reversal // make table if needed if (_cosTable.Count != n) { Initialize(n); } // do transform: so single point transforms, then doubles, etc. double sign = forward ? 1 : -1; var mmax = 1; var tptr = 0; while (n > mmax) { var istep = 2 * mmax; for (var m = 0; m < istep; m += 2) { var wr = _cosTable[tptr]; var wi = sign * _sinTable[tptr++]; for (var k = m; k < 2 * n; k += 2 * istep) { var j = k + istep; var tempr = wr * data[j] - wi * data[j + 1]; var tempi = wi * data[j] + wr * data[j + 1]; data[j] = data[k] - tempr; data[j + 1] = data[k + 1] - tempi; data[k] = data[k] + tempr; data[k + 1] = data[k + 1] + tempi; } } mmax = istep; } // copy out with optional scaling if (forward) { return; } var scale = 1.0 / n; for (var i = 0; i < 2 * n; ++i) { data[i] *= scale; } }
/// <summary> /// Compute the forward or inverse Fourier Transform of data, with /// data containing complex valued data as alternating real and /// imaginary parts. The length must be a power of 2. /// </summary> /// <param name="data">The complex data stored as alternating real /// and imaginary parts</param> /// <param name="forward">true for a forward transform, false for /// inverse transform</param> public void DynamicFFT(ref double[] data, bool forward) { var n = data.Length; // checks n is a power of 2 in 2's complement format if ((n & (n - 1)) != 0) { data = data.SubArray(SignalExtension.NextPowerOf2(n)); n = data.Length; } n /= 2; // n is the number of samples Reverse(ref data, n); // bit index data reversal // do transform: so single point transforms, then doubles, etc. double sign = forward ? 1 : -1; var mmax = 1; while (n > mmax) { var istep = 2 * mmax; var theta = sign * Math.PI / mmax; double wr = 1, wi = 0; var wpr = Math.Cos(theta); var wpi = Math.Sin(theta); for (var m = 0; m < istep; m += 2) { for (var k = m; k < 2 * n; k += 2 * istep) { var j = k + istep; var tempr = wr * data[j] - wi * data[j + 1]; var tempi = wi * data[j] + wr * data[j + 1]; data[j] = data[k] - tempr; data[j + 1] = data[k + 1] - tempi; data[k] = data[k] + tempr; data[k + 1] = data[k + 1] + tempi; } var t = wr; // trig recurrence wr = wr * wpr - wi * wpi; wi = wi * wpr + t * wpi; } mmax = istep; } // inverse scaling in the backward case if (forward) { return; } var scale = 1.0 / n; for (var i = 0; i < 2 * n; ++i) { data[i] *= scale; } }
static bool Test(FFTAction fftFunction, double[] test, IList <double> answer) { // Test the given function on the given data and see if the result is the given answer. var returnValue = true; var copy = test.ToArray(); // make a copy if ((test.Length & (test.Length - 1)) != 0) { test = test.SubArray(SignalExtension.NextPowerOf2(test.Length)); } fftFunction(ref copy, true); // forward transform returnValue &= Compare(copy, answer); // check it fftFunction(ref copy, false); // backward transform returnValue &= Compare(copy, test); // check it return(returnValue); }
/// <summary> /// Executes the block /// </summary> public override void Execute() { var connectingNode = InputNodes[0].ConnectingNode as BlockOutputNode; if (connectingNode == null || connectingNode.Object == null) { return; } int beforeSize = 0, afterSize = 0; if (ExtensionSize > 0) { beforeSize = ExtensionSize; afterSize = ExtensionSize; } OutputNodes[0].Object.Clear(); foreach (var signal in connectingNode.Object) { if (ExtensionSize <= 0) { var size = SignalExtension.NextPowerOf2(signal.Samples.Length); beforeSize = afterSize = (size - signal.Samples.Length) / 2; while (beforeSize + afterSize + signal.Samples.Length < size) { afterSize++; } } var output = signal.Clone(); SignalExtension.Extend(ref output, ExtensionMode, beforeSize, afterSize); OutputNodes[0].Object.Add(output); } if (Cascade && OutputNodes[0].ConnectingNode != null) { OutputNodes[0].ConnectingNode.Root.Execute(); } }
/// <summary> /// Compute the forward or inverse Fourier Transform of data, with /// data containing real valued data only. The output is complex /// valued after the first two entries, stored in alternating real /// and imaginary parts. The first two returned entries are the real /// parts of the first and last value from the conjugate symmetric /// output, which are necessarily real. The length must be a power /// of 2. /// </summary> /// <param name="data">The complex data stored as alternating real /// and imaginary parts</param> /// <param name="forward">true for a forward transform, false for /// inverse transform</param> public void RealFFT(ref double[] data, bool forward) { var n = data.Length; // # of real inputs, 1/2 the complex length // checks n is a power of 2 in 2's complement format if ((n & (n - 1)) != 0) { data = data.SubArray(SignalExtension.NextPowerOf2(n)); n = data.Length; } double sign = -1; if (forward) { // do packed FFT. This can be changed to FFT to save memory TableFFT(ref data, true); sign = 1; } var theta = sign * 2 * Math.PI / n; var wpr = Math.Cos(theta); var wpi = Math.Sin(theta); var wjr = wpr; var wji = wpi; for (var j = 1; j <= n / 4; ++j) { var k = n / 2 - j; var tnr = data[2 * k]; var tni = data[2 * k + 1]; var tjr = data[2 * j]; var tji = data[2 * j + 1]; var e = (tjr + tnr); var f = (tji - tni); var a = (tjr - tnr) * wji; var d = (tji + tni) * wji; var b = (tji + tni) * wjr; var c = (tjr - tnr) * wjr; // compute entry y[j] data[2 * j] = 0.5 * (e + sign * (a + b)); data[2 * j + 1] = 0.5 * (f - sign * (c - d)); // compute entry y[k] data[2 * k] = 0.5 * (e - sign * (a + b)); data[2 * k + 1] = 0.5 * (sign * (-c + d) - f); var temp = wjr; // todo - allow more accurate version here? make option? wjr = wjr * wpr - wji * wpi; wji = temp * wpi + wji * wpr; } if (forward) { // compute final y0 and y_{N/2}, store data[0], data[1] var temp = data[0]; data[0] += data[1]; data[1] = temp - data[1]; } else { var temp = data[0]; // unpack the y[j], then invert FFT data[0] = 0.5 * (temp + data[1]); data[1] = 0.5 * (temp - data[1]); // do packed FFT. This can be changed to FFT to save memory TableFFT(ref data, false); } }
/// <summary> /// Convolves vectors input and filter using a managed FFT algorithm. /// </summary> /// <param name="input">The input signal</param> /// <param name="filter">The filter</param> /// <param name="returnOnlyValid">True to return only the middle of the array</param> /// <param name="margin">Margin to be used if returnOnlyValid is set to true</param> /// <param name="mode">Mode</param> /// <returns></returns> public static double[] ConvolveManagedFFT(double[] input, double[] filter, bool returnOnlyValid = true, int margin = 0, ManagedFFTModeEnum mode = ManagedFFTModeEnum.UseLookupTable) { if (input == null || filter == null) { return(null); } if (input.Length < filter.Length) { var auxSignal = input; input = filter; filter = auxSignal; } var realSize = input.Length + filter.Length - 1; var size = ((realSize > 0) && ((realSize & (realSize - 1)) == 0) ? realSize : SignalExtension.NextPowerOf2(realSize)); var inputFFT = MemoryPool.Pool.New <double>(size * 2); var filterFFT = MemoryPool.Pool.New <double>(size * 2); var ifft = MemoryPool.Pool.New <double>(size * 2); for (var i = 0; i < input.Length; i++) { inputFFT[i * 2] = input[i]; } for (var i = 0; i < filter.Length; i++) { filterFFT[i * 2] = filter[i]; } ManagedFFT.Instance.FFT(ref inputFFT, true, mode); ManagedFFT.Instance.FFT(ref filterFFT, true, mode); for (var i = 0; i < ifft.Length; i = i + 2) { ifft[i] = inputFFT[i] * filterFFT[i] - inputFFT[i + 1] * filterFFT[i + 1]; ifft[i + 1] = (inputFFT[i] * filterFFT[i + 1] + inputFFT[i + 1] * filterFFT[i]) * -1; } ManagedFFT.Instance.FFT(ref ifft, false, mode); var ifft2 = MemoryPool.Pool.New <double>(size); ifft2[0] = ifft[0]; for (var i = 0; i < ifft2.Length - 2; i = i + 1) { ifft2[i + 1] = ifft[ifft.Length - 2 - i * 2]; } int start; if (returnOnlyValid) { size = input.Length - filter.Length + 1; var padding = (realSize - size) / 2; start = padding + margin; size = input.Length - filter.Length - margin * 2 + 1; } else { start = 0; size = realSize; } var result = MemoryPool.Pool.New <double>(size); Array.Copy(ifft2, start, result, 0, size); return(result); }