Skip to content
Snippets Groups Projects
spectrometer.rs 5.22 KiB
Newer Older
Udo Eisenbarth's avatar
Udo Eisenbarth committed
#![warn(missing_docs)]
use uom::si::length::nanometer;

use crate::lightdata::LightData;
use crate::{
    error::OpossumError,
    optic_node::{Dottable, LightResult, Optical},
    optic_ports::OpticPorts,
};
use std::collections::HashMap;
use std::fmt::Debug;

type Result<T> = std::result::Result<T, OpossumError>;

#[non_exhaustive]
#[derive(Debug, Default, PartialEq, Clone, Copy)]
Udo Eisenbarth's avatar
Udo Eisenbarth committed
/// Type of the [`Spectrometer`]. This is currently not used.
pub enum SpectrometerType {
    /// an ideal energy meter
    #[default]
    IdealSpectrometer,
    /// Ocean Optics HR2000
    HR2000,
}
#[derive(Default)]
/// (ideal) spectrometer
///
Udo Eisenbarth's avatar
Udo Eisenbarth committed
/// It normally measures / displays the spectrum of the incoming light.
///
/// ## Optical Ports
///   - Inputs
///     - `in1`
///   - Outputs
///     - `out1`
///
/// During analysis, the output port contains a replica of the input port similar to a [`Dummy`](crate::nodes::Dummy) node. This way,
Udo Eisenbarth's avatar
Udo Eisenbarth committed
/// different dectector nodes can be "stacked" or used somewhere within the optical setup.
pub struct Spectrometer {
    light_data: Option<LightData>,
    spectrometer_type: SpectrometerType,
}
impl Spectrometer {
    /// Creates a new [`Spectrometer`] of the given [`SpectrometerType`].
    pub fn new(spectrometer_type: SpectrometerType) -> Self {
        Spectrometer {
            light_data: None,
            spectrometer_type,
        }
    }
    /// Returns the meter type of this [`Spectrometer`].
    pub fn spectrometer_type(&self) -> SpectrometerType {
        self.spectrometer_type
    }
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    /// Sets the meter type of this [`Spectrometer`].
    pub fn set_spectrometer_type(&mut self, meter_type: SpectrometerType) {
        self.spectrometer_type = meter_type;
    }
}
impl Optical for Spectrometer {
    fn node_type(&self) -> &str {
        "spectrometer"
    }
    fn ports(&self) -> OpticPorts {
        let mut ports = OpticPorts::new();
        ports.add_input("in1").unwrap();
        ports.add_output("out1").unwrap();
        ports
    }
    fn analyze(
        &mut self,
        incoming_data: LightResult,
        _analyzer_type: &crate::analyzer::AnalyzerType,
    ) -> Result<LightResult> {
        if let Some(data) = incoming_data.get("in1") {
            self.light_data = data.clone();
            Ok(HashMap::from([("out1".into(), data.clone())]))
        } else {
            Ok(HashMap::from([("out2".into(), None)]))
        }
    }
    fn export_data(&self, file_name: &str) {
        if let Some(data) = &self.light_data {
            data.export(file_name)
        }
    }
    fn is_detector(&self) -> bool {
        true
    }
}

impl Debug for Spectrometer {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.light_data {
            Some(data) => match data {
                LightData::Energy(data_energy) => {
                    let spectrum_range = data_energy.spectrum.range();
                    write!(
                        f,
                        "Spectrum {:.3} - {:.3} nm (Type: {:?})",
                        spectrum_range.start.get::<nanometer>(),
                        spectrum_range.end.get::<nanometer>(),
                        self.spectrometer_type
                    )
                }
                _ => write!(f, "no spectrum data to display"),
            },
            None => write!(f, "no data"),
        }
    }
}
impl Dottable for Spectrometer {
    fn node_color(&self) -> &str {
        "lightseagreen"
    }
}

#[cfg(test)]
mod test {
    use crate::{analyzer::AnalyzerType, lightdata::DataEnergy, spectrum::create_he_ne_spectrum};

    use super::*;
    #[test]
    fn new() {
        let meter = Spectrometer::new(SpectrometerType::IdealSpectrometer);
        assert!(meter.light_data.is_none());
        assert_eq!(meter.spectrometer_type, SpectrometerType::IdealSpectrometer);
    }
    #[test]
    fn default() {
        let meter = Spectrometer::default();
        assert!(meter.light_data.is_none());
        assert_eq!(meter.spectrometer_type, SpectrometerType::IdealSpectrometer);
        assert_eq!(meter.node_type(), "spectrometer");
        assert_eq!(meter.is_detector(), true);
        assert_eq!(meter.node_color(), "lightseagreen");
    }
    #[test]
    fn meter_type() {
        let meter = Spectrometer::new(SpectrometerType::IdealSpectrometer);
        assert_eq!(
            meter.spectrometer_type(),
            SpectrometerType::IdealSpectrometer
        );
    }
    #[test]
    fn set_meter_type() {
        let mut meter = Spectrometer::new(SpectrometerType::IdealSpectrometer);
        meter.set_spectrometer_type(SpectrometerType::HR2000);
        assert_eq!(meter.spectrometer_type, SpectrometerType::HR2000);
    }
    #[test]
    fn ports() {
        let meter = Spectrometer::default();
        let ports = meter.ports();
        assert_eq!(ports.inputs(), vec!["in1"]);
        assert_eq!(ports.outputs(), vec!["out1"]);
    }
    #[test]
    fn analyze() {
        let mut meter = Spectrometer::default();
        let mut input = LightResult::default();
        input.insert(
            "in1".into(),
            Some(LightData::Energy(DataEnergy {
                spectrum: create_he_ne_spectrum(1.0),
            })),
        );
        let result = meter.analyze(input, &AnalyzerType::Energy);
        assert!(result.is_ok());
    }
}