如何正确 运行 对来自纯正弦波的窗口数据集进行 FFT

How to properly run an FFT on a windowed set of data from a pure sine wave

我正在尝试 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 峰值)!我怀疑这可能是窗口错误,但对于我来说,我看不出它是什么。

FFT 肯定是有的,但是你映射的比例是错误的。

只要改变X轴就可以看到

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

虽然我不是数学网专家所以我不知道,但你生成的正弦波形似乎也不正确。中心似乎不为零。

好像对复数有些误解。在您的代码中,它们似乎像点(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 例程 returns 包含负频率的完整频谱,因此您还应该在频率 -500 处看到相同大小的尖峰。