FFTW只会返回无穷大、接近零或负无穷的值。

3

我将尝试计算一个20hz正弦波的离散傅里叶变换(DFT)。

首先,我使用正弦函数在20 hz下填充了一个std :: vector中的10个周期:

std::vector<double> sinx;
double samplerate = 1000.0;
double frequency = 20.0;
double num_cycles = 10.0;
for (int i=0; i<samplerate/frequency*num_cycles; i++){
    sinx.push_back(sin(2.0*M_PI*((double)i)*frequency/samplerate));
}
double N = sinx.size();

然后我初始化FFTW的输入和输出数组以及FFTW计划:

fftw_complex *in, *out;
fftw_plan p;

in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)* N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);

然后,我使用正弦波函数的值来填充输入数组:
for (int i=0; i<N; i++){
    in[i][0] = sinx[i];
}

然后我计算FFT:

fftw_execute(p);

计算完成FFT后,我会检查结果并进行清理。

    double hz_per_index = samplerate/2.0/N*num_cycles;
for (int i=0; i<N/num_cycles; i++){
    std::cout<<"hz: "<<hz_per_index*i<<" out: "<<fabs(out[i][0])<<" "<<out[i][1]<<" in: "<<in[i][0]<<std::endl;
}

我预期会在20hz处看到一个峰值,但是却没有任何峰值。所有返回的值要么趋近于零,要么是无穷大或负无穷大。我的第一个想法是输入正在被覆盖,但在计算后检查输入是正确的。我尝试以不同的模式运行FFTW,包括FFTW_FORWARD、FFTW_BACKWARD、FFTW_ESTIMATE和FFTW_RELATIVE,但没有什么帮助。我尝试在不同的频率、采样率和循环次数下计算不同的正弦函数。

可悲的是,我曾经让它工作过。然后我继续工作,发现它不起作用了,然后重新打开了那个工作的文件,现在它又不起作用了!

还有其他人遇到这个问题吗?

编辑:根据建议的新代码

它仍然不起作用。我尝试将复数的虚部初始化为0,但仍然存在同样的问题,所以这里是利用fftw库的实入/复出函数的代码。

基本上,我得到的一切都是0。 以下是显示输入、输出和hz值的所有5个周期的完整输出:

https://gist.github.com/anonymous/ed419f4cfb43887c810e

double *in;
fftw_complex* out;
fftw_plan p;

std::vector<double> sinx;
double samplerate = 1000.0;
double frequency = 20.0;
double num_cycles = 5.0;
for (int i=0; i<samplerate/frequency*num_cycles; i++){
    sinx.push_back(sin(2.0*M_PI*((double)i)*frequency/samplerate));
}
int N = sinx.size();

in = (double*) fftw_malloc(sizeof(double)* N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
p = fftw_plan_dft_r2c_1d(N, in, out, FFTW_ESTIMATE);

for (int i=0; i<N; i++){
    in[i] = sinx[i];
}

fftw_execute(p);

double hz_per_index = samplerate/2.0/N*num_cycles;
for (int i=0; i<N; i++){
    std::cout<<"hz: "<<hz_per_index * (i % (int)(N/num_cycles))<<" out: "<<out[i][0]<<" in: "<<in[i]<<std::endl;
}

fftw_destroy_plan(p);
fftw_free(in); fftw_free(out);

return 0;

}


你的输出函数只输出了500个结果中的前50个。看起来你试图输出每5个结果之一或者类似的东西。 - M.M
请将“N”更改为整数类型。 - NicholasM
我之前只输出了输入的第一个完整循环。现在代码输出了所有的循环,并且我创建了一个gist,展示了输入、输出和对应的周期赫兹值。 - sugarmuff
2个回答

6
你正在分配一个由fftw_complex组成的数组,它由两个元素组成——实部和虚部,但你只初始化了每个样本的实部。这可能导致复数部分包含随机数据,从而导致意外的疯狂结果!
如果你不需要处理复杂的样本——这很可能是——你可以使用FFTW的实数据DFT函数之一,它以double数组作为输入。否则,请将所有复数部分(in[i][1])初始化为零。
此外,你没有查看输出的复数部分。那里可能有一些重要的东西你会错过。

伟大的想法,我确信这将解决问题,但却毫无作用!尽管如此,我还是实施了你的建议。您可以在编辑后的帖子中看到新代码。 - sugarmuff
检查一下你得到的转换输出的复杂组件。我认为那里面有些问题。 - user149341
输出的虚部看起来都是乱码,除了在50赫兹时只有第一个周期出现随机的-125值? - sugarmuff
2
那就是你的问题所在。它显示为复杂组件,因为你的信号在原点周围是反对称的(因为它是正弦波而不是余弦波)。 - user149341

2

你输入的是一个纯实正弦波的10个周期。这意味着:

  • 输出是纯虚数,由于奇函数正弦(sin(x) = -sin(-x))的影响。 如果是偶函数余弦,输出就是纯实数,因为余弦是偶函数(cos(x) = cos(-x))。
  • 只有两条频率线,一条在正频带,一条在负频带。
  • 因为你的输入有10个周期,你的线应该出现在out[10][1]和out[N-10][1]
  • 因为你没有对输出进行归一化,样本数量为500, 所以幅度应该是out[10][1] = -250和out[490][1] = +250。
  • 其他所有值都应该为零。

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