Example #1
0
        /// <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));
        }
Example #2
0
        /// <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)));
        }
Example #3
0
        /// <summary>
        /// Frequency response of an FIR filter is the FT of its impulse response
        /// </summary>
        public override ComplexDiscreteSignal FrequencyResponse(int length = 512)
        {
            var real = Kernel.PadZeros(length);
            var imag = new double[length];

            var fft = new Fft64(length);

            fft.Direct(real, imag);

            return(new ComplexDiscreteSignal(1, real.Take(length / 2 + 1),
                                             imag.Take(length / 2 + 1)));
        }
Example #4
0
        /// <summary>
        /// Returns the complex frequency response of a filter.
        ///
        /// Method calculates the Frequency Response of a filter
        /// by taking FFT of an impulse response (possibly truncated).
        /// </summary>
        /// <param name="length">Number of frequency response samples</param>
        public virtual ComplexDiscreteSignal FrequencyResponse(int length = 512)
        {
            var real = ImpulseResponse(length);
            var imag = new double[length];

            var fft = new Fft64(length);

            fft.Direct(real, imag);

            return(new ComplexDiscreteSignal(1, real.Take(length / 2 + 1),
                                             imag.Take(length / 2 + 1)));
        }
Example #5
0
        /// <summary>
        /// Evaluate frequency response
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public ComplexDiscreteSignal FrequencyResponse(int length = 512)
        {
            var ir = ImpulseResponse(length);

            var real = ir.Length == length ? ir :
                       ir.Length < length?ir.PadZeros(length) :
                           ir.FastCopyFragment(length);

            var imag = new double[length];

            var fft = new Fft64(length);

            fft.Direct(real, imag);

            return(new ComplexDiscreteSignal(1, real.Take(length / 2 + 1),
                                             imag.Take(length / 2 + 1)));
        }
Example #6
0
        /// <summary>
        /// Group delay calculated from TF coefficients
        /// </summary>
        public double[] GroupDelay(int fftSize = 512)
        {
            var cc = Operation.CrossCorrelate(new ComplexDiscreteSignal(1, Numerator),
                                              new ComplexDiscreteSignal(1, Denominator)).Real;

            var cr = Enumerable.Range(0, cc.Length)
                     .Zip(cc, (r, c) => r * c)
                     .ToArray();

            var re = cc.PadZeros(fftSize);
            var im = new double[fftSize];

            var fft = new Fft64(fftSize);

            fft.Direct(re, im);

            var rre = cr.PadZeros(fftSize);
            var rim = new double[fftSize];

            fft.Direct(rre, rim);

            var num = rre.Zip(rim, (r, i) => new Complex(r, i)).ToArray();
            var den = re.Zip(im, (r, i) => new Complex(r, i)).ToArray();

            var dn = Numerator.Length - 1;

            var gd = new double[fftSize / 2];

            for (var i = 1; i <= gd.Length; i++)
            {
                if (Complex.Abs(den[i]) < 1e-10)
                {
                    num[i] = Complex.Zero;
                    den[i] = Complex.One;
                }

                var t = dn - num[i] / den[i];
                gd[i - 1] = t.Real;
            }

            return(gd);
        }
Example #7
0
        /// <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);
        }
Example #8
0
        /// <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));
        }