快速傅里叶变换(FFT)中的振幅和相位
快速傅里叶变换(FFT)是一种高效计算离散傅里叶变换(DFT)的算法,它将时域信号转换为频域表示,包含振幅和相位信息。
振幅(Amplitude)
FFT结果的振幅表示信号在不同频率分量上的强度:
对于复数FFT结果X[k],振幅计算为:
Amplitude[k] = |X[k]| = sqrt(Re(X[k])^2 + Im(X[k])^2)
对于实值信号的N点FFT,通常只需要前N/2+1个点(由于对称性)
要得到物理上有意义的振幅值,通常需要:
对于非直流分量(k≠0):Amplitude[k] × 2/N
对于直流分量(k=0):Amplitude[0] × 1/N
相位(Phase)
相位表示各频率分量在时间上的偏移:
相位角计算为:
Phase[k] = atan2(Im(X[k]), Re(X[k]))
其中atan2是四象限反正切函数,结果范围在[-π, π]之间
相位信息对于信号重构、时延估计等应用非常重要
示例代码
#include <iostream>
#include <vector>
#include <cmath>
#include <complex>
#include <fftw3.h>const double PI = 3.14159265358979323846;// 计算信号的FFT并返回振幅和相位
void computeFFT(const std::vector<double>& signal,std::vector<double>& amplitudes,std::vector<double>& phases,int sampleRate)
{int N = signal.size();// 分配输入输出数组fftw_complex* in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);fftw_complex* out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);// 准备输入数据(实数信号)for(int i = 0; i < N; ++i) {in[i][0] = signal[i]; // 实部in[i][1] = 0.0; // 虚部}// 创建FFT计划fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);// 执行FFTfftw_execute(plan);// 计算振幅和相位(只计算前N/2+1个点,因为对称)int outputSize = N/2 + 1;amplitudes.resize(outputSize);phases.resize(outputSize);for(int k = 0; k < outputSize; ++k) {// 计算复数幅度double real = out[k][0];double imag = out[k][1];double magnitude = sqrt(real*real + imag*imag);// 振幅(考虑FFT缩放)amplitudes[k] = magnitude / (N/2);if(k == 0) amplitudes[k] /= 2; // DC分量特殊处理// 相位(弧度)phases[k] = atan2(imag, real);}// 清理资源fftw_destroy_plan(plan);fftw_free(in);fftw_free(out);
}// 生成测试信号(包含多个频率分量)
void generateTestSignal(std::vector<double>& signal, int sampleRate, int numSamples)
{signal.resize(numSamples);double dt = 1.0/sampleRate;// 信号包含50Hz和120Hz分量for(int i = 0; i < numSamples; ++i) {double t = i * dt;signal[i] = 0.7 * sin(2*PI*50*t) + sin(2*PI*120*t + PI/3); // 120Hz分量有相位偏移}
}int main() {const int sampleRate = 1000; // 采样率1kHzconst int numSamples = 1024; // 采样点数// 1. 生成测试信号std::vector<double> signal;generateTestSignal(signal, sampleRate, numSamples);// 2. 计算FFT、振幅和相位std::vector<double> amplitudes, phases;computeFFT(signal, amplitudes, phases, sampleRate);// 3. 输出结果(前50个频率点)std::cout << "频率(Hz)\t振幅\t相位(rad)\n";for(int k = 0; k < 50; ++k) {double freq = k * sampleRate / numSamples;std::cout << freq << "\t" << amplitudes[k] << "\t" << phases[k] << "\n";}return 0;
}
输出结果:
程序将输出频率、振幅和相位信息。对于我们的测试信号(包含50Hz和120Hz分量),输出中应该能看到:
在50Hz处有振幅约0.7,相位约0(因为50Hz分量没有初始相位偏移)
在120Hz处有振幅约1.0,相位约π/3(1.0472弧度)
振幅和相位计算
for(int k = 0; k < outputSize; ++k) {double real = out[k][0];double imag = out[k][1];// 振幅计算double magnitude = sqrt(real*real + imag*imag);amplitudes[k] = magnitude / (N/2);if(k == 0) amplitudes[k] /= 2; // DC分量特殊处理// 相位计算phases[k] = atan2(imag, real);
}
频率轴计算
频率分辨率为 sampleRate/N
,第k个点对应的频率为:
double freq = k * sampleRate / numSamples;