/// <summary> /// Get the range of element lengths /// </summary> /// <param name = "array"></param> /// <param name = "minimum"></param> /// <param name = "maximum"></param> public static void GetLengthRange(ComplexF[] array, ref float minimum, ref float maximum) { minimum = +float.MaxValue; maximum = -float.MaxValue; for (int i = 0; i < array.Length; i++) { float temp = array[i].GetModulus(); minimum = Math.Min(temp, minimum); maximum = Math.Max(temp, maximum); } }
/// <summary> /// Invert each element in the array /// </summary> /// <param name = "array"></param> public static void Invert(ComplexF[] array) { for (int i = 0; i < array.Length; i++) { array[i] = ((ComplexF) 1)/array[i]; } }
//--------------------------------------------------------------------------------------------- private static void LockWorkspaceF(int length, ref ComplexF[] workspace) { Debug.Assert(_workspaceFLocked == false); _workspaceFLocked = true; if (length >= _workspaceF.Length) { _workspaceF = new ComplexF[length]; } workspace = _workspaceF; }
/// <summary> /// Create a complex number based on an existing complex number /// </summary> /// <param name = "c"></param> public ComplexF(ComplexF c) { Re = c.Re; Im = c.Im; }
/// <summary> /// Add a specific value to each element in the array /// </summary> /// <param name = "array"></param> /// <param name = "offset"></param> public static void Offset(ComplexF[] array, ComplexF offset) { int length = array.Length; for (int i = 0; i < length; i++) { array[i] += offset; } }
/// <summary> /// Multiply each element in the array by a specific value /// </summary> /// <param name = "array"></param> /// <param name = "scale"></param> /// <param name = "start"></param> /// <param name = "length"></param> public static void Scale(ComplexF[] array, ComplexF scale, int start, int length) { Debug.Assert(array != null); Debug.Assert(start >= 0); Debug.Assert(length >= 0); Debug.Assert((start + length) < array.Length); for (int i = 0; i < length; i++) { array[i + start] *= scale; } }
/// <summary> /// Compute a 3D fast fourier transform on a data set of complex numbers /// </summary> /// <param name = "data"></param> /// <param name = "xLength"></param> /// <param name = "yLength"></param> /// <param name = "zLength"></param> /// <param name = "direction"></param> public static void FFT3(ComplexF[] data, int xLength, int yLength, int zLength, FourierDirection direction) { if (data == null) { throw new ArgumentNullException("data"); } if (data.Length < xLength*yLength*zLength) { throw new ArgumentOutOfRangeException("data.Length", data.Length, "must be at least as large as 'xLength * yLength * zLength' parameter"); } if (IsPowerOf2(xLength) == false) { throw new ArgumentOutOfRangeException("xLength", xLength, "must be a power of 2"); } if (IsPowerOf2(yLength) == false) { throw new ArgumentOutOfRangeException("yLength", yLength, "must be a power of 2"); } if (IsPowerOf2(zLength) == false) { throw new ArgumentOutOfRangeException("zLength", zLength, "must be a power of 2"); } int xInc = 1; int yInc = xLength; int zInc = xLength*yLength; if (xLength > 1) { SyncLookupTableLength(xLength); for (int z = 0; z < zLength; z++) { for (int y = 0; y < yLength; y++) { int xStart = y*yInc + z*zInc; LinearFFT_Quick(data, xStart, xInc, xLength, direction); } } } if (yLength > 1) { SyncLookupTableLength(yLength); for (int z = 0; z < zLength; z++) { for (int x = 0; x < xLength; x++) { int yStart = z*zInc + x*xInc; LinearFFT_Quick(data, yStart, yInc, yLength, direction); } } } if (zLength > 1) { SyncLookupTableLength(zLength); for (int y = 0; y < yLength; y++) { for (int x = 0; x < xLength; x++) { int zStart = y*yInc + x*xInc; LinearFFT_Quick(data, zStart, zInc, zLength, direction); } } } }
/// <summary> /// Multiply each element in lhs array with corresponding element in rhs array and /// put product in result array /// </summary> /// <param name = "lhs"></param> /// <param name = "rhs"></param> /// <param name = "result"></param> public static void Multiply(ComplexF[] lhs, ComplexF[] rhs, ComplexF[] result) { Debug.Assert(lhs != null); Debug.Assert(rhs != null); Debug.Assert(result != null); Debug.Assert(lhs.Length == rhs.Length); Debug.Assert(lhs.Length == result.Length); int length = lhs.Length; for (int i = 0; i < length; i++) { result[i] = lhs[i]*rhs[i]; } }
private static void UnlockBufferCF(ref ComplexF[] buffer) { Debug.Assert(_bufferCF == buffer); Debug.Assert(_bufferCFLocked); _bufferCFLocked = false; buffer = null; }
/// <summary> /// Compute a 1D fast Fourier transform of a dataset of complex numbers. /// </summary> /// <param name = "data"></param> /// <param name = "direction"></param> public static void FFT(ComplexF[] data, FourierDirection direction) { if (data == null) { throw new ArgumentNullException("data"); } FFT(data, data.Length, direction); }
private static void Swap(ref ComplexF a, ref ComplexF b) { ComplexF temp = a; a = b; b = temp; }
private static void ReorderArray(ComplexF[] data) { Debug.Assert(data != null); int length = data.Length; Debug.Assert(IsPowerOf2(length)); Debug.Assert(length >= cMinLength); Debug.Assert(length <= cMaxLength); int[] reversedBits = GetReversedBits(Log2(length)); for (int i = 0; i < length; i++) { int swap = reversedBits[i]; if (swap > i) { ComplexF temp = data[i]; data[i] = data[swap]; data[swap] = temp; } } }
//====================================================================================== //====================================================================================== private static void LockBufferCF(int length, ref ComplexF[] buffer) { Debug.Assert(length >= 0); Debug.Assert(_bufferCFLocked == false); _bufferCFLocked = true; if (length != _bufferCF.Length) { _bufferCF = new ComplexF[length]; } buffer = _bufferCF; }
/// <summary> /// Determine whether the elements in the two arrays are the same /// </summary> /// <param name = "array1"></param> /// <param name = "array2"></param> /// <param name = "tolerance"></param> /// <returns></returns> public static bool IsEqual(ComplexF[] array1, ComplexF[] array2, float tolerance) { if (array1.Length != array2.Length) { return false; } for (int i = 0; i < array1.Length; i++) { if (ComplexF.IsEqual(array1[i], array2[i], tolerance) == false) { return false; } } return true; }
/// <summary> /// Compute a 1D fast Fourier transform of a dataset of complex numbers. /// </summary> /// <param name = "data"></param> /// <param name = "length"></param> /// <param name = "direction"></param> public static void FFT_Quick(ComplexF[] data, int length, FourierDirection direction) { /*if( data == null ) { throw new ArgumentNullException( "data" ); } if( data.Length < length ) { throw new ArgumentOutOfRangeException( "length", length, "must be at least as large as 'data.Length' parameter" ); } if( Fourier.IsPowerOf2( length ) == false ) { throw new ArgumentOutOfRangeException( "length", length, "must be a power of 2" ); } Fourier.SyncLookupTableLength( length );*/ int ln = Log2(length); // reorder array ReorderArray(data); // successive doubling int N = 1; int signIndex = (direction == FourierDirection.Forward) ? 0 : 1; for (int level = 1; level <= ln; level++) { int M = N; N <<= 1; float[] uRLookup = _uRLookupF[level, signIndex]; float[] uILookup = _uILookupF[level, signIndex]; for (int j = 0; j < M; j++) { float uR = uRLookup[j]; float uI = uILookup[j]; for (int even = j; even < length; even += N) { int odd = even + M; float r = data[odd].Re; float i = data[odd].Im; float odduR = r*uR - i*uI; float odduI = r*uI + i*uR; r = data[even].Re; i = data[even].Im; data[even].Re = r + odduR; data[even].Im = i + odduI; data[odd].Re = r - odduR; data[odd].Im = i - odduI; } } } }
/// <summary> /// Multiply each element in target array with corresponding element in rhs array /// </summary> /// <param name = "target"></param> /// <param name = "rhs"></param> public static void Multiply(ComplexF[] target, ComplexF[] rhs) { Multiply(target, rhs, target); }
private static void LinearFFT(ComplexF[] data, int start, int inc, int length, FourierDirection direction) { Debug.Assert(data != null); Debug.Assert(start >= 0); Debug.Assert(inc >= 1); Debug.Assert(length >= 1); Debug.Assert((start + inc*(length - 1)) < data.Length); // copy to buffer ComplexF[] buffer = null; LockBufferCF(length, ref buffer); int j = start; for (int i = 0; i < length; i++) { buffer[i] = data[j]; j += inc; } FFT(buffer, length, direction); // copy from buffer j = start; for (int i = 0; i < length; i++) { data[j] = buffer[i]; j += inc; } UnlockBufferCF(ref buffer); }
/// <summary> /// Scale and offset the elements in the array so that the /// overall range is [0, 1] /// </summary> /// <param name = "array"></param> public static void Normalize(ComplexF[] array) { float min = 0, max = 0; GetLengthRange(array, ref min, ref max); Scale(array, (1/(max - min))); Offset(array, (-min/(max - min))); }
/// <summary> /// Copy an array /// </summary> /// <param name = "dest"></param> /// <param name = "source"></param> public static void Copy(ComplexF[] dest, ComplexF[] source) { Debug.Assert(dest != null); Debug.Assert(source != null); Debug.Assert(dest.Length == source.Length); for (int i = 0; i < dest.Length; i++) { dest[i] = source[i]; } }
/// <summary> /// Multiply each element in the array by a specific value /// </summary> /// <param name = "array"></param> /// <param name = "scale"></param> public static void Scale(ComplexF[] array, ComplexF scale) { Debug.Assert(array != null); int length = array.Length; for (int i = 0; i < length; i++) { array[i] *= scale; } }
/// <summary> /// Divide each element in target array with corresponding element in rhs array /// </summary> /// <param name = "target"></param> /// <param name = "rhs"></param> public static void Divide(ComplexF[] target, ComplexF[] rhs) { Divide(target, rhs, target); }
/// <summary> /// Shift (offset) the elements in the array /// </summary> /// <param name = "array"></param> /// <param name = "offset"></param> public static void Shift(ComplexF[] array, int offset) { Debug.Assert(array != null); Debug.Assert(offset >= 0); Debug.Assert(offset < array.Length); if (offset == 0) { return; } int length = array.Length; ComplexF[] workspace = null; LockWorkspaceF(length, ref workspace); for (int i = 0; i < length; i++) { workspace[(i + offset)%length] = array[i]; } for (int i = 0; i < length; i++) { array[i] = workspace[i]; } UnlockWorkspaceF(ref workspace); }
/// <summary> /// Divide each element in lhs array with corresponding element in rhs array and /// put product in result array /// </summary> /// <param name = "lhs"></param> /// <param name = "rhs"></param> /// <param name = "result"></param> public static void Divide(ComplexF[] lhs, ComplexF[] rhs, ComplexF[] result) { Debug.Assert(lhs != null); Debug.Assert(rhs != null); Debug.Assert(result != null); Debug.Assert(lhs.Length == rhs.Length); Debug.Assert(lhs.Length == result.Length); ComplexF zero = ComplexF.Zero; int length = lhs.Length; for (int i = 0; i < length; i++) { if (rhs[i] != zero) { result[i] = lhs[i]/rhs[i]; } else { result[i] = zero; } } }
private static void UnlockWorkspaceF(ref ComplexF[] workspace) { Debug.Assert(_workspaceF == workspace); Debug.Assert(_workspaceFLocked); _workspaceFLocked = false; workspace = null; }
//----------------------------------------------------------------------------------- //----------------------------------------------------------------------------------- /// <summary> /// Determine whether two complex numbers are almost (i.e. within the tolerance) equivalent. /// </summary> /// <param name = "a"></param> /// <param name = "b"></param> /// <param name = "tolerance"></param> /// <returns></returns> public static bool IsEqual(ComplexF a, ComplexF b, float tolerance) { return (Math.Abs(a.Re - b.Re) < tolerance) && (Math.Abs(a.Im - b.Im) < tolerance); }