コード例 #1
0
        /// <summary>
        /// This algorithm is derived from P 154 (Table 8-1) of ISBN 0-9660176-3-3 "The Scientist and Engineer's Guide to Digital Signal Processing"
        /// </summary>
        /// <param name="frequencyDomain"></param>
        /// <returns></returns>
        public List <double> Synthesize(FrequencyDomain frequencyDomain)
        {
            int           freqDomainLen = frequencyDomain.RealComponent.Count;
            int           timeDomainLen = freqDomainLen * 2 - 1;
            List <double> timeDomain    = new List <double>(timeDomainLen);

            List <double> cosineAmplitudes = new List <double>(freqDomainLen);
            List <double> sineAmplitudes   = new List <double>(freqDomainLen);

            for (int K = 0; K < freqDomainLen; K++)
            {
                cosineAmplitudes.Add(frequencyDomain.ScaledRealComponent(K) / (timeDomainLen / 2));
                sineAmplitudes.Add(-1 * frequencyDomain.ScaledImaginaryComponent(K) / (timeDomainLen / 2));
            }

            // Fixup the endpoints
            cosineAmplitudes[0] = cosineAmplitudes[0] / 2;
            cosineAmplitudes[freqDomainLen - 1] = cosineAmplitudes[freqDomainLen - 1] / 2;

            for (int I = 0; I < timeDomainLen; I++)
            {
                timeDomain.Add(0.0);
            }

            for (int K = 0; K < freqDomainLen; K++)
            {
                for (int I = 0; I < timeDomainLen; I++)
                {
                    timeDomain[I] += cosineAmplitudes[K] * Math.Cos(2 * Math.PI * K * I / timeDomainLen);
                    timeDomain[I] += sineAmplitudes[K] * Math.Sin(2 * Math.PI * K * I / timeDomainLen);
                }
            }

            return(timeDomain);
        }
コード例 #2
0
 // Copy constructor
 public FrequencyDomain(FrequencyDomain rh)
 {
     this.RealComponent      = new List <double>(rh.RealComponent);
     this.ScalingFactor      = new List <double>(rh.ScalingFactor);
     this.ImaginaryComponent = new List <double>(rh.ImaginaryComponent);
     this.frequencyDomainLen = rh.frequencyDomainLen;
 }
コード例 #3
0
        /// <summary>
        /// Adapted from Udemy course "Master the Fourier transform and its applications" by Mike X Cohen
        /// </summary>
        /// <param name="timeDomain">Time series data</param>
        /// <returns></returns>
        public FrequencyDomain Transform(List <double> timeDomain, double sampleRateHz)
        {
            FrequencyDomain result = FindFourierCoeffecients(timeDomain, sampleRateHz);

            LoadFrequencyAmplitudes(result);
            return(result);
        }
コード例 #4
0
        private static FrequencyDomain FindFourierCoeffecients(List <double> timeDomain, double sampleRateHz)
        {
            int timeDomainLen    = timeDomain.Count;
            int numProcs         = Environment.ProcessorCount;
            int concurrencyLevel = numProcs * 2;

            // Prepare the Fourier Transform
            Dictionary <int, double>            fourTime = new Dictionary <int, double>(timeDomainLen);
            ConcurrentDictionary <int, Complex> csw      = new ConcurrentDictionary <int, Complex>(concurrencyLevel, timeDomainLen);

            List <Complex> fCoefs = new List <Complex>(timeDomainLen);

            // Setup the fourier time array
            for (int idx = 0; idx < timeDomainLen; idx++)
            {
                fourTime.Add(idx, ((double)idx / (double)timeDomainLen));
            }

            Complex negOneI            = new Complex(0, -1);
            Complex euler              = new Complex(Math.E, 0);
            Complex exponentMultiplier = negOneI * Math.PI * 2;

            for (int fi = 1; fi <= timeDomainLen; fi++)
            {
                Parallel.ForEach(fourTime, (timeVal) =>
                {
                    Complex value    = Complex.Pow(euler, (exponentMultiplier * (fi - 1) * timeVal.Value));
                    csw[timeVal.Key] = value;
                });

                // Compute dot product between signal and complex sine wave
                Complex dotProduct     = new Complex(0, 0);
                object  dotProductLock = new object();

                Parallel.For(0, (timeDomainLen - 1), (idx) => {
                    Complex dotProductLocal = new Complex(0, 0);

                    dotProductLocal += ((csw[idx] * timeDomain[idx]));

                    lock (dotProductLock)
                    {
                        dotProduct += dotProductLocal;
                    }
                }

                             );

                fCoefs.Add(dotProduct);
                csw.Clear();
            }

            FrequencyDomain result = new FrequencyDomain();

            result.FourierCoefficients = fCoefs;
            result.SampleRateHz        = sampleRateHz;
            return(result);
        }
