赞
踩
本文主要记录FFTW的安装过程,FFTW的简单示例代码。并实现一个输入和输出为一维实数的数字滤波器
FFT的一些理论知识可以参考:
从头到尾彻底理解傅里叶变换算法
傅里叶分析之掐死教程(完整版)
先展示效果。如图一为滤波前后时域波形对比,经过低通滤波后,波形变的比较平滑。
滤除了10kHz以上的高频,以下为滤波前后频域波形对比。
接下来是使用FFTW使用滤波的具体步骤。
FFTW官网下载源码 http://www.fftw.org/download.html
我是用在linux系统下,下载tar包,并解压
tar -zxvf ./fftw-3.3.10.tar.gz
cd ./fftw-3.3.10/
进行安装。一般使用默认配置安装即可(默认不使能多线程库、实数为double类型),只需如下3步
若要定制化修改配置可参考官方的 配置说明文档
./configure
make
make install
默认会安装到 /usr/local 路径下,其中 /usr/local/include放置头文件,/usr/local/lib放置.a文件
FFTW安装完成,可直接在编译时链接。
贴上官方给出的示例,IFFT的只需把fftw_plan_dft_1d()函数的FFTW_FORWARD改为FFTW_BACKWARD
#include <fftw3.h>
...
{
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);
...
fftw_execute(p); /* repeat as needed */
...
fftw_destroy_plan(p);
fftw_free(in); fftw_free(out);
}
一维实数的FFT和IFFT写在一起,为了更好理解功能。实际上转换前后的数据基本(近似)相同
#include <fftw3.h> void fft_real_f(int n, const float *in, float *out) { int i, begin, end; double *real; fftw_complex *complex; fftw_plan plan; // fftw的内存分配方式和mallco类似,但使用SIMD(单指令多数据流)时,fftw_alloc会将数组以更高效的方式对齐 real = fftw_alloc_real(n); complex = fftw_alloc_complex(n/2+1); // 实际只会用到(n/2)+1个complex对象 // Step1:FFT实现时域到频域的转换 plan = fftw_plan_dft_r2c_1d(n, real, complex, FFTW_ESTIMATE); for (i = 0; i < n; i++) { real[i] = in[i]; } // 对长度为n的实数进行FFT,输出的长度为(n/2)-1的复数 fftw_execute(plan); fftw_destroy_plan(plan); // Step3:IFFT实现频域到时域的转换 // 使用FFTW_ESTIMATE构建plan不会破坏输入数据 plan = fftw_plan_dft_c2r_1d(n, complex, real, FFTW_ESTIMATE); fftw_execute(plan); fftw_destroy_plan(plan); for (i = 0; i < n; i++) { // 需除以数据个数,得到滤波后的实数 out[i] = real[i] / n; } fftw_free(real); fftw_free(complex); }
使用GCC编译是命令如下
gcc fft.c -lfftw3 -lm
接下来是实际FFT的应用,实现一个数字滤波器
/** * @Description : 使用FFT进行滤波 * 使用示例: * 原始采样频率为100kHz,采集了10000个点,保存为单精度浮点数。滤除其中20kHz~30kHz的频率 * fft_filter_f(10000, in, out, 100e3, 20e3, 30e3) * @Input : n 输入数据个数 * in 输入数据 * in和out指向不同位置,不改变输入 * in和out指向相同位置,输出将覆盖输入 * sample_rate 原始数据采样频率 Hz * freq_start 需过滤的起始频率 Hz * freq_end 需过滤的截止频率 Hz * 若freq_end大于采样频率的50%,则将滤除大于freq_start的所有高频信号 * @Output : out 输出数据 * in和out指向不同位置,不改变输入 * in和out指向相同位置,输出将覆盖输入 * @Return : 无 */ void fft_filter_f(int n, const float *in, float *out, float sample_rate, float freq_start, float freq_end) { int i, begin, end; double *real; fftw_complex *complex; fftw_plan plan; // fftw的内存分配方式和mallco类似,但使用SIMD(单指令多数据流)时,fftw_alloc会将数组以更高效的方式对齐 real = fftw_alloc_real(n); complex = fftw_alloc_complex(n/2+1); // 实际只会用到(n/2)+1个complex对象 // Step1:FFT实现时域到频域的转换 plan = fftw_plan_dft_r2c_1d(n, real, complex, FFTW_ESTIMATE); for (i = 0; i < n; i++) { real[i] = in[i]; } // 对长度为n的实数进行FFT,输出的长度为(n/2)-1的复数 fftw_execute(plan); fftw_destroy_plan(plan); // Step2:计算需滤波的频率在频域数组中的下标 begin = (int)((freq_start / sample_rate) * n); end = (int)((freq_end / sample_rate) * n); end = end < (n / 2 + 1) ? end : (n / 2 + 1); for (i = begin; i < end; i++) { // 对应的频率分量置为0,即去除该频率 complex[i][0] = 0; complex[i][1] = 0; } // Step3:IFFT实现频域到时域的转换 // 使用FFTW_ESTIMATE构建plan不会破坏输入数据 plan = fftw_plan_dft_c2r_1d(n, complex, real, FFTW_ESTIMATE); fftw_execute(plan); fftw_destroy_plan(plan); // Step4:计算滤波后的时域值 for (i = 0; i < n; i++) { // 需除以数据个数,得到滤波后的实数 out[i] = real[i] / n; } fftw_free(real); fftw_free(complex); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。