如何对经过FFT算法的复数数据进行修改和反演?
How to modify and then inverse complex data that went through FFT algorithm?
我写了一个基于 FFT 算法的 low-pass 滤波器。
首先,我使用正向 FFT 处理输入数据,然后降低部分结果频谱的“音量”,然后将数据放入逆向 FFT,最后像这样对数据进行归一化:
fn lowpass_filter(data: &[f32], sampling_rate: f32, cutoff_frequency: f32) -> Vec<f32> {
let len = data.len();
// step 1:
let mut spectrum = fft::forward(&data);
// step 2:
let start: usize = (len as f32 * (cutoff_frequency / sampling_rate * 2.0)) as usize;
let diff = len - start;
// what to add to multiplikator in each iteration
let step: f32 = PI / diff as f32;
// reduce volume of frequencies after cutoff_frequency in spectrum
let mut multiplikator: f32 = 0.0;
for i in start..len {
let mul = (multiplikator.cos() + 1.0) / 2.0;
spectrum[i] *= mul;
multiplikator += step;
}
// step 3:
let data = fft::inverse(&spectrum);
// step 4:
fft::normalize(&data, true)
}
当仅执行步骤 1、3 和 4 时它可以工作,但唯一的问题是在对反演数据进行归一化后,我只能得到数据的绝对值,因此 65Hz 正弦波如下所示:
我面临的主要问题是,我不知道如何减少频谱中特定频率的音量。
当像第 3 步那样降低所述频率时,将 cutoff_frequency
设置为 100.0Hz
的 65Hz 正弦波通过低通滤波器的可视化效果如下所示:
我做错了什么?
有关已定义函数的更多信息:
use rustfft::FftPlanner;
pub use rustfft::num_complex::Complex;
pub fn forward(data: &[f32]) -> Vec<Complex<f32>> {
let length = data.len();
// conversion to complex numbers
let mut buffer: Vec<Complex<f32>> = Vec::new();
for d in data {
buffer.push(Complex{re: *d, im: 0.0});
}
// creates a planner
let mut planner = FftPlanner::<f32>::new();
// creates a FFT
let fft = planner.plan_fft_forward(length);
//input.append(&mut data.to_vec());
fft.process(&mut buffer);
buffer
}
pub fn inverse(data: &[Complex<f32>]) -> Vec<Complex<f32>> {
let length = data.len();
let mut data = data.to_vec();
// creates a planner
let mut planner = FftPlanner::<f32>::new();
// creates a FFT
let fft = planner.plan_fft_inverse(length);
fft.process(&mut data);
data.to_vec()
}
pub fn normalize(data: &[Complex<f32>], normalize: bool) -> Vec<f32> {
let len: f32 = data.len() as f32;
let norm = data
.iter()
.map(|x| x.norm() / len)
.collect();
norm
}
我正在使用 rustfft 进行实际处理。
您的代码有两个问题:
由于要处理实数据(即虚部为0
的数据),前向FFT的输出是对称的,需要对匹配频率应用相同的系数(即 spectrum[i]
和 spectrum[spectrum.len() - i]
,记住 spectrum[0]
是独立的,如果长度是偶数,spectrum[spectrum.len()/2]
也是独立的。
如果频率对称,逆FFT的结果应该是实数(即虚部应该是0
±小舍入误差)。因此,您的 normalize
函数应该使用 x.re
而不是 x.norm()
,这将允许它保留其符号。
解决这些问题并向 lowpass_filter
函数添加 cutoff_end_freq
后,代码如下所示:
pub fn lowpass_filter(data: &[f32], sampling_rate: f32, cutoff_start_freq: f32, cutoff_end_freq: f32) -> Vec<f32> {
let len = data.len();
let spectrum_len = len / 2;
let mut spectrum = fft::forward(&data);
assert!(len == spectrum.len());
let start: usize = (spectrum_len as f32 * (cutoff_start_freq / sampling_rate * 2.0)) as usize;
let end: usize = (spectrum_len as f32 * (cutoff_end_freq / sampling_rate * 2.0)) as usize;
let diff = end - start;
// what to add to multiplikator in each iteration
let step: f32 = PI / diff as f32;
let mut multiplikator: f32 = 0.0;
for i in start..=end {
let mul = (multiplikator.cos() + 1.0) / 2.0;
spectrum[i] *= mul;
spectrum[len - i - 1] *= mul;
multiplikator += step;
}
for i in end..spectrum_len {
spectrum[i] *= 0.0;
spectrum[len - i - 1] *= 0.0;
}
let data = fft::inverse(&spectrum);
fft::get_real(&data)
}
normalize
函数现在重命名为 get_real
:
pub fn get_real(data: &[Complex<f32>]) -> Vec<f32> {
let len: f32 = data.len() as f32;
let norm = data
.iter()
.map(|x| x.re / len)
.collect();
norm
}
现在它工作得很好,但是当可视化阈值之外的频率时,两端仍然有交替的“尖峰”:
我只是在上面加了一个汉宁窗效应,因为我听说 FFT 希望数据是连续的。
它有效,缺点是侧面的很多信息都丢失了。
是否有更好的方法来规避这个问题,还是低通还有问题?
我写了一个基于 FFT 算法的 low-pass 滤波器。
首先,我使用正向 FFT 处理输入数据,然后降低部分结果频谱的“音量”,然后将数据放入逆向 FFT,最后像这样对数据进行归一化:
fn lowpass_filter(data: &[f32], sampling_rate: f32, cutoff_frequency: f32) -> Vec<f32> {
let len = data.len();
// step 1:
let mut spectrum = fft::forward(&data);
// step 2:
let start: usize = (len as f32 * (cutoff_frequency / sampling_rate * 2.0)) as usize;
let diff = len - start;
// what to add to multiplikator in each iteration
let step: f32 = PI / diff as f32;
// reduce volume of frequencies after cutoff_frequency in spectrum
let mut multiplikator: f32 = 0.0;
for i in start..len {
let mul = (multiplikator.cos() + 1.0) / 2.0;
spectrum[i] *= mul;
multiplikator += step;
}
// step 3:
let data = fft::inverse(&spectrum);
// step 4:
fft::normalize(&data, true)
}
当仅执行步骤 1、3 和 4 时它可以工作,但唯一的问题是在对反演数据进行归一化后,我只能得到数据的绝对值,因此 65Hz 正弦波如下所示:
我面临的主要问题是,我不知道如何减少频谱中特定频率的音量。
当像第 3 步那样降低所述频率时,将 cutoff_frequency
设置为 100.0Hz
的 65Hz 正弦波通过低通滤波器的可视化效果如下所示:
我做错了什么?
有关已定义函数的更多信息:
use rustfft::FftPlanner;
pub use rustfft::num_complex::Complex;
pub fn forward(data: &[f32]) -> Vec<Complex<f32>> {
let length = data.len();
// conversion to complex numbers
let mut buffer: Vec<Complex<f32>> = Vec::new();
for d in data {
buffer.push(Complex{re: *d, im: 0.0});
}
// creates a planner
let mut planner = FftPlanner::<f32>::new();
// creates a FFT
let fft = planner.plan_fft_forward(length);
//input.append(&mut data.to_vec());
fft.process(&mut buffer);
buffer
}
pub fn inverse(data: &[Complex<f32>]) -> Vec<Complex<f32>> {
let length = data.len();
let mut data = data.to_vec();
// creates a planner
let mut planner = FftPlanner::<f32>::new();
// creates a FFT
let fft = planner.plan_fft_inverse(length);
fft.process(&mut data);
data.to_vec()
}
pub fn normalize(data: &[Complex<f32>], normalize: bool) -> Vec<f32> {
let len: f32 = data.len() as f32;
let norm = data
.iter()
.map(|x| x.norm() / len)
.collect();
norm
}
我正在使用 rustfft 进行实际处理。
您的代码有两个问题:
由于要处理实数据(即虚部为
0
的数据),前向FFT的输出是对称的,需要对匹配频率应用相同的系数(即spectrum[i]
和spectrum[spectrum.len() - i]
,记住spectrum[0]
是独立的,如果长度是偶数,spectrum[spectrum.len()/2]
也是独立的。如果频率对称,逆FFT的结果应该是实数(即虚部应该是
0
±小舍入误差)。因此,您的normalize
函数应该使用x.re
而不是x.norm()
,这将允许它保留其符号。
解决这些问题并向 lowpass_filter
函数添加 cutoff_end_freq
后,代码如下所示:
pub fn lowpass_filter(data: &[f32], sampling_rate: f32, cutoff_start_freq: f32, cutoff_end_freq: f32) -> Vec<f32> {
let len = data.len();
let spectrum_len = len / 2;
let mut spectrum = fft::forward(&data);
assert!(len == spectrum.len());
let start: usize = (spectrum_len as f32 * (cutoff_start_freq / sampling_rate * 2.0)) as usize;
let end: usize = (spectrum_len as f32 * (cutoff_end_freq / sampling_rate * 2.0)) as usize;
let diff = end - start;
// what to add to multiplikator in each iteration
let step: f32 = PI / diff as f32;
let mut multiplikator: f32 = 0.0;
for i in start..=end {
let mul = (multiplikator.cos() + 1.0) / 2.0;
spectrum[i] *= mul;
spectrum[len - i - 1] *= mul;
multiplikator += step;
}
for i in end..spectrum_len {
spectrum[i] *= 0.0;
spectrum[len - i - 1] *= 0.0;
}
let data = fft::inverse(&spectrum);
fft::get_real(&data)
}
normalize
函数现在重命名为 get_real
:
pub fn get_real(data: &[Complex<f32>]) -> Vec<f32> {
let len: f32 = data.len() as f32;
let norm = data
.iter()
.map(|x| x.re / len)
.collect();
norm
}
现在它工作得很好,但是当可视化阈值之外的频率时,两端仍然有交替的“尖峰”:
我只是在上面加了一个汉宁窗效应,因为我听说 FFT 希望数据是连续的。 它有效,缺点是侧面的很多信息都丢失了。
是否有更好的方法来规避这个问题,还是低通还有问题?