コード例 #5
0
        public static List <Tuple <double, double> > ToMagnitudePhaseList(FrequencyDomain frequencyDomain)
        {
            List <Tuple <double, double> > magPhaseList = new List <Tuple <double, double> >();

            foreach (Complex coefficient in frequencyDomain.FourierCoefficients)
            {
                magPhaseList.Add(new Tuple <double, double>(coefficient.Magnitude, coefficient.Phase));
            }

            return(magPhaseList);
        }
コード例 #6
0
        /// <summary>
        /// Simple pass through , callers typically parse magnitude/phase CSVs and pass in a list of magnitude and phases which we
        /// then assign to a new frequencyDomain object and return.
        /// </summary>
        /// <param name="magPhaseList"></param>
        /// <returns></returns>
        public static FrequencyDomain FromMagnitudePhaseList(List <Tuple <double, double> > magPhaseList)
        {
            if (null == magPhaseList)
            {
                return(null);
            }

            FrequencyDomain frequencyDomain = new FrequencyDomain();

            foreach (Tuple <double, double> magPhase in magPhaseList)
            {
                frequencyDomain.FourierCoefficients.Add(Complex.FromPolarCoordinates(magPhase.Item1, magPhase.Item2));
            }
            return(frequencyDomain);
        }
コード例 #7
0
        /// <summary>
        /// Adapted from Udemy course "Master the Fourier transform and its applications" by Mike X Cohen
        /// </summary>
        /// <param name="freqDomain">Time Frequency Domain data</param>
        /// <returns></returns>
        public List <double> Synthesize(FrequencyDomain freqDomain)
        {
            List <Complex> fCoefs              = freqDomain.FourierCoefficients;
            int            freqDomainLen       = fCoefs.Count;
            List <double>  timeDomain          = new List <double>(freqDomainLen);
            List <Complex> csw                 = new List <Complex>(freqDomainLen);
            List <Complex> reconstructedSignal = new List <Complex>(freqDomainLen);

            List <double> fourTime = new List <double>(freqDomainLen);

            // Setup the fourier time array
            for (int idx = 0; idx < freqDomainLen; idx++)
            {
                fourTime.Add((double)idx / (double)freqDomainLen);
                reconstructedSignal.Add(new Complex(0, 0));
            }

            for (int fi = 1; fi <= freqDomainLen; fi++)
            {
                Complex posOneI = new Complex(0, 1);
                Complex euler   = new Complex(Math.E, 0);

                foreach (double timeVal in fourTime)
                {
                    Complex value = fCoefs[fi - 1] * Complex.Pow(euler, (posOneI * Math.PI * 2 * (fi - 1) * timeVal));
                    csw.Add(value);
                }

                for (int idx = 0; idx < csw.Count; idx++)
                {
                    reconstructedSignal[idx] += csw[idx];
                }

                csw.Clear();
            }

            for (int idx = 0; idx < reconstructedSignal.Count; idx++)
            {
                reconstructedSignal[idx] /= freqDomainLen;
            }

            foreach (Complex sigVal in reconstructedSignal)
            {
                timeDomain.Add(sigVal.Real);
            }

            return(timeDomain);
        }
コード例 #8
0
        /// <summary>
        /// This algorithm is derived from Chapter 12 (table 12-4) of ISBN 0-9660176-3-3 "The Scientist and Engineer's Guide to Digital Signal Processing"
        /// </summary>
        /// <param name="timeDomain"></param>
        /// <returns></returns>
        public FrequencyDomain Transform(List <double> timeDomain, double sampleRateHz)
        {
            FrequencyDomain fftResult = new FrequencyDomain(timeDomain.Count, this);

            // The FFT operates in place, store the timeDomain samples in the real component (leave the imaginary compoenent zeroed)
            fftResult.RealComponent.Clear();
            fftResult.RealComponent.AddRange(timeDomain);
            fftResult.ImaginaryComponent.Clear();
            fftResult.ImaginaryComponent.Capacity = timeDomain.Count;
            fftResult.SampleRateHz = sampleRateHz;
            for (int idx = 0; idx < timeDomain.Count; idx++)
            {
                fftResult.ImaginaryComponent.Add(0.0);
            }

            return(Transform(fftResult));
        }
