【问题标题】:How to properly run an FFT on a windowed set of data from a pure sine wave如何在纯正弦波的窗口数据集上正确运行 FFT
【发布时间】:2016-12-07 17:32:56
【问题描述】:

我正在尝试使用 Math.Net,特别是 FFT 部分。我正在尝试从纯正弦波中提取频域信息。代码如下:

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;

        }

如您所见,我正在生成频率为 500Hz 的正弦波,以 20kHz 采样并生成 10k 个样本。

输出如下(左边是FFT,右边是wave)

FFT 完全没有显示(除了 0Hz 附近的 1.8 峰值)!我怀疑这可能是窗口错误,但对于我的生活,我看不到它是什么。

【问题讨论】:

  • 当我尝试复制这个时,我找不到 Window.HannPeriodic 函数。它在 MathNet 文档中,但我只能在切换到仅使用 Window.Hann 时进行编译。我错过了什么吗?
  • @KelsonB​​all 在 3.14.0-beta3 版本中。

标签: c# fft math.net


【解决方案1】:

似乎对复数有一些误解。在您的代码中,它们似乎像点(x,y 元组)一样使用,但它们与点完全无关。您的真实数据点的复数等价物是一个数组,其中复数的实部与您的真实数据点匹配,而虚部全为零。本质上:

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

如果您需要一种简单的方法来为您的频率图获得正确的 x 轴,您可以使用 FrequencyScale 函数,如下所示:

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

您应该在索引 5 处看到一个尖峰,根据计算得出的 scale 数组对应于频率 500,它与您的波频率匹配。

请注意,FFT 例程会返回包括负频率在内的完整频谱,因此您还应该在频率 -500 处看到相同大小的尖峰。

【讨论】:

  • 啊,谢谢。我现在明白我的错误了。我正在阅读https://msdn.microsoft.com/en-us/library/system.numerics.complex(v=vs.110).aspx,特别是它说:“复数的实部位于x轴(水平轴)上,虚部位于y轴上”所以我假设您输入了点值,然后 realimaginary 属性将计算复数等值。
  • 作为快速跟进,正在阅读有关如何需要“标准化”FFT 的信息。这可能是使幅度与现实保持一致的必要步骤吗?目前,幅度似乎取决于频率落在 2 个 bin 之间的位置。我知道这是窗口化的影响,但我不知道如何纠正它。你有任何可用的资源吗?我的假设是否正确?
  • 也许他们指的是 FFT 缩放?您可以使用FourierOptions 参数来控制它,但默认情况下它已经对称缩放,因此它满足 parseval 定理(时间空间能量 = 频率空间能量)。这与例如不同。 MATLAB 不对称缩放,你必须自己缩放。
  • 如果您对尖峰的能量感兴趣,可以将尖峰样本及其两侧相邻样本的平方幅度相加。
【解决方案2】:

FFT 肯定存在,但您映射的比例是错误的。

只要改变X轴,你就会看到它

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

您生成的正弦波形似乎也不正确,尽管我不是数学网络专家,所以我不知道。中心似乎并不为零。

【讨论】:

  • 嘿,谢谢!我认为 FFT 图上的 y 轴将一对一地表示该频率下的波幅。我现在看到它没有。你能解释一下应该如何解释 FFT 图中的幅度吗?谢谢
猜你喜欢
  • 1970-01-01
  • 2010-11-02
  • 1970-01-01
  • 2013-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-02
  • 1970-01-01
相关资源
最近更新 更多