使用FFT计算频率时出现错误的值

6
我得到了错误的频率,我不明白为什么会出现错误的值。我按照StackOverflow上的指示进行计算。我使用了来自http://introcs.cs.princeton.edu/java/97data/FFT.java.html的FFT和http://introcs.cs.princeton.edu/java/97data/Complex.java.html的complex。
audioRec.startRecording();
audioRec.read(bufferByte, 0,bufferSize);
for(int i=0;i<bufferSize;i++){
    bufferDouble[i]=(double)bufferByte[i];    
    }
Complex[] fftArray = new Complex[bufferSize];
    for(int i=0;i<bufferSize;i++){
    fftArray[i]=new Complex(bufferDouble[i],0);
    }
    FFT.fft(fftArray);
double[] magnitude=new double[bufferSize];
for(int i=0;i<bufferSize;i++){
      magnitude[i] = Math.sqrt((fftArray[i].re()*fftArray[i].re()) + (fftArray[i].im()*fftArray[i].im()));
    }
double max = 0.0;
int index = -1;
for(int j=0;j<bufferSize;j++){
    if(max < magnitude[j]){
            max = magnitude[j];
        index = j;
        }
    }
    final int peak=index * sampleRate/bufferSize;
    Log.v(TAG2, "Peak Frequency = " + index * sampleRate/bufferSize);
    handler.post(new Runnable() {
            public void run() {
                textView.append("---"+peak+"---");
            }
        });

我得到了像21000、18976、40222、30283等数值...

请帮忙...

谢谢。


2
你应该尝试在已知合成正弦波的干净数据上运行。你似乎没有关注幅度,所以你的结果可能只是噪音。而且不清楚你是否使用了窗函数。 - Chris Stratton
你的采样率和缓冲区大小是多少?你的输入信号是什么样子的? - hotpaw2
2
放下麦克风,用可配置频率的计算正弦波进行实验,直到获得正确结果。当您能够恢复时,显示幅度并注意即使输入恒定时检测到的幅度如何随频率变化而变化...为了解决这个问题,请阅读有关窗口函数的相关资料。 - Chris Stratton
用一个计算正弦波的程序替换麦克风,并进行实验以了解所有这些是如何工作的。如果你不明白这意味着什么,那么在开始玩FFT之前,你需要进行大量阅读和研究。 - Chris Stratton
1
@ChrisStratton 这是计算正弦波的方法: '缓冲区大小=4096; 采样大小=44100; for(int i=0;i<bufferSize;i++){ pure_sine[i]=Math.sin((2*Math.PI*i)/sampleSize); Log.i(Tag, "sine_Wave=="+pure_sine[i]); // disp.append(pure_sine[i]+"-->");}' - Pandian
显示剩余5条评论
1个回答

2
您的源代码几乎没有问题。唯一的问题是您通过完整频谱搜索峰值,即从0到Fs/2到Fs。
对于任何实值输入信号(您已经拥有),Fs/2和Fs(=采样频率)之间的频谱是0到Fs/2之间频谱的精确镜像(我发现this nice background explanation)。因此,对于每个频率,存在两个具有几乎相同振幅的峰值。我写“几乎”是因为由于有限的机器精度,它们不一定完全相同。因此,您随机找到第一个半频谱中的峰值,其中包含纳奎斯特频率以下的频率,或者在第二个半频谱中找到具有纳奎斯特频率以上的频率。
如果您想自己纠正错误,请停止阅读此处。否则,请继续:
只需替换
for(int j=0;j<bufferSize;j++){

使用

for(int j=0;j<=bufferSize/2;j++){

在您提供的源代码中。
附注:通常最好对分析缓冲区应用窗函数(例如汉明窗),但对于您的峰值拾取应用程序,它不会改变结果太多。

抱歉耽搁了一下,我已经放弃FFT并采用Zero Crossing。请参考此链接:https://github.com/gast-lib/gast-lib/blob/master/library/src/root/gast/audio/processing/ZeroCrossing.java。 在频率检测方面似乎没有太大的差异。如果您有任何建议,请与我分享。谢谢。 - Pandian
我会查看链接。同时,您可以接受我的答案,因为很可能您算法的错误值也是通过搜索镜像频谱得出的。这对其他读者可能也很有趣。 - Hartmut Pfitzinger
实际上,这种粗略的零交叉分析只有在谐波与基频相比非常低且信噪比良好的情况下,并且只有在分析单声道信号时才有效。在所有其他情况下,估计结果会更或多或少变差。 - Hartmut Pfitzinger
非常感谢。我已经尝试过了,但仍然有很多噪音。请清楚地解释一下。 - Pandian
通过这里描述的基于FFT的提取,还是从这些评论中的零交叉? - Hartmut Pfitzinger
显示剩余4条评论

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