コード例 #9
0
        /// <summary>
        /// Discrete Fourier Transform Analysis
        /// Derived from Chapter 31 of the "The Scientist and Engineer's Guide to Digital Signal Processing
        /// https://www.dspguide.com/CH31.PDF
        /// </summary>
        /// <param name="signal"></param>
        /// <param name="sampleRateHz"></param>
        /// <returns></returns>
        public FrequencyDomain Transform(List <double> signal, double sampleRateHz)
        {
            if (signal == null)
            {
                return(null);
            }

            // Note index variables and arrays named to coincide with equation on page Chapter 31, page 578,
            // Discrete Fourier Transform (DFT) analysis
            List <double>   x               = signal;
            List <Complex>  X               = new List <Complex>(x.Count);
            int             N               = x.Count;
            Complex         j               = new Complex(0, 1);
            Complex         exponentPart    = -1 * j * Math.PI * 2;
            FrequencyDomain frequencyDomain = null;
            object          xLock           = new object();

            for (int k = 0; k <= (N - 1); k++)
            {
                X.Add(new Complex(0, 0));
            }

            // k runs over period 0 to N - 1
            Parallel.For(0, (N - 1), (k) =>
            {
                Complex Xk = new Complex(0, 0);
                //X[k] = 1/N * (Sum from 0 to N - 1 of x[n] * e ^ (-j * 2 * Pi * k * n / N
                for (int n = 0; n <= (N - 1); n++)
                {
                    double knOverN = (double)(k * n) / (double)N;
                    Xk            += x[n] * Complex.Pow(Math.E, (exponentPart * knOverN));
                }

                lock (xLock)
                {
                    X[k] = Xk / N;     // Normalize X[k] (1/N multiplier)
                }
            });

            frequencyDomain = new FrequencyDomain();
            frequencyDomain.FourierCoefficients = X;
            frequencyDomain.SampleRateHz        = sampleRateHz;
            LoadFrequencyAmplitudes(frequencyDomain);
            return(frequencyDomain);
        }
コード例 #10
0
        /// <summary>
        /// This algorithm is derived from P 160 (Table 8-2) of ISBN 0-9660176-3-3 "The Scientist and Engineer's Guide to Digital Signal Processing"
        /// </summary>
        /// <param name="timeDomain"></param>
        /// <returns></returns>
        public FrequencyDomain Transform(List <double> timeDomain, double sampleRateHz)
        {
            int             timeDomainLen   = timeDomain.Count;
            FrequencyDomain frequencyDomain = new FrequencyDomain(timeDomainLen, this);

            frequencyDomain.SampleRateHz = sampleRateHz;
            int freqDomainLen = frequencyDomain.RealComponent.Count;

            for (int K = 0; K < freqDomainLen; K++)
            {
                for (int I = 0; I < timeDomainLen; I++)
                {
                    frequencyDomain.RealComponent[K]      += timeDomain[I] * Math.Cos(2 * Math.PI * K * I / timeDomainLen);
                    frequencyDomain.ImaginaryComponent[K] -= timeDomain[I] * Math.Sin(2 * Math.PI * K * I / timeDomainLen);
                }
            }
            return(frequencyDomain);
        }
コード例 #11
0
        private static void LoadFrequencyAmplitudes(FrequencyDomain frequencyDomain)
        {
            double sampleRate = frequencyDomain.SampleRateHz;
            int    points     = frequencyDomain.FourierCoefficients.Count;

            List <double> frequencyVector  = linspace(0, sampleRate / 2, (int)Math.Floor((double)((points / 2) + 1)));
            List <double> amplitudesVector = new List <double>(points);

            foreach (Complex coefficient in frequencyDomain.FourierCoefficients)
            {
                double amplitude = 2 * (Complex.Abs(coefficient) / points);
                amplitudesVector.Add(amplitude);
            }

            for (int idx = 0; idx < frequencyVector.Count; idx++)
            {
                frequencyDomain.FrequencyAmplitudes.Add(frequencyVector[idx], amplitudesVector[idx]);
            }
        }
コード例 #12
0
        /// <summary>
        /// This algorithm is derived from Table 12-5 of ISBN 0-9660176-3-3 "The Scientist and Engineer's Guide to Digital Signal Processing"
        /// Inverse Fourier Transform
        /// </summary>
        /// <param name="frequencyDomain"></param>
        /// <returns></returns>
        public List <double> Synthesize(FrequencyDomain frequencyDomain)
        {
            for (int k = 0; k < frequencyDomain.frequencyDomainLen; k++)
            {
                frequencyDomain.ImaginaryComponent[k] *= -1;
            }

            // Transformation happens in place
            Transform(frequencyDomain);

            // Chainge the sign of the imaginary component
            for (int i = 0; i < frequencyDomain.frequencyDomainLen; i++)
            {
                frequencyDomain.RealComponent[i]     /= frequencyDomain.frequencyDomainLen;
                frequencyDomain.ImaginaryComponent[i] = -1 * frequencyDomain.ImaginaryComponent[i] / frequencyDomain.frequencyDomainLen;
            }

            return(frequencyDomain.RealComponent);
        }
