// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2018 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // using System; using IStation.Numerics.LinearAlgebra; using IStation.Numerics.LinearAlgebra.Storage; using IStation.Numerics.Providers.FourierTransform; using Complex = System.Numerics.Complex; namespace IStation.Numerics.IntegralTransforms { /// /// Complex Fast (FFT) Implementation of the Discrete Fourier Transform (DFT). /// public static partial class Fourier { /// /// Applies the forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors. /// /// Sample vector, where the FFT is evaluated in place. public static void Forward(Complex32[] samples) { FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.SymmetricScaling); } /// /// Applies the forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors. /// /// Sample vector, where the FFT is evaluated in place. public static void Forward(Complex[] samples) { FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.SymmetricScaling); } /// /// Applies the forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. public static void Forward(Complex32[] samples, FourierOptions options) { switch (options) { case FourierOptions.NoScaling: case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.NoScaling); break; case FourierOptions.InverseExponent: FourierTransformControl.Provider.Backward(samples, FourierTransformScaling.SymmetricScaling); break; case FourierOptions.InverseExponent | FourierOptions.NoScaling: case FourierOptions.InverseExponent | FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.Backward(samples, FourierTransformScaling.NoScaling); break; default: FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. public static void Forward(Complex[] samples, FourierOptions options) { switch (options) { case FourierOptions.NoScaling: case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.NoScaling); break; case FourierOptions.InverseExponent: FourierTransformControl.Provider.Backward(samples, FourierTransformScaling.SymmetricScaling); break; case FourierOptions.InverseExponent | FourierOptions.NoScaling: case FourierOptions.InverseExponent | FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.Backward(samples, FourierTransformScaling.NoScaling); break; default: FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors. /// /// Real part of the sample vector, where the FFT is evaluated in place. /// Imaginary part of the sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. public static void Forward(float[] real, float[] imaginary, FourierOptions options = FourierOptions.Default) { if (real.Length != imaginary.Length) { throw new ArgumentException("The array arguments must have the same length."); } // TODO: consider to support this natively by the provider, without the need for copying // TODO: otherwise, consider ArrayPool Complex32[] data = new Complex32[real.Length]; for (int i = 0; i < data.Length; i++) { data[i] = new Complex32(real[i], imaginary[i]); } Forward(data, options); for (int i = 0; i < data.Length; i++) { real[i] = data[i].Real; imaginary[i] = data[i].Imaginary; } } /// /// Applies the forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors. /// /// Real part of the sample vector, where the FFT is evaluated in place. /// Imaginary part of the sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. public static void Forward(double[] real, double[] imaginary, FourierOptions options = FourierOptions.Default) { if (real.Length != imaginary.Length) { throw new ArgumentException("The array arguments must have the same length."); } // TODO: consider to support this natively by the provider, without the need for copying // TODO: otherwise, consider ArrayPool Complex[] data = new Complex[real.Length]; for (int i = 0; i < data.Length; i++) { data[i] = new Complex(real[i], imaginary[i]); } Forward(data, options); for (int i = 0; i < data.Length; i++) { real[i] = data[i].Real; imaginary[i] = data[i].Imaginary; } } /// /// Packed Real-Complex forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors. /// Since for real-valued time samples the complex spectrum is conjugate-even (symmetry), /// the spectrum can be fully reconstructed from the positive frequencies only (first half). /// The data array needs to be N+2 (if N is even) or N+1 (if N is odd) long in order to support such a packed spectrum. /// /// Data array of length N+2 (if N is even) or N+1 (if N is odd). /// The number of samples. /// Fourier Transform Convention Options. public static void ForwardReal(float[] data, int n, FourierOptions options = FourierOptions.Default) { int length = n.IsEven() ? n + 2 : n + 1; if (data.Length < length) { throw new ArgumentException($"The given array is too small. It must be at least {length} long."); } if ((options & FourierOptions.InverseExponent) == FourierOptions.InverseExponent) { throw new NotSupportedException(); } switch (options) { case FourierOptions.NoScaling: case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.ForwardReal(data, n, FourierTransformScaling.NoScaling); break; default: FourierTransformControl.Provider.ForwardReal(data, n, FourierTransformScaling.SymmetricScaling); break; } } /// /// Packed Real-Complex forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors. /// Since for real-valued time samples the complex spectrum is conjugate-even (symmetry), /// the spectrum can be fully reconstructed form the positive frequencies only (first half). /// The data array needs to be N+2 (if N is even) or N+1 (if N is odd) long in order to support such a packed spectrum. /// /// Data array of length N+2 (if N is even) or N+1 (if N is odd). /// The number of samples. /// Fourier Transform Convention Options. public static void ForwardReal(double[] data, int n, FourierOptions options = FourierOptions.Default) { int length = n.IsEven() ? n + 2 : n + 1; if (data.Length < length) { throw new ArgumentException($"The given array is too small. It must be at least {length} long."); } if ((options & FourierOptions.InverseExponent) == FourierOptions.InverseExponent) { throw new NotSupportedException(); } switch (options) { case FourierOptions.NoScaling: case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.ForwardReal(data, n, FourierTransformScaling.NoScaling); break; default: FourierTransformControl.Provider.ForwardReal(data, n, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the forward Fast Fourier Transform (FFT) to multiple dimensional sample data. /// /// Sample data, where the FFT is evaluated in place. /// /// The data size per dimension. The first dimension is the major one. /// For example, with two dimensions "rows" and "columns" the samples are assumed to be organized row by row. /// /// Fourier Transform Convention Options. public static void ForwardMultiDim(Complex32[] samples, int[] dimensions, FourierOptions options = FourierOptions.Default) { switch (options) { case FourierOptions.NoScaling: case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.ForwardMultidim(samples, dimensions, FourierTransformScaling.NoScaling); break; case FourierOptions.InverseExponent: FourierTransformControl.Provider.BackwardMultidim(samples, dimensions, FourierTransformScaling.SymmetricScaling); break; case FourierOptions.InverseExponent | FourierOptions.NoScaling: case FourierOptions.InverseExponent | FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.BackwardMultidim(samples, dimensions, FourierTransformScaling.NoScaling); break; default: FourierTransformControl.Provider.ForwardMultidim(samples, dimensions, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the forward Fast Fourier Transform (FFT) to multiple dimensional sample data. /// /// Sample data, where the FFT is evaluated in place. /// /// The data size per dimension. The first dimension is the major one. /// For example, with two dimensions "rows" and "columns" the samples are assumed to be organized row by row. /// /// Fourier Transform Convention Options. public static void ForwardMultiDim(Complex[] samples, int[] dimensions, FourierOptions options = FourierOptions.Default) { switch (options) { case FourierOptions.NoScaling: case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.ForwardMultidim(samples, dimensions, FourierTransformScaling.NoScaling); break; case FourierOptions.InverseExponent: FourierTransformControl.Provider.BackwardMultidim(samples, dimensions, FourierTransformScaling.SymmetricScaling); break; case FourierOptions.InverseExponent | FourierOptions.NoScaling: case FourierOptions.InverseExponent | FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.BackwardMultidim(samples, dimensions, FourierTransformScaling.NoScaling); break; default: FourierTransformControl.Provider.ForwardMultidim(samples, dimensions, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the forward Fast Fourier Transform (FFT) to two dimensional sample data. /// /// Sample data, organized row by row, where the FFT is evaluated in place /// The number of rows. /// The number of columns. /// Data available organized column by column instead of row by row can be processed directly by swapping the rows and columns arguments. /// Fourier Transform Convention Options. public static void Forward2D(Complex32[] samplesRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default) { ForwardMultiDim(samplesRowWise, new[] { rows, columns }, options); } /// /// Applies the forward Fast Fourier Transform (FFT) to two dimensional sample data. /// /// Sample data, organized row by row, where the FFT is evaluated in place /// The number of rows. /// The number of columns. /// Data available organized column by column instead of row by row can be processed directly by swapping the rows and columns arguments. /// Fourier Transform Convention Options. public static void Forward2D(Complex[] samplesRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default) { ForwardMultiDim(samplesRowWise, new[] { rows, columns }, options); } /// /// Applies the forward Fast Fourier Transform (FFT) to a two dimensional data in form of a matrix. /// /// Sample matrix, where the FFT is evaluated in place /// Fourier Transform Convention Options. public static void Forward2D(Matrix samples, FourierOptions options = FourierOptions.Default) { var rowMajorArray = samples.AsRowMajorArray(); if (rowMajorArray != null) { ForwardMultiDim(rowMajorArray, new[] { samples.RowCount, samples.ColumnCount }, options); return; } var columnMajorArray = samples.AsColumnMajorArray(); if (columnMajorArray != null) { ForwardMultiDim(columnMajorArray, new[] { samples.ColumnCount, samples.RowCount }, options); return; } // Fall Back columnMajorArray = samples.ToColumnMajorArray(); ForwardMultiDim(columnMajorArray, new[] { samples.ColumnCount, samples.RowCount }, options); var denseStorage = new DenseColumnMajorMatrixStorage(samples.RowCount, samples.ColumnCount, columnMajorArray); denseStorage.CopyToUnchecked(samples.Storage, ExistingData.Clear); } /// /// Applies the forward Fast Fourier Transform (FFT) to a two dimensional data in form of a matrix. /// /// Sample matrix, where the FFT is evaluated in place /// Fourier Transform Convention Options. public static void Forward2D(Matrix samples, FourierOptions options = FourierOptions.Default) { var rowMajorArray = samples.AsRowMajorArray(); if (rowMajorArray != null) { ForwardMultiDim(rowMajorArray, new[] { samples.RowCount, samples.ColumnCount }, options); return; } var columnMajorArray = samples.AsColumnMajorArray(); if (columnMajorArray != null) { ForwardMultiDim(columnMajorArray, new[] { samples.ColumnCount, samples.RowCount }, options); return; } // Fall Back columnMajorArray = samples.ToColumnMajorArray(); ForwardMultiDim(columnMajorArray, new[] { samples.ColumnCount, samples.RowCount }, options); var denseStorage = new DenseColumnMajorMatrixStorage(samples.RowCount, samples.ColumnCount, columnMajorArray); denseStorage.CopyToUnchecked(samples.Storage, ExistingData.Clear); } /// /// Applies the inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors. /// /// Spectrum data, where the iFFT is evaluated in place. public static void Inverse(Complex32[] spectrum) { FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.SymmetricScaling); } /// /// Applies the inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors. /// /// Spectrum data, where the iFFT is evaluated in place. public static void Inverse(Complex[] spectrum) { FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.SymmetricScaling); } /// /// Applies the inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors. /// /// Spectrum data, where the iFFT is evaluated in place. /// Fourier Transform Convention Options. public static void Inverse(Complex32[] spectrum, FourierOptions options) { switch (options) { case FourierOptions.NoScaling: FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.NoScaling); break; case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.BackwardScaling); break; case FourierOptions.InverseExponent: FourierTransformControl.Provider.Forward(spectrum, FourierTransformScaling.SymmetricScaling); break; case FourierOptions.InverseExponent | FourierOptions.NoScaling: FourierTransformControl.Provider.Forward(spectrum, FourierTransformScaling.NoScaling); break; case FourierOptions.InverseExponent | FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.Forward(spectrum, FourierTransformScaling.ForwardScaling); break; default: FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors. /// /// Spectrum data, where the iFFT is evaluated in place. /// Fourier Transform Convention Options. public static void Inverse(Complex[] spectrum, FourierOptions options) { switch (options) { case FourierOptions.NoScaling: FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.NoScaling); break; case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.BackwardScaling); break; case FourierOptions.InverseExponent: FourierTransformControl.Provider.Forward(spectrum, FourierTransformScaling.SymmetricScaling); break; case FourierOptions.InverseExponent | FourierOptions.NoScaling: FourierTransformControl.Provider.Forward(spectrum, FourierTransformScaling.NoScaling); break; case FourierOptions.InverseExponent | FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.Forward(spectrum, FourierTransformScaling.ForwardScaling); break; default: FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors. /// /// Real part of the sample vector, where the iFFT is evaluated in place. /// Imaginary part of the sample vector, where the iFFT is evaluated in place. /// Fourier Transform Convention Options. public static void Inverse(float[] real, float[] imaginary, FourierOptions options = FourierOptions.Default) { if (real.Length != imaginary.Length) { throw new ArgumentException("The array arguments must have the same length."); } // TODO: consider to support this natively by the provider, without the need for copying // TODO: otherwise, consider ArrayPool Complex32[] data = new Complex32[real.Length]; for (int i = 0; i < data.Length; i++) { data[i] = new Complex32(real[i], imaginary[i]); } Inverse(data, options); for (int i = 0; i < data.Length; i++) { real[i] = data[i].Real; imaginary[i] = data[i].Imaginary; } } /// /// Applies the inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors. /// /// Real part of the sample vector, where the iFFT is evaluated in place. /// Imaginary part of the sample vector, where the iFFT is evaluated in place. /// Fourier Transform Convention Options. public static void Inverse(double[] real, double[] imaginary, FourierOptions options = FourierOptions.Default) { if (real.Length != imaginary.Length) { throw new ArgumentException("The array arguments must have the same length."); } // TODO: consider to support this natively by the provider, without the need for copying // TODO: otherwise, consider ArrayPool Complex[] data = new Complex[real.Length]; for (int i = 0; i < data.Length; i++) { data[i] = new Complex(real[i], imaginary[i]); } Inverse(data, options); for (int i = 0; i < data.Length; i++) { real[i] = data[i].Real; imaginary[i] = data[i].Imaginary; } } /// /// Packed Real-Complex inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors. /// Since for real-valued time samples the complex spectrum is conjugate-even (symmetry), /// the spectrum can be fully reconstructed form the positive frequencies only (first half). /// The data array needs to be N+2 (if N is even) or N+1 (if N is odd) long in order to support such a packed spectrum. /// /// Data array of length N+2 (if N is even) or N+1 (if N is odd). /// The number of samples. /// Fourier Transform Convention Options. public static void InverseReal(float[] data, int n, FourierOptions options = FourierOptions.Default) { int length = n.IsEven() ? n + 2 : n + 1; if (data.Length < length) { throw new ArgumentException($"The given array is too small. It must be at least {length} long."); } if ((options & FourierOptions.InverseExponent) == FourierOptions.InverseExponent) { throw new NotSupportedException(); } switch (options) { case FourierOptions.NoScaling: FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.NoScaling); break; case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.BackwardScaling); break; default: FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.SymmetricScaling); break; } } /// /// Packed Real-Complex inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors. /// Since for real-valued time samples the complex spectrum is conjugate-even (symmetry), /// the spectrum can be fully reconstructed form the positive frequencies only (first half). /// The data array needs to be N+2 (if N is even) or N+1 (if N is odd) long in order to support such a packed spectrum. /// /// Data array of length N+2 (if N is even) or N+1 (if N is odd). /// The number of samples. /// Fourier Transform Convention Options. public static void InverseReal(double[] data, int n, FourierOptions options = FourierOptions.Default) { int length = n.IsEven() ? n + 2 : n + 1; if (data.Length < length) { throw new ArgumentException($"The given array is too small. It must be at least {length} long."); } if ((options & FourierOptions.InverseExponent) == FourierOptions.InverseExponent) { throw new NotSupportedException(); } switch (options) { case FourierOptions.NoScaling: FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.NoScaling); break; case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.BackwardScaling); break; default: FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the inverse Fast Fourier Transform (iFFT) to multiple dimensional sample data. /// /// Spectrum data, where the iFFT is evaluated in place. /// /// The data size per dimension. The first dimension is the major one. /// For example, with two dimensions "rows" and "columns" the samples are assumed to be organized row by row. /// /// Fourier Transform Convention Options. public static void InverseMultiDim(Complex32[] spectrum, int[] dimensions, FourierOptions options = FourierOptions.Default) { switch (options) { case FourierOptions.NoScaling: FourierTransformControl.Provider.BackwardMultidim(spectrum, dimensions, FourierTransformScaling.NoScaling); break; case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.BackwardMultidim(spectrum, dimensions, FourierTransformScaling.BackwardScaling); break; case FourierOptions.InverseExponent: FourierTransformControl.Provider.ForwardMultidim(spectrum, dimensions, FourierTransformScaling.SymmetricScaling); break; case FourierOptions.InverseExponent | FourierOptions.NoScaling: FourierTransformControl.Provider.ForwardMultidim(spectrum, dimensions, FourierTransformScaling.NoScaling); break; case FourierOptions.InverseExponent | FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.ForwardMultidim(spectrum, dimensions, FourierTransformScaling.ForwardScaling); break; default: FourierTransformControl.Provider.BackwardMultidim(spectrum, dimensions, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the inverse Fast Fourier Transform (iFFT) to multiple dimensional sample data. /// /// Spectrum data, where the iFFT is evaluated in place. /// /// The data size per dimension. The first dimension is the major one. /// For example, with two dimensions "rows" and "columns" the samples are assumed to be organized row by row. /// /// Fourier Transform Convention Options. public static void InverseMultiDim(Complex[] spectrum, int[] dimensions, FourierOptions options = FourierOptions.Default) { switch (options) { case FourierOptions.NoScaling: FourierTransformControl.Provider.BackwardMultidim(spectrum, dimensions, FourierTransformScaling.NoScaling); break; case FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.BackwardMultidim(spectrum, dimensions, FourierTransformScaling.BackwardScaling); break; case FourierOptions.InverseExponent: FourierTransformControl.Provider.ForwardMultidim(spectrum, dimensions, FourierTransformScaling.SymmetricScaling); break; case FourierOptions.InverseExponent | FourierOptions.NoScaling: FourierTransformControl.Provider.ForwardMultidim(spectrum, dimensions, FourierTransformScaling.NoScaling); break; case FourierOptions.InverseExponent | FourierOptions.AsymmetricScaling: FourierTransformControl.Provider.ForwardMultidim(spectrum, dimensions, FourierTransformScaling.ForwardScaling); break; default: FourierTransformControl.Provider.BackwardMultidim(spectrum, dimensions, FourierTransformScaling.SymmetricScaling); break; } } /// /// Applies the inverse Fast Fourier Transform (iFFT) to two dimensional sample data. /// /// Sample data, organized row by row, where the iFFT is evaluated in place /// The number of rows. /// The number of columns. /// Data available organized column by column instead of row by row can be processed directly by swapping the rows and columns arguments. /// Fourier Transform Convention Options. public static void Inverse2D(Complex32[] spectrumRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default) { InverseMultiDim(spectrumRowWise, new[] { rows, columns }, options); } /// /// Applies the inverse Fast Fourier Transform (iFFT) to two dimensional sample data. /// /// Sample data, organized row by row, where the iFFT is evaluated in place /// The number of rows. /// The number of columns. /// Data available organized column by column instead of row by row can be processed directly by swapping the rows and columns arguments. /// Fourier Transform Convention Options. public static void Inverse2D(Complex[] spectrumRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default) { InverseMultiDim(spectrumRowWise, new[] { rows, columns }, options); } /// /// Applies the inverse Fast Fourier Transform (iFFT) to a two dimensional data in form of a matrix. /// /// Sample matrix, where the iFFT is evaluated in place /// Fourier Transform Convention Options. public static void Inverse2D(Matrix spectrum, FourierOptions options = FourierOptions.Default) { var rowMajorArray = spectrum.AsRowMajorArray(); if (rowMajorArray != null) { InverseMultiDim(rowMajorArray, new[] { spectrum.RowCount, spectrum.ColumnCount }, options); return; } var columnMajorArray = spectrum.AsColumnMajorArray(); if (columnMajorArray != null) { InverseMultiDim(columnMajorArray, new[] { spectrum.ColumnCount, spectrum.RowCount }, options); return; } // Fall Back columnMajorArray = spectrum.ToColumnMajorArray(); InverseMultiDim(columnMajorArray, new[] { spectrum.ColumnCount, spectrum.RowCount }, options); var denseStorage = new DenseColumnMajorMatrixStorage(spectrum.RowCount, spectrum.ColumnCount, columnMajorArray); denseStorage.CopyToUnchecked(spectrum.Storage, ExistingData.Clear); } /// /// Applies the inverse Fast Fourier Transform (iFFT) to a two dimensional data in form of a matrix. /// /// Sample matrix, where the iFFT is evaluated in place /// Fourier Transform Convention Options. public static void Inverse2D(Matrix spectrum, FourierOptions options = FourierOptions.Default) { var rowMajorArray = spectrum.AsRowMajorArray(); if (rowMajorArray != null) { InverseMultiDim(rowMajorArray, new[] { spectrum.RowCount, spectrum.ColumnCount }, options); return; } var columnMajorArray = spectrum.AsColumnMajorArray(); if (columnMajorArray != null) { InverseMultiDim(columnMajorArray, new[] { spectrum.ColumnCount, spectrum.RowCount }, options); return; } // Fall Back columnMajorArray = spectrum.ToColumnMajorArray(); InverseMultiDim(columnMajorArray, new[] { spectrum.ColumnCount, spectrum.RowCount }, options); var denseStorage = new DenseColumnMajorMatrixStorage(spectrum.RowCount, spectrum.ColumnCount, columnMajorArray); denseStorage.CopyToUnchecked(spectrum.Storage, ExistingData.Clear); } /// /// Naive forward DFT, useful e.g. to verify faster algorithms. /// /// Time-space sample vector. /// Fourier Transform Convention Options. /// Corresponding frequency-space vector. [Obsolete("Use Forward instead. Will be dropped in version 5.0 and behave like Forward until then.")] public static Complex32[] NaiveForward(Complex32[] samples, FourierOptions options = FourierOptions.Default) { var result = new Complex32[samples.Length]; samples.Copy(result); Forward(result, options); return result; } /// /// Naive forward DFT, useful e.g. to verify faster algorithms. /// /// Time-space sample vector. /// Fourier Transform Convention Options. /// Corresponding frequency-space vector. [Obsolete("Use Forward instead. Will be dropped in version 5.0 and behave like Forward until then.")] public static Complex[] NaiveForward(Complex[] samples, FourierOptions options = FourierOptions.Default) { var result = new Complex[samples.Length]; samples.Copy(result); Forward(result, options); return result; } /// /// Naive inverse DFT, useful e.g. to verify faster algorithms. /// /// Frequency-space sample vector. /// Fourier Transform Convention Options. /// Corresponding time-space vector. [Obsolete("Use Inverse instead. Will be dropped in version 5.0 and behave like Inverse until then.")] public static Complex32[] NaiveInverse(Complex32[] spectrum, FourierOptions options = FourierOptions.Default) { var result = new Complex32[spectrum.Length]; spectrum.Copy(result); Inverse(result, options); return result; } /// /// Naive inverse DFT, useful e.g. to verify faster algorithms. /// /// Frequency-space sample vector. /// Fourier Transform Convention Options. /// Corresponding time-space vector. [Obsolete("Use Inverse instead. Will be dropped in version 5.0 and behave like Inverse until then.")] public static Complex[] NaiveInverse(Complex[] spectrum, FourierOptions options = FourierOptions.Default) { var result = new Complex[spectrum.Length]; spectrum.Copy(result); Inverse(result, options); return result; } /// /// Radix-2 forward FFT for power-of-two sized sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. /// [Obsolete("Use Forward instead. Will be dropped in version 5.0 and behave like Forward until then.")] public static void Radix2Forward(Complex32[] samples, FourierOptions options = FourierOptions.Default) { Forward(samples, options); } /// /// Radix-2 forward FFT for power-of-two sized sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. /// [Obsolete("Use Forward instead. Will be dropped in version 5.0 and behave like Forward until then.")] public static void Radix2Forward(Complex[] samples, FourierOptions options = FourierOptions.Default) { Forward(samples, options); } /// /// Radix-2 inverse FFT for power-of-two sized sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. /// [Obsolete("Use Inverse instead. Will be dropped in version 5.0 and behave like Inverse until then.")] public static void Radix2Inverse(Complex32[] spectrum, FourierOptions options = FourierOptions.Default) { Inverse(spectrum, options); } /// /// Radix-2 inverse FFT for power-of-two sized sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. /// [Obsolete("Use Inverse instead. Will be dropped in version 5.0 and behave like Inverse until then.")] public static void Radix2Inverse(Complex[] spectrum, FourierOptions options = FourierOptions.Default) { Inverse(spectrum, options); } /// /// Bluestein forward FFT for arbitrary sized sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. [Obsolete("Use Forward instead. Will be dropped in version 5.0 and behave like Forward until then.")] public static void BluesteinForward(Complex32[] samples, FourierOptions options = FourierOptions.Default) { Forward(samples, options); } /// /// Bluestein forward FFT for arbitrary sized sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. [Obsolete("Use Forward instead. Will be dropped in version 5.0 and behave like Forward until then.")] public static void BluesteinForward(Complex[] samples, FourierOptions options = FourierOptions.Default) { Forward(samples, options); } /// /// Bluestein inverse FFT for arbitrary sized sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. [Obsolete("Use Inverse instead. Will be dropped in version 5.0 and behave like Inverse until then.")] public static void BluesteinInverse(Complex32[] spectrum, FourierOptions options = FourierOptions.Default) { Inverse(spectrum, options); } /// /// Bluestein inverse FFT for arbitrary sized sample vectors. /// /// Sample vector, where the FFT is evaluated in place. /// Fourier Transform Convention Options. [Obsolete("Use Inverse instead. Will be dropped in version 5.0 and behave like Inverse until then.")] public static void BluesteinInverse(Complex[] spectrum, FourierOptions options = FourierOptions.Default) { Inverse(spectrum, options); } /// /// Generate the frequencies corresponding to each index in frequency space. /// The frequency space has a resolution of sampleRate/N. /// Index 0 corresponds to the DC part, the following indices correspond to /// the positive frequencies up to the Nyquist frequency (sampleRate/2), /// followed by the negative frequencies wrapped around. /// /// Number of samples. /// The sampling rate of the time-space data. public static double[] FrequencyScale(int length, double sampleRate) { double[] scale = new double[length]; double f = 0, step = sampleRate / length; int secondHalf = (length >> 1) + 1; for (int i = 0; i < secondHalf; i++) { scale[i] = f; f += step; } f = -step * (secondHalf - 2); for (int i = secondHalf; i < length; i++) { scale[i] = f; f += step; } return scale; } } }