前言
在信号处理中,滤波器是不可或缺的工具。通过过滤掉特定频率的信号成分,我们可以获得更清晰、更有用的数据。在本文中,我们将展示如何在Windows平台下,使用Visual Studio(VS)集成开发环境(IDE),借助FFTW库实现高效的信号滤波。我们将涵盖低通、高通和带通滤波器的实现。
1. 为什么选择FFTW库?
FFTW(Fastest Fourier Transform in the West)是一个广泛使用的C库,专门用于计算离散傅里叶变换(FFT)。FFT将时域信号转换到频域,在频域中,我们可以轻松地对信号进行滤波处理。FFTW提供了高效的实现,能够在大多数平台上运行,并且支持多种变换类型。
2. 安装FFTW库
在Windows平台下,安装FFTW库的过程稍微复杂一些。我们将通过以下步骤来安装FFTW并在Visual Studio中配置它。
步骤 1:下载FFTW库
- 访问FFTW官方网站:FFTW官网
- 下载Windows版的FFTW库(通常是一个压缩文件)。选择适合Visual Studio版本(如VS 2019或VS 2022)的预编译版本。
-
找到对应的版本,进行下载
-
解压下载的文件,将其保存到选择的文件夹中。发现没有lib库,需要本地编译处理,找到 lib.exe的目录 加入环境变量
# D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.42.34433\bin\Hostx64\x64\lib.exe
C:\Users\DN0>lib
Microsoft (R) Library Manager Version 14.42.34433.0
Copyright (C) Microsoft Corporation. All rights reserved.
用法: LIB [选项] [文件]
选项:
/DEF[:文件名]
/ERRORREPORT:{NONE|PROMPT|QUEUE|SEND}
/EXPORT:符号
/EXTRACT:成员名
/INCLUDE:符号
/LIBPATH:目录
/LINKREPRO:dir
/LINKREPROFULLPATHRSP:filename
/LINKREPROTARGET:filename
/LIST[:文件名]
/LTCG
/MACHINE:{ARM|ARM64|ARM64X|EBC|X64|X86}
/NAME:文件名
/NODEFAULTLIB[:库]
/NOLOGO
/OUT:文件名
/REMOVE:成员名
/SUBSYSTEM:{BOOT_APPLICATION|CONSOLE|EFI_APPLICATION|
EFI_BOOT_SERVICE_DRIVER|EFI_ROM|EFI_RUNTIME_DRIVER|
NATIVE|POSIX|WINDOWS|WINDOWSCE}[,#[.##]]
/VERBOSE
/WX[:NO]
/WX[:nnnn[,nnnn...]]
C:\Users\DN0>
- cd到解压目录执行编译
lib /machine:x64 /def:libfftw3-3.def
lib /machine:x64 /def:libfftw3f-3.def
lib /machine:x64 /def:libfftw3l-3.def
- 生成我们需要的
libfftw3-3.lib
libfftw3f-3.lib
libfftw3l-3.lib
步骤 2:配置Visual Studio
-
打开Visual Studio。
-
创建一个新的C++控制台应用程序项目 FFTWCpp,拷贝fftw-3.3.5-dll64到项目下。
-
配置FFTW库:
-
右键点击项目,选择“属性”。
-
在“C/C++” -> “常规” -> “附加包含目录”中,添加FFTW的头文件目录(例如:fftw-3.3.5-dll64)。
-
在“链接器” -> “常规” -> “附加库目录”中,添加FFTW的库文件目录(例如:fftw-3.3.5-dll64)。
-
在“链接器” -> “输入” -> “附加依赖项”中,添加FFTW的静态库文件(例如:fftw-3.3.5-dll64中的lib文件libfftw3-3.lib 、libfftw3l-3.lib、 libfftw3f-3.lib)。
-
步骤 3:添加FFTW的DLL文件(如果使用动态链接)
- 如果使用动态链接版本的FFTW库,需要确保将相应的.dll文件放到可执行文件的目录中。通常,这些文件位于FFTW库的bin目录中(例如:libfftw3-3.dll)。可以使用生成后事件。
xcopy /Y /E /I "$(SolutionDir)fftw-3.3.5-dll64\*.dll" "$(OutDir)"
3. 创建滤波器程序
现在我们已经配置好了环境,可以开始编写代码来实现不同类型的滤波器:低通滤波器、高通滤波器和带通滤波器。
步骤 1:导入头文件并设置FFTW计划
我们需要包括fftw3.h头文件,并定义FFT和IFFT的处理函数。首先,确保在代码开头包含以下头文件:
#include <iostream>
#include <fftw3.h>
#include <vector>
#include <cmath>
#include <complex>
步骤 2:FFT与IFFT实现
在进行滤波之前,我们需要将时域信号转换到频域,并且在滤波处理之后将其转换回时域。
// FFT 处理
void fft_process(const std::vector<double>& signal, std::vector<std::complex<double>>& frequency_domain) {
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);
fftw_plan plan = fftw_plan_dft_r2c_1d(N, (double*)signal.data(), out, FFTW_ESTIMATE);
fftw_execute(plan);
// 将频域信号保存为复数形式
for (int i = 0; i < N; ++i) {
frequency_domain[i] = std::complex<double>(out[i][0], out[i][1]);
}
fftw_destroy_plan(plan);
fftw_free(in);
fftw_free(out);
}
// IFFT 处理
void ifft_process(std::vector<double>& signal, const std::vector<std::complex<double>>& frequency_domain) {
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);
fftw_plan plan = fftw_plan_dft_c2r_1d(N, in, (double*)out, FFTW_ESTIMATE);
for (int i = 0; i < N; ++i) {
in[i][0] = frequency_domain[i].real();
in[i][1] = frequency_domain[i].imag();
}
fftw_execute(plan);
// 归一化并保存回时域信号
for (int i = 0; i < N; ++i) {
signal[i] = out[i][0] / N;
}
fftw_destroy_plan(plan);
fftw_free(in);
fftw_free(out);
}
步骤 3:实现滤波器
低通滤波器
低通滤波器允许低频通过,抑制高频。我们将频域中的高于某个截止频率的部分置为零。
void low_pass_filter(std::vector<std::complex<double>>& frequency_domain, double cutoff_frequency, int sample_rate) {
int N = frequency_domain.size();
int cutoff_index = static_cast<int>(cutoff_frequency * N / sample_rate);
for (int i = cutoff_index; i < N - cutoff_index; ++i) {
frequency_domain[i] = std::complex<double>(0, 0);
}
}
高通滤波器
高通滤波器允许高频通过,抑制低频。我们将频域中的低于某个截止频率的部分置为零。
void high_pass_filter(std::vector<std::complex<double>>& frequency_domain, double cutoff_frequency, int sample_rate) {
int N = frequency_domain.size();
int cutoff_index = static_cast<int>(cutoff_frequency * N / sample_rate);
for (int i = 0; i < cutoff_index; ++i) {
frequency_domain[i] = std::complex<double>(0, 0);
}
}
带通滤波器
带通滤波器允许一定频段内的信号通过,抑制低频和高频。我们需要设置一个低频和高频的截止频率。
void band_pass_filter(std::vector<std::complex<double>>& frequency_domain, double low_cutoff, double high_cutoff, int sample_rate) {
int N = frequency_domain.size();
int low_cutoff_index = static_cast<int>(low_cutoff * N / sample_rate);
int high_cutoff_index = static_cast<int>(high_cutoff * N / sample_rate);
for (int i = 0; i < low_cutoff_index; ++i) {
frequency_domain[i] = std::complex<double>(0, 0);
}
for (int i = high_cutoff_index; i < N - high_cutoff_index; ++i) {
frequency_domain[i] = std::complex<double>(0, 0);
}
}
步骤 4:主函数
在主函数中,我们将创建一个简单的示例信号,并对其应用滤波器。
int main() {
// 信号参数
int N = 16; // 信号长度
int sample_rate = 100; // 采样率
double cutoff_frequency = 20.0; // 截止频率(低通、高通)
// 示例信号(时域)
std::vector<double> signal = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 90, 80, 70, 60, 50, 40};
std::vector<std::complex<double>> frequency_domain(N);
// FFT:从时域转换到频域
fft_process(signal, frequency_domain);
// 低通滤波器处理
low_pass_filter(frequency_domain, cutoff_frequency, sample_rate);
// IFFT:从频域转换回时域
std::vector<double> filtered_signal(N);
ifft_process(filtered_signal, frequency_domain);
// 打印处理后的时域信号
std::cout << "Filtered Time Domain Signal: ";
for (const auto& val : filtered_signal) {
std::cout << val << " ";
}
std::cout << std::endl;
return 0;
}
4. 总结
在这篇文章中,我们展示了如何在Windows平台下,使用Visual Studio和FFTW库实现低通、高通和带通滤波器。通过FFT和IFFT转换,我们能够有效地从时域信号中提取或抑制特定频率的成分,从而达到滤波的目的。这些滤波技术在音频、图像和其他信号处理应用中非常常见和有用。