RustのFFT crateで逆FFTを行う

rustfftには逆FFTの機能もあるので、FFT結果を逆FFTして元の波形と比較してみます

<やってること>

① 二つの三角関数の合成のVec作成してreに代入、imは全部ゼロ

② 作成したComplex Vecからre部分抽出のVecを作成してグラフ化

③  FFT実行

④ FFT実行結果VecのnormのVec作成してグラフ化

⑤ FFT実行結果Vecをそのまま使用して、iFFT実行

⑥ iFFT実行結果のComplex Vecからre部分抽出のVecを作成してグラフ化

 

<最終的なコード>

// Computes a forward FFT
//
use plotters::prelude::*;
use rustfft::{num_complex::Complex, FftPlanner};
use std::f32::consts::PI;
use std::time::Instant;

const N: usize = 512; // FFT size
const SAMPLE_RATE: f32 = 256.0; // sampling rate
const INPUT_FREQ: f32 = 20.0; // input frequency

// prepare target data
fn complex_vector(length: usize, dt: f32, frequency: f32) -> Vec<Complex<f32>> {
    (0..length)
        .map(|i| {
            let t = i as f32 * dt;
            let phase = frequency * 2.0 * PI * t;
            let phase2 = 3.0 * frequency * 2.0 * PI * t;
            Complex::new(phase.sin() + phase2.sin(), 0.0)
        })
        .collect()
}

fn re_vector(complex_numbers: Vec<Complex<f32>>) -> Vec<f32> {
    complex_numbers.iter().map(|z| z.re).collect()
}

fn draw(x: Vec<usize>, y: Vec<f32>, f_name: &str, cap: &str) -> Result<(), Box<dyn std::error::Error>> {
    let image_width = 1080;
    let image_height = 720;

    let root = BitMapBackend::new(f_name, (image_width, image_height)).into_drawing_area();

    root.fill(&WHITE)?;

    //   https://qiita.com/lo48576/items/343ca40a03c3b86b67cb
    let (y_min, y_max) = y
        .iter()
        .fold((0.0 / 0.0, 0.0 / 0.0), |(m, n), v| (v.min(m), v.max(n)));

    let caption = cap;
    let font = ("sans-serif", 20);

    let mut chart = ChartBuilder::on(&root)
        .caption(caption, font.into_font())
        .margin(10)
        .x_label_area_size(16)
        .y_label_area_size(42)
        .build_cartesian_2d(*x.first().unwrap()..*x.last().unwrap(), y_min..y_max)?;

    chart.configure_mesh().draw()?;

    let line_series = LineSeries::new(x.iter().zip(y.iter()).map(|(x, y)| (*x, *y)), &RED);
    chart.draw_series(line_series)?;

    Ok(())
}

fn main() {
    // fft data preparation
    let sr = 1.0 / SAMPLE_RATE;
    let mut buffer = complex_vector(N, sr, INPUT_FREQ);

    // draw input signal
    let buf_re = re_vector(buffer.clone());

    let n = N;
    let f_input = "plot_input.png";
    let cap = "fft input";
    let x: Vec<usize> = (1..=n).collect();
    let _ = draw(x, buf_re, &f_input, &cap);

    // fft mode setting
    let mut planner = FftPlanner::new();
    let fft = planner.plan_fft_forward(N);

    let start_time = Instant::now();
    // fft exec
    fft.process(&mut buffer);

    let end_time = Instant::now();
    println!("Elapsed time: {:?}", end_time.duration_since(start_time));

    // absolute value calc
    let y: Vec<f32> = buffer.iter().map(|z| z.norm()).collect();

    // drwa fft graph output
    let n = N;
    let file_fft = "plot_fft.png";
    let cap = "fft result";
    let x: Vec<usize> = (1..=n).collect();
    let _ = draw(x, y, &file_fft, &cap);

    // ifft exec
    let ifft = planner.plan_fft_inverse(N);
    ifft.process(&mut buffer);
    // get complex:re
    let y = re_vector(buffer);

    // ifft graph output
    let n = N;
    let file_ifft = "plot_ifft.png";
    let cap = "ifft result";
    let x: Vec<usize> = (1..=n).collect();
    let _ = draw(x, y, &file_ifft, &cap);

}

<作成されたグラフ>

②のグラフ

④のグラフ

⑥のグラフ

確かに元の波形が逆FFTで再現できています、サンプリング周波数を2のn乗分の1にしたのでノイズなく綺麗なスペクトルが現れています。

スペクトル計算ではnorm計算が必要ですが、iFFTの結果の波形はVecのre部分だけを使います

 

admin

カテゴリーRust