Ejemplo n.º 1
0
        /// <summary>
        /// Fast cross-correlation via FFT
        /// </summary>
        /// <param name="signal1"></param>
        /// <param name="signal2"></param>
        /// <returns></returns>
        public static ComplexDiscreteSignal CrossCorrelate(ComplexDiscreteSignal signal1, ComplexDiscreteSignal signal2)
        {
            var reversedKernel =
                new ComplexDiscreteSignal(signal2.SamplingRate, signal2.Real.Reverse(), signal2.Imag.Reverse());

            return(Convolve(signal1, reversedKernel));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Compute complex analytic signal
        /// </summary>
        /// <param name="samples">Array of samples</param>
        /// <returns>Complex analytic signal</returns>
        public ComplexDiscreteSignal AnalyticSignal(double[] samples)
        {
            var analyticSignal = new ComplexDiscreteSignal(1, samples);

            var re = analyticSignal.Real;
            var im = analyticSignal.Imag;

            _fft.Direct(re, im);

            for (var i = 1; i < re.Length / 2; i++)
            {
                re[i] *= 2;
                im[i] *= 2;
            }

            for (var i = re.Length / 2 + 1; i < re.Length; i++)
            {
                re[i] = 0.0;
                im[i] = 0.0;
            }

            _fft.Inverse(re, im);

            return(analyticSignal);
        }
Ejemplo n.º 3
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));
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Fast cross-correlation via FFT
        /// </summary>
        /// <param name="signal"></param>
        /// <param name="kernel"></param>
        /// <returns></returns>
        public ComplexDiscreteSignal CrossCorrelate(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel)
        {
            var reversedKernel =
                new ComplexDiscreteSignal(kernel.SamplingRate, kernel.Real.Reverse(), kernel.Imag.Reverse());

            return(Convolve(signal, reversedKernel));
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Compute complex analytic signal, double precision
        /// </summary>
        /// <param name="samples">Array of samples</param>
        /// <param name="norm">Normalize by fft size</param>
        /// <returns>Complex analytic signal</returns>
        public ComplexDiscreteSignal AnalyticSignal(double[] samples, bool norm = true)
        {
            var analyticSignal = new ComplexDiscreteSignal(1, samples);

            var re = analyticSignal.Real;
            var im = analyticSignal.Imag;

            _fft64.Direct(re, im);

            for (var i = 1; i < re.Length / 2; i++)
            {
                re[i] *= 2;
                im[i] *= 2;
            }

            for (var i = re.Length / 2 + 1; i < re.Length; i++)
            {
                re[i] = 0.0;
                im[i] = 0.0;
            }

            _fft64.Inverse(re, im);

            if (norm)
            {
                analyticSignal.Attenuate(re.Length);
            }

            return(analyticSignal);
        }
Ejemplo n.º 6
0
 public void TestInitializeWithDifferentRealAndImagSizes()
 {
     Assert.Throws <ArgumentException>(() =>
     {
         var s = new ComplexDiscreteSignal(8000, new double[] { 1, 2 }, new double[] { 3 });
     });
 }
Ejemplo n.º 7
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)));
        }
Ejemplo n.º 8
0
 public void TestInitializeWithBadSamplingRate()
 {
     Assert.Multiple(() =>
     {
         Assert.Throws <ArgumentException>(() => { var s = new ComplexDiscreteSignal(0, new double[] { 1 }); });
         Assert.Throws <ArgumentException>(() => { var s = new ComplexDiscreteSignal(-8000, new double[] { 1 }); });
     });
 }
Ejemplo n.º 9
0
        /// <summary>
        /// Method for converting zeros(poles) to TF numerator(denominator)
        /// </summary>
        /// <param name="zp"></param>
        /// <returns></returns>
        public static double[] ZpToTf(ComplexDiscreteSignal zp)
        {
            var poly = new Complex[] { 1, new Complex(-zp.Real[0], -zp.Imag[0]) };

            for (var k = 1; k < zp.Length; k++)
            {
                var poly1 = new Complex[] { 1, new Complex(-zp.Real[k], -zp.Imag[k]) };
                poly = MathUtils.MultiplyPolynomials(poly, poly1);
            }

            return(poly.Select(p => p.Real).ToArray());
        }
