/**************************************************************************** * TrimFFT * Trim FFT array prior to Lorentzian fitting based on user specifications ****************************************************************************/ public FFTData TrimFFT(FFTData fftData, LorentzSettings lorentzSettings) { FFTData fftTrimData = new FFTData(); int startIndex = Array.FindIndex(fftData.freq, x => x >= lorentzSettings.trimStartFreq); int stopIndex = Array.FindIndex(fftData.freq, x => x >= lorentzSettings.trimStopFreq); fftTrimData.fft = fftData.fft.ToList().Skip(startIndex + 1).Take(stopIndex - startIndex).ToArray(); fftTrimData.freq = fftData.freq.ToList().ToList().Skip(startIndex + 1).Take(stopIndex - startIndex).ToArray(); return(fftTrimData); }
public SpectrumDisplay() { this.GetObservable(FFTDataProperty) .Throttle(TimeSpan.FromMilliseconds(250)) .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(x => { if (FFTData != null) { FFTData = new double[FFTData.GetLength(0), FFTData.GetLength(1)]; } }); }
/// <summary> /// Updates the plugin /// </summary> public override void Refresh() { // paint red linestrip GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.MatrixMode(MatrixMode.Modelview); GL.LoadIdentity(); // size float width = 100; float height = 100; // margins float bottom = 10; float top = 10; float left = 10; float right = 10; float size = 1024; if ((System.Boolean)showSecondLine.Value) { float[] rev = FFTData.Reverse().ToArray <float>(); GL.Color3((Color)colorSecondLine.Value); GL.Begin(BeginMode.LineStrip); for (int i = 0; i < rev.Length; i++) { float p = rev[i]; float w = width - left - right; float h = height - top - bottom; float x = left + (i * w / size); float y = bottom + (p * h * (float)Math.Sqrt(size - i)); GL.Vertex2(x, y); } GL.End(); } GL.Color3((Color)colorFirstLine.Value); GL.Begin(BeginMode.LineStrip); for (int i = 0; i < FFTData.Length; i++) { float p = FFTData[i]; float w = width - left - right; float h = height - top - bottom; float x = left + (i * w / size); float e = (float)0.5 + ((float)1.5 * (i / size)); float y = bottom + (p * h * (float)Math.Sqrt(i)); GL.Vertex2(x, y); } GL.End(); }
/// <summary> /// Calculate and render the display update rate of the scope view. /// </summary> private void RenderFftStats(FFTData data) { if (DateTime.Now.Subtract(_lastFftRenderTime).TotalMilliseconds > 200) { if (data != null) { lblFFTResolution.Text = "Resolution: " + data.ResolutionBandWith.ToString("F0") + "Hz"; lblFFTFrequency.Text = "Base frequency: " + data.BaseFrequency.ToString("F0") + "Hz"; } else { lblFFTResolution.Text = "Resolution: ?"; lblFFTFrequency.Text = "Base frequency: ?"; } _lastFftRenderTime = DateTime.Now; } }
void RenderFFTData(FFTData data) { try { spectrum.Series.RemoveAt(0); spectrum.Series.Add("Series1"); spectrum.Series["Series1"].ChartArea = "ChartArea1"; spectrum.Series["Series1"].ChartType = SeriesChartType.Line; spectrum.Series["Series1"].MarkerSize = 1; if (data != null) { double[] yPoints = rbDbm.Checked ? data.FreqDomain : data.Vrms; double[] xPoints = Enumerable.Range(0, yPoints.Count()).Select(s => s * (double)data.SamplesPerSecond / data.FreqDomain.Length / 2).ToArray(); spectrum.ChartAreas["ChartArea1"].CursorX.Interval = data.SamplesPerSecond / (double)data.FreqDomain.Length / 2.0; //matches cursor step size to X axis step size spectrum.Series["Series1"].Points.DataBindXY(xPoints, yPoints); } RenderFftStats(data); } catch (Exception) { } }
// LORENTZIAN FITTING // /**************************************************************************** * GetLorentzParams * Get lorentzian fitting parameters ****************************************************************************/ public void GetLorentzParams(FFTData fftData, LorentzSettings lorentzSettings, ref LorentzParams lorentzParams) { double[][] dataPoints = new double[][] { fftData.freq, fftData.fft }; LMA algorithm; if (lorentzSettings.isYShiftLorentz) { LMAFunction lorentzShift = new LorenzianFunctionShift(); double[] startPoint = new double[4]; try { startPoint[0] = lorentzSettings.startPointLP.A; } catch (Exception) { startPoint[0] = 10; } try { startPoint[1] = lorentzSettings.startPointLP.f0; } catch (Exception) { startPoint[1] = 25; } try { startPoint[2] = lorentzSettings.startPointLP.gamma; } catch (Exception) { startPoint[2] = 1; } try { startPoint[3] = lorentzSettings.startPointLP.up; } catch (Exception) { startPoint[3] = 1; } algorithm = new LMA(lorentzShift, startPoint, dataPoints, null, new GeneralMatrix(4, 4), 1d - 20, lorentzSettings.nIter); algorithm.Fit(); lorentzParams.A = algorithm.Parameters[0]; lorentzParams.gamma = algorithm.Parameters[2]; lorentzParams.f0 = algorithm.Parameters[1]; lorentzParams.up = algorithm.Parameters[3]; } else { LMAFunction lorentz = new LorenzianFunction(); double[] startPoint = new double[3]; try { startPoint[0] = lorentzSettings.startPointLP.A; } catch (Exception) { startPoint[0] = 10; } try { startPoint[1] = lorentzSettings.startPointLP.f0; } catch (Exception) { startPoint[1] = 25; } try { startPoint[2] = lorentzSettings.startPointLP.gamma; } catch (Exception) { startPoint[2] = 1; } algorithm = new LMA(lorentz, startPoint, dataPoints, null, new GeneralMatrix(3, 3), 1d - 20, lorentzSettings.nIter); algorithm.Fit(); lorentzParams.A = algorithm.Parameters[0]; lorentzParams.gamma = algorithm.Parameters[2]; lorentzParams.f0 = algorithm.Parameters[1]; lorentzParams.up = 0; } }
// FFT CALCULATIONS // /**************************************************************************** * GetFFT * Get the raw FFT of an array ****************************************************************************/ private FFTData GetFFT(int[] amp, int[] time, FFTSettings fftSettings, double[] lastfft, int SampleCount) { FFTData fftData = new FFTData(); double[] fft = new double[amp.Length]; double[] freq = new double[amp.Length / 2]; //AForge.Math.Complex[] fftComplex = new AForge.Math.Complex[amp.Length]; Complex[] fftComplex = new Complex[amp.Length]; // if FFT style has changed, reset the averaging by making last FFT null. if (fftSettings.isFftStyleChange) { lastfft = null; } //calculate fft for (int i = 0; i < amp.Length; i++) { //fftComplex[i] = new AForge.Math.Complex(); fftComplex[i] = new Complex(amp[i], 0); } //AForge.Math.FourierTransform.FFT(fftComplex, AForge.Math.FourierTransform.Direction.Forward); Accord.Math.Transforms.FourierTransform2.FFT(fftComplex, Accord.Math.FourierTransform.Direction.Forward); // transform fft based on user selected style for (int i = 0; i < amp.Length; i++) { time[i] = time[i] - time.Min(); switch (fftSettings.Fftstyle) { case 0: //Log fft[i] = 20 * Math.Log10(fftComplex[i].Magnitude); // decibels=20Log, http://www.arrl.org/files/file/Instructor%20resources/A%20Tutorial%20on%20the%20Dec-N0AX.pdf if (fft[i] == Double.NegativeInfinity || fft[i] == Double.NaN) { fft[i] = 0; } break; case 1: // Log - Complex Conjugate fft[i] = 20 * Math.Log10(Complex.Conjugate(fftComplex[i]).Magnitude *fftComplex[i].Magnitude); if (fft[i] == Double.NegativeInfinity || fft[i] == Double.NaN) { fft[i] = 0; } break; case 2: // Magnitude fft[i] = fftComplex[i].Magnitude; break; case 3: // Magnitude - Complex Conjugate fft[i] = Complex.Conjugate(fftComplex[i]).Magnitude *fftComplex[i].Magnitude; break; default: break; } if (i % 2 == 0) { freq[i / 2] = /*Math.Pow(2,1024/SampleCount-1) */ (1024 / SampleCount) * Convert.ToDouble(i / 2) * 1e3 / (time[9] - time[8]); } } fft = fft.Take(fft.Length / 2).ToArray(); // transform FFT based on user selected averaging technique- New Average = (New Spectrum • 1/N) + (Old Average) • (N−1)/N if (lastfft == null) { fftSettings.isFftMovAvg = false; fftSettings.isFftContAvg = false; } if (fftSettings.isFftMovAvg) { for (int i = 0; i < fft.Length; i++) { fft[i] = fft[i] * 1 / fftSettings.nFftMovAvg + lastfft[i] * (fftSettings.nFftMovAvg - 1) / fftSettings.nFftMovAvg; } } if (fftSettings.isFftContAvg) { for (int i = 0; i < fft.Length; i++) { try { fft[i] = fft[i] * 1 / fftSettings.nFftContAvg + lastfft[i] * (fftSettings.nFftContAvg - 1) / fftSettings.nFftContAvg; } catch (Exception) { } } fftSettings.nFftContAvg++; } fftData.fft = fft; fftData.freq = freq; return(fftData); }