/// <summary> /// FIR filter design using frequency sampling method (like firwin2 in sciPy). /// /// This method doesn't do any interpolation of the magnitude response, /// so you need to take care of it before calling the method. /// Usage example: /// /// var zeros = Enumerable.Repeat(0.0, 80); /// var ones1 = Enumerable.Repeat(1.0, 80); /// var ones2 = Enumerable.Repeat(1.0, 40); /// var magnitudes = ones1.Concat(zeros).Concat(ones2).ToArray(); /// // 80 ones + 80 zeros + 40 ones = 200 magnitude values /// // 56 zero magnitude values will be added for closest power of two (256) /// /// var kernel = DesignFilter.Fir(101, magnitudes); /// /// var filter = new FirFilter(kernel); /// /// </summary> /// <param name="order">Filter order</param> /// <param name="magnitudeResponse">Magnitude response</param> /// <param name="window">Window</param> /// <returns>FIR filter kernel</returns> public static double[] Fir(int order, double[] magnitudeResponse, WindowTypes window = WindowTypes.Blackman) { // 2x because we reserve space for symmetric part var fftSize = 2 * MathUtils.NextPowerOfTwo(magnitudeResponse.Length); var complexResponse = new Complex[fftSize]; for (var i = 0; i < magnitudeResponse.Length; i++) { complexResponse[i] = magnitudeResponse[i] * Complex.Exp(new Complex(0, -(order - 1) / 2.0 * 2 * Math.PI * i / fftSize)); } var real = complexResponse.Select(c => c.Real).ToArray(); var imag = complexResponse.Select(c => c.Imaginary).ToArray(); var kernel = new double[fftSize]; var fft = new RealFft64(fftSize); fft.Inverse(real, imag, real); kernel = real.Take(order).Select(s => s / fftSize).ToArray(); kernel.ApplyWindow(window); return(kernel); }
public FftArrayVsSpan() { _fft = new RealFft(FrameSize); _fft64 = new RealFft64(FrameSize); _complexFft = new Fft(FrameSize); _samples = new WhiteNoiseBuilder().OfLength(N).Build().Samples; _samples64 = _samples.ToDoubles(); }
/// <summary> /// Constructor /// </summary> /// <param name="kernel"></param> /// <param name="fftSize"></param> public OlaBlockConvolver64(IEnumerable <double> kernel, int fftSize) { _kernel = kernel.ToArray(); _fftSize = MathUtils.NextPowerOfTwo(fftSize); Guard.AgainstExceedance(_kernel.Length, _fftSize, "Kernel length", "the size of FFT"); _fft = new RealFft64(_fftSize); _kernelSpectrumRe = _kernel.PadZeros(_fftSize); _kernelSpectrumIm = new double[_fftSize]; _convRe = new double[_fftSize]; _convIm = new double[_fftSize]; _blockRe = new double[_fftSize]; _blockIm = new double[_fftSize]; _lastSaved = new double[_kernel.Length - 1]; _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); Reset(); }
/// <summary> /// <para> /// Designs FIR filter using frequency sampling method /// (works identical to firwin2 in sciPy and fir2 in MATLAB). /// </para> /// <para> /// Note. By default, the FFT size is auto-computed. /// If it is set explicitly, then (fftSize/2 + 1) must exceed the filter order. /// </para> /// <para> /// Note. Array of frequencies can be null. /// In this case the <paramref name="fftSize"/> must be provided and size of gains array must be fftSize/2 + 1. /// Frequencies will be uniformly sampled on range [0..0.5]. /// </para> /// </summary> /// <param name="order">Filter order</param> /// <param name="frequencies">Frequencies (frequency sampling points), each in range [0..0.5]</param> /// <param name="gain">Filter gains at the frequency sampling points</param> /// <param name="fftSize">FFT size</param> /// <param name="window">Window</param> public static double[] Fir(int order, double[] frequencies, double[] gain, int fftSize = 0, WindowType window = WindowType.Hamming) { if (fftSize == 0) { fftSize = 2 * MathUtils.NextPowerOfTwo(order); } var freqCount = fftSize / 2 + 1; if (frequencies is null) { frequencies = Enumerable.Range(0, freqCount) .Select(i => (double)i / fftSize) .ToArray(); } if (order >= freqCount) { throw new ArgumentException($"Given that filter order is {order} the FFT size must be at least {2 * MathUtils.NextPowerOfTwo(order)}"); } Guard.AgainstInequality(frequencies.Length, gain.Length, "Length of frequencies array", "length of gain array"); Guard.AgainstNotOrdered(frequencies, "Array of frequencies"); // linear interpolation var step = 1.0 / fftSize; var grid = Enumerable.Range(0, freqCount) .Select(f => f * step) .ToArray(); var response = new double[grid.Length]; var x = frequencies; var y = gain; var left = 0; var right = 1; for (var i = 0; i < grid.Length; i++) { while (grid[i] > x[right] && right < x.Length - 1) { right++; left++; } response[i] = y[left] + (y[right] - y[left]) * (grid[i] - x[left]) / (x[right] - x[left]); } // prepare complex frequency response var complexResponse = new Complex[fftSize]; for (var i = 0; i < response.Length; i++) { complexResponse[i] = response[i] * Complex.Exp(new Complex(0, -(order - 1) / 2.0 * 2 * Math.PI * i / fftSize)); } var real = complexResponse.Select(c => c.Real).ToArray(); var imag = complexResponse.Select(c => c.Imaginary).ToArray(); // IFFT var fft = new RealFft64(fftSize); fft.Inverse(real, imag, real); var kernel = real.Take(order).Select(s => s / fftSize).ToArray(); kernel.ApplyWindow(window); return(kernel); }