1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Static spectrum analysis: print spectrum to PNG file.

use plotters::prelude::*;
use std::collections::BTreeMap;
use std::path::PathBuf;

pub fn spectrum_static_plotters_png_visualize(
    frequency_spectrum: &BTreeMap<u32, f32>,
    directory: &str,
    filename: &str,
) {
    // assert no NAN
    assert!(
        !frequency_spectrum.iter().any(|(_, f)| f.is_nan()),
        "There are NAN-values in the spectrum!"
    );

    // find maximum for graphics scaling
    let mut max = 0.0;
    for mag in frequency_spectrum.values() {
        if *mag > max {
            max = *mag;
        }
    }

    let max_frequency = *frequency_spectrum
        .iter()
        .skip(frequency_spectrum.len() - 2)
        .last()
        .unwrap()
        .0;

    let mut path = PathBuf::new();
    path.push(directory);
    path.push(filename);

    let mut width = frequency_spectrum.len() as u32;
    if width < 700 {
        width = 700;
    }

    let height = if width < 700 {
        (width as f32 / 0.8) as u32
    } else {
        700
    };

    let root = BitMapBackend::new(&path, (width as u32, height)).into_drawing_area();
    root.fill(&WHITE).unwrap();
    let mut chart = ChartBuilder::on(&root)
        .caption("y=f magnitudes of sample", ("sans-serif", 20).into_font())
        .margin(5)
        .x_label_area_size(60)
        .y_label_area_size(60)
        .build_cartesian_2d(
            0.0..(max_frequency as f32), /*.log10()*/
            0.0..max as f32,
        )
        .unwrap();

    chart.configure_mesh().draw().unwrap();

    chart
        .draw_series(LineSeries::new(
            // (-50..=50).map(|x| x as f32 / 50.0).map(|x| (x, x * x)),
            frequency_spectrum
                .iter()
                .into_iter()
                .map(|(frequency, magnitude)| ((*frequency as f32) /*.log10()*/, *magnitude)),
            &RED,
        ))
        .unwrap()
        .label("frequency magnitude")
        .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], &RED));

    chart
        .configure_series_labels()
        .background_style(&WHITE.mix(0.8))
        .border_style(&BLACK)
        .draw()
        .unwrap();
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::testutil::TEST_OUT_DIR;
    use std::f32::NAN;

    #[test]
    fn test_visualize_sine_waves_spectrum_plotters() {
        let mut spectrum = BTreeMap::new();
        spectrum.insert(0, 0.0);
        spectrum.insert(10, 5.0);
        spectrum.insert(20, 20.0);
        spectrum.insert(30, 40.0);
        spectrum.insert(40, 80.0);
        spectrum.insert(50, 120.0);
        spectrum.insert(55, 130.0);
        spectrum.insert(60, 140.0);
        spectrum.insert(65, 130.0);
        spectrum.insert(70, 120.0);
        spectrum.insert(80, 80.0);
        spectrum.insert(90, 40.0);
        spectrum.insert(100, 20.0);
        spectrum.insert(110, 5.0);
        spectrum.insert(120, 0.0);
        spectrum.insert(130, 0.0);

        spectrum_static_plotters_png_visualize(
            &spectrum,
            TEST_OUT_DIR,
            "spectrum_60hz_peak_plotters_visualization.png",
        );
    }

    #[allow(non_snake_case)]
    #[test]
    #[should_panic]
    fn test_panic_on_NAN() {
        let mut spectrum = BTreeMap::new();
        spectrum.insert(0, NAN);

        spectrum_static_plotters_png_visualize(
            &spectrum,
            TEST_OUT_DIR,
            "spectrum_60hz_peak_plotters_visualization_NAN.png",
        );
    }
}