/// <summary> /// Fast convolution via FFT for general complex-valued case /// </summary> /// <param name="signal"></param> /// <param name="kernel"></param> /// <returns></returns> public ComplexDiscreteSignal Convolve(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel) { var length = signal.Length + kernel.Length - 1; var fftSize = MathUtils.NextPowerOfTwo(length); var fft = new Fft64(fftSize); signal = signal.ZeroPadded(fftSize); kernel = kernel.ZeroPadded(fftSize); // 1) do FFT of both signals fft.Direct(signal.Real, signal.Imag); fft.Direct(kernel.Real, kernel.Imag); // 2) do complex multiplication of spectra var spectrum = signal.Multiply(kernel); // 3) do inverse FFT of resulting spectrum fft.Inverse(spectrum.Real, spectrum.Imag); // 3a) normalize for (var i = 0; i < spectrum.Length; i++) { spectrum.Real[i] /= fftSize; spectrum.Imag[i] /= fftSize; } // 4) return resulting meaningful part of the signal (truncate size to N + M - 1) return(new ComplexDiscreteSignal(signal.SamplingRate, spectrum.Real, spectrum.Imag).First(length)); }
/// <summary> /// Fast deconvolution via FFT for general complex-valued case. /// /// NOTE! /// /// Deconvolution is an experimental feature. /// It's problematic due to division by zero. /// /// </summary> /// <param name="signal"></param> /// <param name="kernel"></param> /// <param name="fftSize"></param> /// <returns></returns> public ComplexDiscreteSignal Deconvolve(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel, int fftSize = 0) { // first, try to divide polynomials var div = MathUtils.DividePolynomial(signal.Real.Zip(signal.Imag, (r, i) => new Complex(r, i)).ToArray(), kernel.Real.Zip(kernel.Imag, (r, i) => new Complex(r, i)).ToArray()); var quotient = div[0]; var remainder = div[1]; if (remainder.All(d => Math.Abs(d.Real) < 1e-10) && remainder.All(d => Math.Abs(d.Imaginary) < 1e-10)) { return(new ComplexDiscreteSignal(signal.SamplingRate, quotient.Select(q => q.Real), quotient.Select(q => q.Imaginary))); } // ... deconvolve via FFT var length = signal.Length - kernel.Length + 1; if (fftSize == 0) { fftSize = MathUtils.NextPowerOfTwo(signal.Length); } var fft = new Fft64(fftSize); signal = signal.ZeroPadded(fftSize); kernel = kernel.ZeroPadded(fftSize); // 1) do FFT of both signals fft.Direct(signal.Real, signal.Imag); fft.Direct(kernel.Real, kernel.Imag); for (var i = 0; i < fftSize; i++) { signal.Real[i] += 1e-10; signal.Imag[i] += 1e-10; kernel.Real[i] += 1e-10; kernel.Imag[i] += 1e-10; } // 2) do complex division of spectra var spectrum = signal.Divide(kernel); // 3) do inverse FFT of resulting spectrum fft.Inverse(spectrum.Real, spectrum.Imag); // 4) return resulting meaningful part of the signal (truncate to N - M + 1) return(new ComplexDiscreteSignal(signal.SamplingRate, spectrum.Real.FastCopyFragment(length), spectrum.Imag.FastCopyFragment(length))); }
/// <summary> /// FIR filter design using frequency sampling method /// </summary> /// <param name="order">Filter order</param> /// <param name="magnitudeResponse">Magnitude response</param> /// <param name="phaseResponse">Phase response</param> /// <param name="window">Window</param> /// <returns>FIR filter kernel</returns> public static double[] Fir(int order, double[] magnitudeResponse, double[] phaseResponse = null, WindowTypes window = WindowTypes.Blackman) { Guard.AgainstEvenNumber(order, "The order of the filter"); var fftSize = MathUtils.NextPowerOfTwo(magnitudeResponse.Length); var real = phaseResponse == null? magnitudeResponse.PadZeros(fftSize) : magnitudeResponse.Zip(phaseResponse, (m, p) => m * Math.Cos(p)).ToArray(); var imag = phaseResponse == null ? new double[fftSize] : magnitudeResponse.Zip(phaseResponse, (m, p) => m * Math.Sin(p)).ToArray(); var fft = new Fft64(fftSize); fft.Inverse(real, imag); var kernel = new double[order]; var compensation = 2.0 / fftSize; var middle = order / 2; for (var i = 0; i <= middle; i++) { kernel[i] = real[middle - i] * compensation; kernel[i + middle] = real[i] * compensation; } kernel.ApplyWindow(window); return(kernel); }
/// <summary> /// Method for FIR filter design using window method /// </summary> /// <param name="order"></param> /// <param name="magnitudeResponse"></param> /// <param name="phaseResponse"></param> /// <param name="window"></param> /// <returns></returns> public static FirFilter Fir(int order, double[] magnitudeResponse, double[] phaseResponse = null, WindowTypes window = WindowTypes.Blackman) { if (order % 2 == 0) { throw new ArgumentException("The order of a filter must be an odd number!"); } var fftSize = MathUtils.NextPowerOfTwo(magnitudeResponse.Length); var real = phaseResponse == null? magnitudeResponse.PadZeros(fftSize) : magnitudeResponse.Zip(phaseResponse, (m, p) => m * Math.Cos(p)).ToArray(); var imag = phaseResponse == null ? new double[fftSize] : magnitudeResponse.Zip(phaseResponse, (m, p) => m * Math.Sin(p)).ToArray(); var fft = new Fft64(fftSize); fft.Inverse(real, imag); var kernel = new double[order]; var compensation = 2.0 / fftSize; var middle = order / 2; for (var i = 0; i <= middle; i++) { kernel[i] = real[middle - i] * compensation; kernel[i + middle] = real[i] * compensation; } kernel.ApplyWindow(window); return(new FirFilter(kernel)); }