Пример #1
0
        /// <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);
        }
Пример #2
0
 public FftArrayVsSpan()
 {
     _fft        = new RealFft(FrameSize);
     _fft64      = new RealFft64(FrameSize);
     _complexFft = new Fft(FrameSize);
     _samples    = new WhiteNoiseBuilder().OfLength(N).Build().Samples;
     _samples64  = _samples.ToDoubles();
 }
Пример #3
0
        /// <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();
        }
Пример #4
0
        /// <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);
        }