コード例 #13
0
        /// <summary>
        /// Discrete Fourier Transform Synthesis
        /// Derived from Chapter 31 of the "The Scientist and Engineer's Guide to Digital Signal Processing
        /// https://www.dspguide.com/CH31.PDF
        /// </summary>
        /// <param name="frequencyDomain"></param>
        /// <returns>The real portion of the time domain signal</returns>
        public List <double> Synthesize(FrequencyDomain frequencyDomain)
        {
            if (frequencyDomain == null)
            {
                return(null);
            }

            // Note index variables and arrays named to coincide with equation on page Chapter 31, page 578,
            // Discrete Fourier Transform (DFT) synthesis
            int            N = frequencyDomain.FourierCoefficients.Count;
            List <double>  realTimeDomain = new List <double>(N);
            List <Complex> X                  = frequencyDomain.FourierCoefficients;
            Complex        j                  = new Complex(0, 1);
            Complex        exponentPart       = j * Math.PI * 2;
            object         realTimeDomainLock = new object();

            for (int n = 0; n <= (N - 1); n++)
            {
                realTimeDomain.Add(0.0);
            }

            Parallel.For(0, (N - 1), (n) =>
            {
                Complex xn = new Complex(0, 0);
                for (int k = 0; k <= (N - 1); k++)
                {
                    double knOverN = (double)(k * n) / (double)N;
                    xn            += X[k] * Complex.Pow(Math.E, exponentPart * knOverN);
                }

                lock (realTimeDomainLock)
                {
                    realTimeDomain[n] = xn.Real;
                }
            });

            return(realTimeDomain);
        }
コード例 #14
0
        private FrequencyDomain Transform(FrequencyDomain frequencyDomain)
        {
            int n   = frequencyDomain.RealComponent.Count;
            int nm1 = n - 1;
            int nd2 = n / 2;
            int m   = (int)(Math.Log(n, 2));
            int j   = nd2;

            #region bitreversal
            int k;
            for (int idx = 1; idx <= (n - 2); idx++)
            {
                if (idx <= j)
                {
                    double tr = frequencyDomain.RealComponent[j];
                    double ti = frequencyDomain.ImaginaryComponent[j];
                    frequencyDomain.RealComponent[j]        = frequencyDomain.RealComponent[idx];
                    frequencyDomain.ImaginaryComponent[j]   = frequencyDomain.ImaginaryComponent[idx];
                    frequencyDomain.RealComponent[idx]      = tr;
                    frequencyDomain.ImaginaryComponent[idx] = ti;
                }
                k = nd2;
                while (!(k > j))
                {
                    j = j - k;

                    if (k >= 2)
                    {
                        k = k / 2;
                    }
                }
                j = j + k;
            }
            #endregion

            #region stageLoop
            for (int L = 1; L <= m; L++)
            {
                int    le  = (int)Math.Pow(2, L);
                int    le2 = le / 2;
                double ur  = 1;
                double ui  = 0;

                double sr = Math.Cos(Math.PI / le2);
                double si = -1 * Math.Sin(Math.PI / le2);
                double tr;
                double ti;

                for (j = 1; j <= le2; j++)
                {
                    int jm1 = j - 1;
                    for (int idx = jm1; idx <= nm1; idx += le)
                    {
                        int ip = (idx + le2);
                        tr = frequencyDomain.RealComponent[ip] * ur - frequencyDomain.ImaginaryComponent[ip] * ui;
                        ti = frequencyDomain.RealComponent[ip] * ui + frequencyDomain.ImaginaryComponent[ip] * ur;
                        frequencyDomain.RealComponent[ip]       = frequencyDomain.RealComponent[idx] - tr;
                        frequencyDomain.ImaginaryComponent[ip]  = frequencyDomain.ImaginaryComponent[idx] - ti;
                        frequencyDomain.RealComponent[idx]      = frequencyDomain.RealComponent[idx] + tr;
                        frequencyDomain.ImaginaryComponent[idx] = frequencyDomain.ImaginaryComponent[idx] + ti;
                    }
                    tr = ur;
                    ur = tr * sr - ui * si;
                    ui = tr * si + ui * sr;
                }
            }
            #endregion

            return(frequencyDomain);
        }