2016-12-07 3 views
2

J'essaie d'expérimenter avec Math.Net, en particulier la partie FFT. J'essaye d'extraire l'information de domaine de fréquence d'une onde sinusoïdale pure. Voici le code:Comment exécuter correctement une FFT sur un ensemble de données fenêtrées à partir d'une onde sinusoïdale pure

private void Form1_Load(object sender, EventArgs e) 
     { 
      //Set up the wave and derive some useful info 
      Double WaveFreq = 500; 
      Double WavePeriod = 1/WaveFreq; 
      Double SampleFreq = 20000; 
      Double SampleTime = (1/SampleFreq); 

      //Generate the wave using the above parameters 
      var points = Generate.Sinusoidal(100000, SampleFreq, WaveFreq, 1); 

      //Array to hold our complex numbers 
      var data = new Complex[points.Length]; 

      //Set up the series to display our raw wave 
      Series WaveSeries = new Series("Waveform"); 
      WaveSeries.ChartType = SeriesChartType.Line; 

      //Creat the series for displaying the FFT 
      Series FFTSeries = new Series("FFT Test"); 
      FFTSeries.ChartType = SeriesChartType.Column; 

      //Populate both the wave series and the data array 
      for (int i = 0; i < points.Length; i++) 
      { 
       Double x = SampleTime * i; 
       WaveSeries.Points.AddXY(x, points[i]); 
       data[i] = new Complex(x, points[i]); 
      } 

      //Create the window to evaluate (using a window 5 times wider than the wavelength of the lowest ferequency being measured) 
      int WindowWidth = (int)Math.Round((1/WaveFreq)/(1/SampleFreq) * 5 + 0.5f); 
      var HannWindow = Window.HannPeriodic(WindowWidth); 
      var window = new Complex[WindowWidth]; 

      for(int i = 0; i < WindowWidth; i++) 
      { 
       var y = data[i].Imaginary * HannWindow[i]; 
       window[i] = new Complex(data[i].Real, y); 
      } 

      //Perform the FFT 
      Fourier.Forward(window); 

      //Add the calculated FFT to our FFTSeries 
      foreach(Complex sample in window) 
      { 
       FFTSeries.Points.AddXY(sample.Phase, sample.Magnitude); 
      } 

      chart2.Series.Add(WaveSeries); 
      chart2.ChartAreas[0].AxisX.Minimum = 0; 
      chart2.ChartAreas[0].AxisX.Maximum = .01; 
      chart2.ChartAreas[0].AxisY.Minimum = -2; 
      chart2.ChartAreas[0].AxisY.Maximum = 2; 

      chart1.Series.Add(FFTSeries); 
      chart1.ChartAreas[0].AxisX.Minimum = 0; 
      chart1.ChartAreas[0].AxisX.Maximum = 1000; 
      chart1.ChartAreas[0].AxisY.Minimum = 0; 
      chart1.ChartAreas[0].AxisY.Maximum = 5; 

     } 

Comme vous pouvez le voir, je générer une onde sinusoïdale à une fréquence de 500 Hz, l'échantillonnage à 20 kHz et générer 10k échantillons.

La sortie est comme suit (FFT à gauche, vague à droite) enter image description here

La FFT montre absolument rien (apartés d'un sommet de 1,8 autour de 0 Hz)! Je soupçonne que c'est probablement une erreur avec le fenêtrage mais pour la vie de moi je ne peux pas voir ce que c'est.

J'apprécie toute aide!

+0

Lorsque j'essaie de répliquer cela, je ne trouve pas la fonction Window.HannPeriodic. C'est dans la documentation MathNet, mais je ne peux compiler que si je passe à simplement utiliser Window.Hann. Est-ce que je manque quelque chose? –

+1

@KelsonBall Il est dans la version 3.14.0-beta3. –

Répondre

3

Il semble y avoir un malentendu sur les nombres complexes. Dans votre code, ils semblent être utilisés comme des points (x, y-tuples), mais ils n'ont rien à voir avec des points. L'équivalent complexe de vos points de données réels est un tableau dans lequel la partie réelle des nombres complexes correspond à vos points de données réels et la partie imaginaire est nulle. Pour l'essentiel:

var window = new Complex[WindowWidth]; 
for (int i = 0; i < WindowWidth; i++) 
{ 
    window[i] = new Complex(points[i] * HannWindow[i], 0.0); 
} 

Si vous avez besoin d'un moyen facile d'obtenir l'axe correct x pour votre tracé de fréquence, vous pouvez utiliser la fonction FrequencyScale, le long des lignes de:

var scale = Fourier.FrequencyScale(WindowWidth, SampleFreq); 
for (int i = 0; i < WindowWidth; i++) 
{ 
    FFTSeries.Points.AddXY(scale[i], window[i].Magnitude); 
} 

Vous devriez voir un pic à l'index 5, qui selon le tableau scale calculé correspond à la fréquence 500, qui correspond à votre fréquence d'onde.

Notez que la routine FFT renvoie le spectre complet, y compris les fréquences négatives, donc vous devriez également voir un pic de la même taille à la fréquence -500.

+0

Ahh merci. Je comprends mon erreur maintenant. Je lisais sur 'https://msdn.microsoft.com/en-us/library/system.numerics.complex (v = vs.110) .aspx', spécifiquement où il est dit:" La partie réelle du nombre complexe est positionné sur l'axe des x (l'axe horizontal), et la partie imaginaire est positionnée sur l'axe des ordonnées "Donc je suppose que vous avez entré les valeurs des points, puis les propriétés' real' et 'imaginary' calculeraient le complexe équivalent. –

+0

Comme un suivi rapide, lisait sur la façon dont on peut avoir besoin de "normaliser" la FFT. Serait-ce la démarche nécessaire pour aligner les amplitudes sur la réalité? Actuellement, les amplitudes semblent dépendre de l'endroit où la fréquence se situe entre 2 cases.Je sais que c'est un effet de fenêtrage, mais je ne sais pas comment corriger cela. Avez-vous des ressources disponibles pour cela? Ai-je raison dans mon hypothèse? –

+0

Peut-être qu'ils se réfèrent à la mise à l'échelle FFT? Vous pouvez le contrôler avec l'argument 'FourierOptions', mais par défaut, il s'équilibre symétriquement, de sorte qu'il satisfait le théorème de parseval (énergie de l'espace temps = énergie spatiale de la fréquence). Ceci est différent d'e.g. MATLAB qui évolue de manière asymétrique et vous devrez vous mettre à l'échelle. –

0

La FFT est définitivement là mais l'échelle que vous avez mappée est fausse.

il suffit de changer l'axe X et vous le verrez

chart1.ChartAreas[0].AxisX.Maximum = 10; 

Il semble aussi que la forme d'onde sinusoïdale que vous générez ne va pas bien que je ne suis pas expert en mathématiques net, donc je ne sais pas. Le centre ne semble pas être à zéro.

+0

Hey merci! Je pensais que l'axe des ordonnées sur le diagramme FFT représenterait une à une l'amplitude de l'onde à cette fréquence. Je vois maintenant que non. Pouvez-vous expliquer comment on devrait interpréter l'amplitude dans le graphique FFT? Merci –