public static void Windowing(int n, double[] din, WindowingTypes windowType, int scale, double[] wdin, bool containsComplex = true)
        {
            int k = 0;      //remember din is complex so input signal goes into real part

            for (int i = 0; i < n; i++)
            {
                switch (windowType)
                {
                case WindowingTypes.Square:     // square (no window)
                    wdin[k] = din[k] / scale;
                    break;

                case WindowingTypes.Parzen:     // parzen window
                    wdin[k] = din[k] * Parzen(i, n) / scale;
                    break;

                case WindowingTypes.Welch:     // welch window
                    wdin[k] = din[k] * Welch(i, n) / scale;
                    break;

                case WindowingTypes.Hanning:     // hanning window
                    wdin[k] = din[k] * Hanning(i, n) / scale;
                    break;

                case WindowingTypes.Hamming:     // hamming window
                    wdin[k] = din[k] * Hamming(i, n) / scale;
                    break;

                case WindowingTypes.Blackman:     // blackman window
                    wdin[k] = din[k] * Blackman(i, n) / scale;
                    break;

                case WindowingTypes.Steeper30db:     // steeper 30-dB/octave rolloff window
                    wdin[k] = din[k] * Steeper(i, n) / scale;
                    break;
                }
                if (containsComplex)
                {
                    k += 2;
                }
                else
                {
                    k++;
                }
            }
        }
        // returns CG (coherent gain) of window being used times N
        private static double WindowGain(int n, WindowingTypes windowType)
        {
            double density = 0;

            for (int i = 0; i < n; i++)
            {
                switch (windowType)
                {
                case WindowingTypes.Square:     // square (no window)
                    density += 1.0;
                    break;

                case WindowingTypes.Parzen:     // parzen window
                    density += Parzen(i, n) * Parzen(i, n);
                    break;

                case WindowingTypes.Welch:     // welch window
                    density += Welch(i, n) * Welch(i, n);
                    break;

                case WindowingTypes.Hanning:     // hanning window
                    density += Hanning(i, n) * Hanning(i, n);
                    break;

                case WindowingTypes.Hamming:     // hamming window
                    density += Hamming(i, n) * Hamming(i, n);
                    break;

                case WindowingTypes.Blackman:     // blackman window
                    density += Blackman(i, n) * Blackman(i, n);
                    break;

                case WindowingTypes.Steeper30db:     // steeper 30-dB/octave rolloff window
                    density += Steeper(i, n) * Steeper(i, n);
                    break;
                }
            }
            density *= n;
            return(density);
        }
        /// <summary>
        /// Executes an FFT on another thread
        /// </summary>
        /// <param name="samples"></param>
        /// <param name="samplesPerSecond"></param>
        /// <param name="wantedAverages"></param>
        /// <param name="windowType"></param>
        public void ExecuteFftAsync(double[] samples, int samplesPerSecond, int wantedAverages, WindowingTypes windowType)
        {
            if (_fftWorker.IsBusy)
            {
                return;
            }

            // Start the worker
            FftWorkerArguments args = new FftWorkerArguments {
                Samples = samples, SamplesPerSecond = samplesPerSecond, WantedAverages = wantedAverages, WindowType = windowType
            };

            _fftWorker.RunWorkerAsync(args);
        }
        /// <summary>
        /// Do fft calculations and averaging
        /// </summary>
        /// <param name="sender">Background worker</param>
        /// <param name="e">Arguments</param>
        private void fftWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                BackgroundWorker   worker = sender as BackgroundWorker;
                FftWorkerArguments args   = (FftWorkerArguments)e.Argument;

                lock (PropertyLock)
                {
                    if (_fftSize != args.Samples.Length || _fftAvgWant != args.WantedAverages)
                    {
                        if (_fftSize > 0)
                        {
                            MemoryPlanCleanUp(); // Plan changed, clean up
                        }
                        _fftSize    = args.Samples.Length;
                        _fftAvgWant = args.WantedAverages;
                        MemoryPlansetup(_fftSize, _fftAvgWant);
                        _fftAvgCnt = 1; // reset averages
                    }
                    if (_windowType != args.WindowType)
                    {
                        _windowType = args.WindowType;
                        _fftAvgCnt  = 1; // reset averages
                    }
                }
                int s = 0;
                for (int i = 0; i < 2 * _fftSize; i = i + 2)
                {
                    _din[i]     = args.Samples[s++]; // real part
                    _din[i + 1] = 0;                 // imaginary part
                }

                Windowing(_fftSize, _din, _windowType, 1, _din);
                fftw.execute(_fplan);

                //convert to dBm
                int    j       = 0;
                double scaling = 2 / WindowGain(_fftSize, _windowType);
                //scaling for FFT N and window gain; no window WindowGain = N*N

                for (int i = 0; i < _fftSize; i++)
                {
                    int     avgIndex = _fftAvgCnt % _fftAvgWant;
                    Complex temp     = new Complex(_dout[j], _dout[j + 1]);
                    _magnitudeSqrt[i, avgIndex] = (temp.Magnitude * temp.Magnitude) * scaling;
                    //scaling FFT output by N and windowing correction
                    if (i == 0)
                    {
                        _magnitudeSqrt[i, avgIndex] = _magnitudeSqrt[i, avgIndex] / 2;
                    }
                    //DC term Scales Differently
                    if (_magnitudeSqrt[i, avgIndex] <= 5e-14)
                    {
                        _magnitudeSqrt[i, avgIndex] = 5e-14;                                          // Clamps to -120 dBm and avoids log10 of 0;
                    }
                    _freqDomain[i, avgIndex]    = 10 * Math.Log10(_magnitudeSqrt[i, avgIndex] / .05); // Power dBm = 10*log10(volt*volt/impedance/1mW) = 10*log10(volt*volt/50/.001)
                    _magnitudeSqrt[i, avgIndex] = Math.Sqrt(_magnitudeSqrt[i, avgIndex]);
                    j += 2;
                }

                if (_fftAvgCnt >= _fftAvgWant) // Wait until wanted amount collected.
                {
                    double[] fft = new double[_fftSize / 2];
                    double[] rms = new double[_fftSize / 2];
                    if (_fftAvgWant > 1)
                    {
                        // Calculate average
                        for (int i = 0; i < fft.Length; i++)
                        {
                            double avgTempDb        = 0;
                            double avgTempMagnitude = 0;
                            for (int f = 0; f < _fftAvgWant; f++)
                            {
                                avgTempDb        += _freqDomain[i, f]; //sum up FFTAvgWant Spectra to calculate average
                                avgTempMagnitude += _magnitudeSqrt[i, f];
                            }
                            fft[i] = avgTempDb / (_fftAvgWant);
                            rms[i] = avgTempMagnitude / (_fftAvgWant);
                        }
                    }
                    else
                    {
                        for (int i = 0; i < fft.Length; i++)
                        {
                            fft[i] = _freqDomain[i, 0];
                            rms[i] = _magnitudeSqrt[i, 0];
                        }
                    }

                    if (worker == null || worker.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }

                    FFTData fftData = new FFTData(fft, rms, args.SamplesPerSecond);
                    e.Result = fftData;
                }
                _fftAvgCnt++; //increment pointer to ring buffer for spectra
            }
            catch (Exception) {}
            finally
            {
                _resetEvent.Set();
            }
        }