public void TestExtendAntisymmetricWholePoint() { var points = new double[] { 2, 6, 1, 6, 5, 4, 5, 3, 4 }; var extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.AntisymmetricWholePoint, 3); var expected = new double[] { -2, 3, -2, 2, 6, 1, 6, 5, 4, 5, 3, 4, 5, 3, 4 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2, 6, 1 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.AntisymmetricWholePoint, 1); expected = new double[] { -2, 2, 6, 1, -4 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.AntisymmetricWholePoint, 1); expected = new double[] { -2, 2, -2 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.AntisymmetricWholePoint, 1); expected = new double[] { 0, 0 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2, 6, 1, 6, 5, 0 }; SignalExtension.Extend(points, SignalExtension.ExtensionMode.AntisymmetricWholePoint, 8); }
private static Signal RescaleSignal(Signal decomposition, Signal signal, DecompositionLevel level) { var rescaled = WaveMath.InterpolateCubic(decomposition, (uint)Math.Pow(2, level.Index + 1)); if (rescaled.SamplesCount < signal.SamplesCount) { var extensionSize = signal.SamplesCount - rescaled.SamplesCount; int right; var left = right = extensionSize / 2; if (left + right != extensionSize) { left++; } SignalExtension.Extend(ref rescaled, SignalExtension.ExtensionMode.SmoothPadding0, left, right); } else { rescaled.Samples = SignalExtension.Deextend(rescaled.Samples, signal.SamplesCount); } rescaled.CustomPlot = null; rescaled.Finish = signal.Finish; rescaled.SamplingRate = signal.SamplingRate; rescaled.SamplingInterval = signal.SamplingInterval; return(rescaled); }
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> /// Multilevel 1-D Discreete Wavelet Transform /// </summary> /// <param name="signal">The signal. Example: new Signal(5, 6, 7, 8, 1, 2, 3, 4)</param> /// <param name="motherWavelet">The mother wavelet to be used. Example: CommonMotherWavelets.GetWaveletFromName("DB4")</param> /// <param name="level">The depth-level to perform the DWT</param> /// <param name="extensionMode">Signal extension mode</param> /// <param name="convolutionMode">Defines what convolution function should be used</param> /// <returns></returns> public static List <DecompositionLevel> ExecuteDWT(Signal signal, MotherWavelet motherWavelet, int level, SignalExtension.ExtensionMode extensionMode = SignalExtension.ExtensionMode.SymmetricHalfPoint, ConvolutionModeEnum convolutionMode = ConvolutionModeEnum.ManagedFFT) { var levels = new List <DecompositionLevel>(); var approximation = (double[])signal.Samples.Clone(); var details = (double[])signal.Samples.Clone(); var realLength = signal.Samples.Length; for (var i = 1; i <= level; i++) { var extensionSize = motherWavelet.Filters.DecompositionLowPassFilter.Length - 1; approximation = SignalExtension.Extend(approximation, extensionMode, extensionSize); details = SignalExtension.Extend(details, extensionMode, extensionSize); approximation = WaveMath.Convolve(convolutionMode, approximation, motherWavelet.Filters.DecompositionLowPassFilter); approximation = WaveMath.DownSample(approximation); details = WaveMath.Convolve(convolutionMode, details, motherWavelet.Filters.DecompositionHighPassFilter); details = WaveMath.DownSample(details); realLength = realLength / 2; levels.Add(new DecompositionLevel { Signal = signal, Index = i - 1, Approximation = approximation, Details = details }); details = (double[])approximation.Clone(); } return(levels); }
/// <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; } }
public void TestDeextend() { var points = new double[] { 2, 6, 1, 6, 5, 0, 4, 5, 3, 4 }; var extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SymmetricHalfPoint, 100); var deextended = SignalExtension.Deextend(extended, points.Length); Assert.IsTrue(deextended.SequenceEqual(points)); points = new double[] { 1, 2 }; deextended = SignalExtension.Deextend(points, 1); Assert.IsTrue(deextended.SequenceEqual(points)); }
public void TestExtendAntisymmetricHalfPoint() { var points = new double[] { 2, 6, 1, 6, 5, 0, 4, 5, 3, 4 }; var extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.AntisymmetricHalfPoint, 3); var expected = new double[] { -1, -6, -2, 2, 6, 1, 6, 5, 0, 4, 5, 3, 4, -4, -3, -5 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2, 6, 1 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.AntisymmetricHalfPoint, 1); expected = new double[] { -2, 2, 6, 1, -1 }; Assert.IsTrue(extended.SequenceEqual(expected)); }
public void TestExtendSmoothPadding0() { var points = new double[] { 2, 6, 1, 6, 5, 0, 4, 5, 3, 4 }; var extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SmoothPadding0, 3); var expected = new double[] { 2, 2, 2, 2, 6, 1, 6, 5, 0, 4, 5, 3, 4, 4, 4, 4 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2, 6, 1 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SmoothPadding0, 1); expected = new double[] { 2, 2, 6, 1, 1 }; Assert.IsTrue(extended.SequenceEqual(expected)); }
public void TestExtendZeroPadding() { var points = new double[] { 2, 6, 1, 6, 5, 0, 4, 5, 3, 4 }; var extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.ZeroPadding, 3); var expected = new double[] { 0, 0, 0, 2, 6, 1, 6, 5, 0, 4, 5, 3, 4, 0, 0, 0 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2, 6, 1 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.ZeroPadding, 1); expected = new double[] { 0, 2, 6, 1, 0 }; Assert.IsTrue(extended.SequenceEqual(expected)); }
public void TestExtendSignal() { var points = new double[] { 2, 6, 1, 6, 5, 0, 4, 5, 3, 4 }; var signal = new Signal(points) { Start = 0, Finish = 9, SamplingRate = 1, SamplingInterval = 1 }; var expected = new double[] { 2, 2, 2, 2, 6, 1, 6, 5, 0, 4, 5, 3, 4, 4, 4, 4 }; SignalExtension.Extend(ref signal, SignalExtension.ExtensionMode.SmoothPadding0, 3, 3); Assert.IsTrue(signal.Samples.SequenceEqual(expected)); Assert.AreEqual(-3, signal.Start); Assert.AreEqual(12, signal.Finish); }
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); }
public void TestExtendSymmetricHalfPoint() { var points = new double[] { 2, 6, 1, 6, 5, 0, 4, 5, 3, 4 }; var extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SymmetricHalfPoint, 3); var expected = new double[] { 1, 6, 2, 2, 6, 1, 6, 5, 0, 4, 5, 3, 4, 4, 3, 5 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2, 6, 1 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SymmetricHalfPoint, 1); expected = new double[] { 2, 2, 6, 1, 1 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2, 6, 1, 6, 5, 0 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SymmetricHalfPoint, 1); expected = new double[] { 2, 2, 6, 1, 6, 5, 0, 0 }; Assert.IsTrue(extended.SequenceEqual(expected)); }
public void TestExtendSmoothPadding1() { var points = new double[] { 2, 6, 1, 6, 5, 0, 4, 5, 3, 4 }; var extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SmoothPadding1, 3); var expected = new double[] { -10, -6, -2, 2, 6, 1, 6, 5, 0, 4, 5, 3, 4, 5, 6, 7 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 7, 6, 1, 6, 5, 0, 4, 5, 3, 1 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SmoothPadding1, 3); expected = new double[] { 10, 9, 8, 7, 6, 1, 6, 5, 0, 4, 5, 3, 1, -1, -3, -5 }; Assert.IsTrue(extended.SequenceEqual(expected)); points = new double[] { 2, 6, 1 }; extended = SignalExtension.Extend(points, SignalExtension.ExtensionMode.SmoothPadding1, 1); expected = new double[] { -2, 2, 6, 1, -4 }; Assert.IsTrue(extended.SequenceEqual(expected)); }
/// <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> /// Multilevel inverse discrete 1-D wavelet transform /// </summary> /// <param name="decompositionLevels">The decomposition levels of the DWT</param> /// <param name="motherWavelet">The mother wavelet to be used. Example: CommonMotherWavelets.GetWaveletFromName("DB4") </param> /// <param name="level">The depth-level to perform the DWT</param> /// <param name="convolutionMode">Defines what convolution function should be used</param> /// <returns></returns> public static double[] ExecuteIDWT(List <DecompositionLevel> decompositionLevels, MotherWavelet motherWavelet, int level = 0, ConvolutionModeEnum convolutionMode = ConvolutionModeEnum.ManagedFFT) { if (level == 0 || level > decompositionLevels.Count) { level = decompositionLevels.Count; } if (level <= 0) { return(null); } var approximation = (double[])decompositionLevels[level - 1].Approximation.Clone(); var details = (double[])decompositionLevels[level - 1].Details.Clone(); for (var i = level - 1; i >= 0; i--) { approximation = WaveMath.UpSample(approximation); approximation = WaveMath.Convolve(convolutionMode, approximation, motherWavelet.Filters.ReconstructionLowPassFilter, true, -1); details = WaveMath.UpSample(details); details = WaveMath.Convolve(convolutionMode, details, motherWavelet.Filters.ReconstructionHighPassFilter, true, -1); //sum approximation with details approximation = WaveMath.Add(approximation, details); if (i <= 0) { continue; } if (approximation.Length > decompositionLevels[i - 1].Details.Length) { approximation = SignalExtension.Deextend(approximation, decompositionLevels[i - 1].Details.Length); } details = (double[])decompositionLevels[i - 1].Details.Clone(); } return(approximation); }
/// <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); }