离散傅里叶变换

30

我目前正在尝试编写一些傅里叶变换算法。我从数学定义中描述的简单DFT算法开始:

public class DFT {
    public static Complex[] Transform(Complex[] input) {
        int N = input.Length;

        Complex[] output = new Complex[N];

        double arg = -2.0 * Math.PI / (double)N;
        for (int n = 0; n < N; n++) {
            output[n] = new Complex();
            for (int k = 0; k < N; k++)
                output[n] += input[k] * Complex.Polar(1, arg * (double)n * (double)k);
        }
        return output;
    }
}

所以我用以下代码测试了这个算法:

    private int samplingFrequency = 120;
    private int numberValues = 240;

    private void doCalc(object sender, EventArgs e) {
        Complex[] input = new Complex[numberValues];
        Complex[] output = new Complex[numberValues];

        double t = 0;
        double y = 0;
        for (int i = 0; i < numberValues; i++) {
            t = (double)i / (double)samplingFrequency;
            y = Math.Sin(2 * Math.PI * t);
            input[i] = new Complex(y, 0);
        }

        output = DFT.Transform(input);

        printFunc(input);
        printAbs(output);
    }

转换效果很好,但仅当numberValues是采样频率的倍数时才有效(在本例中为:120、240、360等)。这是我计算240个值的结果:
转换结果正常。
如果我尝试计算280个值,则会得到以下结果:
为什么改变计算值的数量后会得到错误的结果?我不确定我的问题是代码问题还是对DFT数学定义的误解。无论哪种方式,有人能帮助我解决问题吗?谢谢。

你能告诉我为什么Transform函数的输入是一个复杂数组吗?如果你正在从标准声波(时域)进行转换,那么它不应该是一个单一的双精度数组吗? - Dan W
2
好的,我明白了 - 虚部被设置为0,因此它本质上等同于一个双精度浮点数。顺便说一下,在你的代码中,“Complex.Polar”应该改为“Complex.FromPolarCoordinates”。 - Dan W
2
你用什么来绘制图表? - GorillaApe
那是我自己编写的一个小型C#解决方案。可能有更好的库存在 :) - Marcel
2个回答

32

你所经历的现象被称为频谱泄漏

这是由于傅里叶变换的基本数学假设了一个从负无穷到正无穷的连续函数。因此,您提供的样本范围在实际上会被重复无限次。如果您的窗口中没有完整的波形周期,则末端不会对齐,导致出现不连续性,表现为频率向两侧模糊扩散。

处理这种情况的正常方法称为窗口函数。然而,这种方法也有缺点,会导致幅度略微偏差。这个过程是将你要处理的整个样本窗口乘以某个函数,该函数在窗口两端逐渐趋近于0,使得窗口两端对齐,但会产生振幅失真,因为这个过程会降低总信号功率。

因此,总结一下,您的代码没有错误,结果是符合预期的。可以使用窗口函数减少这些伪像,但这会影响幅度的准确性。您需要调查并确定哪种解决方案最适合您项目的要求。


6
你并没有得到非周期正弦波的错误结果,它们也不仅仅是“伪影”。实际上,你得到的结果比周期性正弦波更加完整,因为在周期性正弦波中无法看到其他非零值所包含的有用信息。这些信息可以用于插值单一非周期性幅度谱内的正弦波频率。
可以将DFT视为矩形窗口与正弦波进行卷积。这会生成(非常接近)Sinc函数,它具有无限的范围,但对于除了其中心DFT bin以外的任何DFT bin频率的正弦波都恰好为零。这种情况只发生在FFT孔径完全位于频率周期上时,而不是其他情况下。Sinc函数有许多“峰”,这些峰在你的第一个图中均被隐藏起来。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接