Ejemplo n.º 10
0
        /// <summary>
        /// TF constructor from zeros/poles
        /// </summary>
        /// <param name="zeros">Zeros</param>
        /// <param name="poles">Poles</param>
        /// <param name="gain">Gain</param>
        public TransferFunction(ComplexDiscreteSignal zeros, ComplexDiscreteSignal poles, double gain = 1)
        {
            _zeros = zeros;
            _poles = poles;

            Denominator = poles != null?ZpToTf(poles) : new[] { 1.0 };
            Numerator   = zeros != null?ZpToTf(zeros) : new[] { 1.0 };

            for (var i = 0; i < Numerator.Length; i++)
            {
                Numerator[i] *= gain;
            }
        }
Ejemplo n.º 11
0
        public void TestImaginaryParts()
        {
            var s1 = new ComplexDiscreteSignal(1, new[] { 1, 0.5 }, new[] { 0, -1.5 });
            var s2 = new ComplexDiscreteSignal(1, new[] { 1, 0.5 }, new[] { 0, 1.5 });

            var conv = Operation.Convolve(s1, s2);

            Assert.Multiple(() =>
            {
                Assert.That(conv.Real, Is.EquivalentTo(new[] { 1, 1, 2.5 }));
                Assert.That(conv.Imag, Is.EqualTo(new[] { 0, 0, 0 }).Within(1e-6));
            });
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Convert Line Spectral Frequencies to LPC coefficients
        /// </summary>
        /// <param name="lsf">The length must be equal to lpc length. Last element must be PI</param>
        /// <param name="lpc"></param>
        public static void FromLsf(float[] lsf, float[] lpc)
        {
            var n         = lsf.Length - 1;
            var halfOrder = n / 2;

            var pPoles = new ComplexDiscreteSignal(1, n);
            var qPoles = new ComplexDiscreteSignal(1, n + 2 * (n % 2));

            var k = 0;

            for (var i = 0; k < halfOrder; i += 2, k++)
            {
                qPoles.Real[k] = Math.Cos(lsf[i]);
                qPoles.Imag[k] = Math.Sin(lsf[i]);
                pPoles.Real[k] = Math.Cos(lsf[i + 1]);
                pPoles.Imag[k] = Math.Sin(lsf[i + 1]);
            }
            for (var i = 0; k < 2 * halfOrder; i += 2, k++)
            {
                qPoles.Real[k] = Math.Cos(lsf[i]);
                qPoles.Imag[k] = Math.Sin(-lsf[i]);
                pPoles.Real[k] = Math.Cos(lsf[i + 1]);
                pPoles.Imag[k] = Math.Sin(-lsf[i + 1]);
            }

            if (n % 2 == 1)
            {
                qPoles.Real[n]     = Math.Cos(lsf[n - 1]);
                qPoles.Imag[n]     = Math.Sin(lsf[n - 1]);
                qPoles.Real[n + 1] = Math.Cos(lsf[n - 1]);
                qPoles.Imag[n + 1] = Math.Sin(-lsf[n - 1]);
            }

            var ps = new ComplexDiscreteSignal(1, TransferFunction.ZpToTf(pPoles));
            var qs = new ComplexDiscreteSignal(1, TransferFunction.ZpToTf(qPoles));

            if (n % 2 == 1)
            {
                ps = Operation.Convolve(ps, new ComplexDiscreteSignal(1, new[] { 1.0, 0, -1.0 }));
            }
            else
            {
                ps = Operation.Convolve(ps, new ComplexDiscreteSignal(1, new[] { 1.0, -1.0 }));
                qs = Operation.Convolve(qs, new ComplexDiscreteSignal(1, new[] { 1.0, 1.0 }));
            }

            for (var i = 0; i < lpc.Length; i++)
            {
                lpc[i] = (float)(0.5 * (ps.Real[i] + qs.Real[i]));
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Method for converting zeros(poles) to TF numerator(denominator)
        /// </summary>
        /// <param name="zp"></param>
        /// <returns></returns>
        public static double[] ZpToTf(ComplexDiscreteSignal zp)
        {
            var tf = new ComplexDiscreteSignal(1, new[] { 1.0, -zp.Real[0] },
                                               new[] { 0.0, -zp.Imag[0] });

            for (var k = 1; k < zp.Length; k++)
            {
                tf = Operation.Convolve(tf, new ComplexDiscreteSignal(1,
                                                                      new[] { 1.0, -zp.Real[k] },
                                                                      new[] { 0.0, -zp.Imag[k] }));
            }

            return(tf.Real);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Method for converting zeros(poles) to TF numerator(denominator)
        /// </summary>
        /// <param name="zp"></param>
        /// <returns></returns>
        public static double[] ZpToTf(ComplexDiscreteSignal zp)
        {
            if (zp == null)
            {
                throw new ArgumentException("");
            }

            var tf = new ComplexDiscreteSignal(1, new[] { 1.0, -zp.Real[0] },
                                               new[] { 0.0, -zp.Imag[0] });

            for (var k = 1; k < zp.Length; k++)
            {
                tf = Operation.Convolve(tf, new ComplexDiscreteSignal(1,
                                                                      new[] { 1.0, -zp.Real[k] },
                                                                      new[] { 0.0, -zp.Imag[k] }));
            }

            return(tf.Real);
        }
Ejemplo n.º 15
0
        public void TestTfToSos()
        {
            var zs = new ComplexDiscreteSignal(1, new[] { 1, 0.5, -0.3, 0.2, 0.5, 0.2 }, new[] { 0, 0.2, 0, -0.9, -0.2, 0.9 });
            var ps = new ComplexDiscreteSignal(1, new[] { 1, 0.2, 0.5, -0.9, 0.6, 0.1, -0.9 }, new[] { 0, 0, 0, 0.2, 0, 0, -0.2 });

            var sos = DesignFilter.TfToSos(new TransferFunction(zs, ps));

            Assert.Multiple(() =>
            {
                Assert.That(sos[0].Numerator, Is.EqualTo(new[] { 1, -0.4, 0.85 }).Within(1e-10));
                Assert.That(sos[0].Denominator, Is.EqualTo(new[] { 1, -0.1, 0 }).Within(1e-10));
                Assert.That(sos[1].Numerator, Is.EqualTo(new[] { 1, -1, 0.29 }).Within(1e-10));
                Assert.That(sos[1].Denominator, Is.EqualTo(new[] { 1, -0.7, 0.1 }).Within(1e-10));
                Assert.That(sos[2].Numerator, Is.EqualTo(new[] { 1, 0.3, 0 }).Within(1e-10));
                Assert.That(sos[2].Denominator, Is.EqualTo(new[] { 1, 1.8, 0.85 }).Within(1e-10));
                Assert.That(sos[3].Numerator, Is.EqualTo(new[] { 1, -1, 0 }).Within(1e-10));
                Assert.That(sos[3].Denominator, Is.EqualTo(new[] { 1, -1.6, 0.6 }).Within(1e-10));
                Assert.That(sos.Length, Is.EqualTo(4));
            });
        }
Ejemplo n.º 16
0
 /// <summary>
 /// Fast convolution via FFT for general complex-valued case
 /// </summary>
 /// <param name="signal"></param>
 /// <param name="kernel"></param>
 /// <returns></returns>
 public static ComplexDiscreteSignal Convolve(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel)
 {
     return(new ComplexConvolver().Convolve(signal, kernel));
 }
Ejemplo n.º 17
0
 /// <summary>
 /// TF constructor from zeros/poles
 /// </summary>
 /// <param name="zeros">Zeros</param>
 /// <param name="poles">Poles</param>
 /// <param name="gain">Gain</param>
 public TransferFunction(ComplexDiscreteSignal zeros, ComplexDiscreteSignal poles, double gain = 1)
     : this(zeros.ToComplexNumbers().ToArray(), poles.ToComplexNumbers().ToArray(), gain)
 {
 }
Ejemplo n.º 18
0
        /// <summary>
        /// Zpk to second-order sections.
        /// </summary>
        /// <param name="tf">Transfer function</param>
        /// <returns>Array of SOS transfer functions</returns>
        public static TransferFunction[] TfToSos(TransferFunction tf)
        {
            var zeros = tf.Zeros.ToComplexNumbers().ToList();
            var poles = tf.Poles.ToComplexNumbers().ToList();

            if (zeros.Count != poles.Count)
            {
                if (zeros.Count > poles.Count)
                {
                    poles.AddRange(new Complex[zeros.Count - poles.Count]);
                }
                if (zeros.Count < poles.Count)
                {
                    zeros.AddRange(new Complex[poles.Count - zeros.Count]);
                }
            }

            var sosCount = (poles.Count + 1) / 2;

            if (poles.Count % 2 == 1)
            {
                zeros.Add(Complex.Zero);
                poles.Add(Complex.Zero);
            }

            RemoveConjugated(zeros);
            RemoveConjugated(poles);

            var gains = new double[sosCount];

            gains[0] = tf.Gain;
            for (var i = 1; i < gains.Length; i++)
            {
                gains[i] = 1;
            }

            var sos = new TransferFunction[sosCount];

            // reverse order of sections

            for (var i = sosCount - 1; i >= 0; i--)
            {
                Complex z1, z2, p1, p2;

                // Select the next pole closest to unit circle

                var pos = ClosestToUnitCircle(poles, Any);
                p1 = poles[pos];
                poles.RemoveAt(pos);

                if (IsReal(p1) && poles.All(IsComplex))
                {
                    pos = ClosestToComplexValue(zeros, p1, IsReal);     // closest to pole p1
                    z1  = zeros[pos];
                    zeros.RemoveAt(pos);

                    p2 = Complex.Zero;
                    z2 = Complex.Zero;
                }
                else
                {
                    if (IsComplex(p1) && zeros.Count(IsReal) == 1)
                    {
                        pos = ClosestToComplexValue(zeros, p1, IsComplex);
                    }
                    else
                    {
                        pos = ClosestToComplexValue(zeros, p1, Any);
                    }

                    z1 = zeros[pos];
                    zeros.RemoveAt(pos);

                    if (IsComplex(p1))
                    {
                        p2 = Complex.Conjugate(p1);

                        if (IsComplex(z1))
                        {
                            z2 = Complex.Conjugate(z1);
                        }
                        else
                        {
                            pos = ClosestToComplexValue(zeros, p1, IsReal);
                            z2  = zeros[pos];
                            zeros.RemoveAt(pos);
                        }
                    }
                    else
                    {
                        if (IsComplex(z1))
                        {
                            z2 = Complex.Conjugate(z1);

                            pos = ClosestToComplexValue(poles, z1, IsReal);
                            p2  = poles[pos];
                            poles.RemoveAt(pos);
                        }
                        else
                        {
                            pos = ClosestToUnitCircle(poles, IsReal);
                            p2  = poles[pos];
                            poles.RemoveAt(pos);

                            pos = ClosestToComplexValue(zeros, p2, IsReal);
                            z2  = zeros[pos];
                            zeros.RemoveAt(pos);
                        }
                    }
                }

                var zs = new ComplexDiscreteSignal(1, new[] { z1.Real, z2.Real }, new[] { z1.Imaginary, z2.Imaginary });
                var ps = new ComplexDiscreteSignal(1, new[] { p1.Real, p2.Real }, new[] { p1.Imaginary, p2.Imaginary });

                sos[i] = new TransferFunction(zs, ps, gains[i]);
            }

            return(sos);
        }
Ejemplo n.º 19
0
 /// <summary>
 /// FIR filter design using frequency sampling method
 /// </summary>
 /// <param name="order">Filter order</param>
 /// <param name="frequencyResponse">Complex frequency response</param>
 /// <param name="window">Window</param>
 /// <returns>FIR filter kernel</returns>
 public static double[] Fir(int order, ComplexDiscreteSignal frequencyResponse, WindowTypes window = WindowTypes.Blackman)
 {
     return(Fir(order, frequencyResponse.Real, frequencyResponse.Imag, window));
 }
Ejemplo n.º 20
0
 /// <summary>
 /// TF constructor from zeros/poles
 /// </summary>
 /// <param name="zeros"></param>
 /// <param name="poles"></param>
 public TransferFunction(ComplexDiscreteSignal zeros, ComplexDiscreteSignal poles)
 {
     Zeros = zeros;
     Poles = poles;
 }
Ejemplo n.º 21
0
 /// <summary>
 /// TF constructor from zeros/poles
 /// </summary>
 /// <param name="zeros">Zeros</param>
 /// <param name="poles">Poles</param>
 /// <param name="gain"></param>
 public TransferFunction(ComplexDiscreteSignal zeros, ComplexDiscreteSignal poles, double gain = 1.0)
 {
     Gain  = gain;
     Zeros = zeros;
     Poles = poles;
 }
Ejemplo n.º 22
0
 /// <summary>
 /// Method for converting zeros(poles) to TF numerator(denominator)
 /// </summary>
 /// <param name="zp"></param>
 /// <returns></returns>
 public static double[] ZpToTf(ComplexDiscreteSignal zp)
 {
     return(ZpToTf(zp.ToComplexNumbers().ToArray()));
 }
Ejemplo n.º 23
0
 /// <summary>
 /// Fast complex cross-correlation via FFT
 /// </summary>
 /// <param name="signal1"></param>
 /// <param name="signal2"></param>
 /// <returns></returns>
 public static ComplexDiscreteSignal CrossCorrelate(ComplexDiscreteSignal signal1, ComplexDiscreteSignal signal2)
 {
     return(new ComplexConvolver().CrossCorrelate(signal1, signal2));
 }