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(); } }