use plotters::prelude::*;
use rustfft::{
num_complex::{Complex, ComplexFloat},
FftPlanner,
};
const PLOT_LINE_COLOR: RGBColor = RGBColor(0, 0, 0);
pub struct ChartOptions {
pub x_inc: f32,
}
impl Default for ChartOptions {
fn default() -> Self {
Self { x_inc: 1.0 }
}
}
pub fn draw_chart(file: &str, data: Vec<(f32, f32)>, opts: ChartOptions) -> anyhow::Result<()> {
let root = SVGBackend::new(file, (1024, 768)).into_drawing_area();
root.fill(&WHITE)?;
let mut y_min = -1.0;
let mut y_max = 1.0;
for (_x, y) in data.iter() {
if *y < y_min {
y_min = *y;
}
if *y > y_max {
y_max = *y;
}
}
let mut chart = ChartBuilder::on(&root)
.x_label_area_size(28)
.y_label_area_size(28)
.margin(20)
.build_cartesian_2d(0.0..data.len() as f32, y_min..y_max)?;
chart
.configure_mesh()
.y_labels(10)
.y_label_formatter(&|y: &f32| format!("{:.2}", y))
.x_labels(10)
.x_label_formatter(&|x: &f32| format!("{:.2}", x * opts.x_inc))
.draw()
.expect("failed to draw chart mesh");
chart.draw_series(LineSeries::new(
data.iter().map(|(x, y)| (*x, *y)),
PLOT_LINE_COLOR,
))?;
root.present()?;
Ok(())
}
pub fn do_fft(data: &[f32]) -> Vec<f32> {
let mut planner = FftPlanner::<f32>::new();
let fft = planner.plan_fft_forward(data.len());
let mut buffer: Vec<_> = data.iter().map(|re| Complex { re: *re, im: 0.0 }).collect();
fft.process(&mut buffer);
let res = buffer
.drain(..buffer.len() / 2)
.map(|c| c.abs() * 2.0 / data.len() as f32)
.collect();
res
}