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