parse_monitors/
domeseeing.rs

1use serde::{Deserialize, Serialize};
2use serde_pickle as pickle;
3use std::{
4    fs::File,
5    ops::{Deref, DerefMut},
6    path::Path,
7};
8
9#[derive(thiserror::Error, Debug)]
10pub enum DomeSeeingError {
11    #[error("Failed to open `domeseeing_PSSN.rs.pkl`")]
12    Io(#[from] std::io::Error),
13    #[error("Failed to read `domeseeing_PSSN.rs.pkl`")]
14    Pickle(#[from] pickle::Error),
15}
16
17/// Photometric band
18pub enum Band {
19    V,
20    H,
21}
22/// Dome seeing data
23#[derive(Deserialize, Debug, Clone, Default, Serialize)]
24pub struct Data {
25    #[serde(rename = "Time")]
26    pub time: f64,
27    #[serde(rename = "V SE PSSn")]
28    pub v_se_pssn: f64,
29    #[serde(rename = "H SE PSSn")]
30    pub h_se_pssn: f64,
31    #[serde(rename = "WFE RMS")]
32    pub wfe_rms: Vec<f64>,
33    #[serde(rename = "tip-tilt")]
34    pub tip_tilt: Vec<f64>,
35    #[serde(rename = "segment tip-tilt")]
36    pub segment_tip_tilt: Vec<f64>,
37    #[serde(rename = "segment piston")]
38    pub segment_piston: Vec<f64>,
39    #[serde(rename = "V LE PSSn")]
40    pub v_le_pssn: Option<f64>,
41    #[serde(rename = "H LE PSSn")]
42    pub h_le_pssn: Option<f64>,
43    #[serde(rename = "V FRAME")]
44    pub v_frame: Option<Vec<f64>>,
45    #[serde(rename = "H FRAME")]
46    pub h_frame: Option<Vec<f64>>,
47}
48/// Time series of dome seeing data
49#[derive(Deserialize, Default, Serialize)]
50pub struct DomeSeeing(Vec<Data>);
51impl Deref for DomeSeeing {
52    type Target = Vec<Data>;
53
54    fn deref(&self) -> &Self::Target {
55        &self.0
56    }
57}
58impl DerefMut for DomeSeeing {
59    fn deref_mut(&mut self) -> &mut Self::Target {
60        &mut self.0
61    }
62}
63impl From<Vec<Data>> for DomeSeeing {
64    fn from(value: Vec<Data>) -> Self {
65        Self(value)
66    }
67}
68impl DomeSeeing {
69    /// Creates an empty [DomeSeeing] instance
70    pub fn new() -> Self {
71        Default::default()
72    }
73    /// Load the dome seeing time series from a "domeseeing_PSSN.rs.pkl" file
74    pub fn load<P>(path: P) -> Result<Self, DomeSeeingError>
75    where
76        P: AsRef<Path>,
77    {
78        let path = path.as_ref().join("domeseeing_PSSN.rs.pkl");
79        log::info!("Logging dome seeing OPD related data from {:?}", path);
80        let mut file = File::open(Path::new(&path))?;
81        Ok(Self(pickle::from_reader(&mut file, Default::default())?))
82    }
83    /// Add another [Data] set to the record
84    pub fn push(&mut self, data: Data) {
85        self.0.push(data);
86    }
87    /// Truncates the records to the first `len` elements
88    pub fn truncates(&mut self, len: usize) {
89        self.0.truncate(len);
90    }
91    /// Returns the number of sample
92    pub fn len(&self) -> usize {
93        self.0.len()
94    }
95    /// Returns an iterator over the wavefront error RMS  [m]
96    pub fn wfe_rms(&self) -> impl Iterator<Item = f64> + '_ {
97        self.iter().map(|ds| ds.wfe_rms[0])
98    }
99    /// Returns the time vector and the wavefront error RMS [m]
100    pub fn wfe_rms_series(&self) -> (Vec<f64>, Vec<f64>) {
101        self.iter().map(|ds| (ds.time, ds.wfe_rms[0])).unzip()
102    }
103    pub fn wfe_rms_iter(&self) -> impl IntoIterator<Item = (f64, Vec<f64>)> + '_ {
104        self.iter()
105            .cloned()
106            .map(|ds| (ds.time, vec![ds.wfe_rms[0]]))
107    }
108    pub fn wfe_rms_iter_10e(
109        &self,
110        exponent: i32,
111    ) -> impl IntoIterator<Item = (f64, Vec<f64>)> + '_ {
112        self.iter()
113            .cloned()
114            .map(move |ds| (ds.time, vec![10f64.powi(-exponent) * ds.wfe_rms[0]]))
115    }
116    /// Returns the time vector and the instantenous PSSn vector
117    pub fn se_pssn(&self, band: Band) -> (Vec<f64>, Vec<f64>) {
118        match band {
119            Band::V => self.iter().map(|ds| (ds.time, ds.v_se_pssn)).unzip(),
120            Band::H => self.iter().map(|ds| (ds.time, ds.h_se_pssn)).unzip(),
121        }
122    }
123    pub fn se_pssn_iter(&self, band: Band) -> Vec<(f64, Vec<f64>)> {
124        match band {
125            Band::V => self
126                .iter()
127                .cloned()
128                .map(|ds| (ds.time, vec![ds.v_se_pssn]))
129                .collect(),
130
131            Band::H => self
132                .iter()
133                .cloned()
134                .map(|ds| (ds.time, vec![ds.h_se_pssn]))
135                .collect(),
136        }
137    }
138    /// Returns the time vector and the long cumulative exposure PSSn vector
139    pub fn le_pssn(&self, band: Band) -> (Vec<f64>, Vec<f64>) {
140        match band {
141            Band::V => self
142                .iter()
143                .filter_map(|ds| ds.v_le_pssn.map(|x| (ds.time, x)))
144                .unzip(),
145            Band::H => self
146                .iter()
147                .filter_map(|ds| ds.h_le_pssn.map(|x| (ds.time, x)))
148                .unzip(),
149        }
150    }
151    pub fn le_pssn_iter(&self, band: Band) -> Vec<(f64, Vec<f64>)> {
152        match band {
153            Band::V => self
154                .iter()
155                .cloned()
156                .filter_map(|ds| ds.v_le_pssn.map(|x| (ds.time, vec![x])))
157                .collect(),
158            Band::H => self
159                .iter()
160                .cloned()
161                .filter_map(|ds| ds.h_le_pssn.map(|x| (ds.time, vec![x])))
162                .collect(),
163        }
164    }
165    /// Returns the PSSn
166    pub fn pssn(&self, band: Band) -> Option<f64> {
167        match band {
168            Band::V => self.iter().map(|ds| ds.v_se_pssn).last(),
169            Band::H => self.iter().map(|ds| ds.h_se_pssn).last(),
170        }
171    }
172}
173
174#[cfg(test)]
175pub mod tests {
176    use super::*;
177    #[test]
178    fn load_dome_seeing() {
179        let ds = DomeSeeing::load("data").unwrap();
180        println!("Dome Seeing entry #1: {:?}", ds[0]);
181    }
182    #[test]
183    fn dome_seeing_pssn() {
184        let ds = DomeSeeing::load("data").unwrap();
185        println!(
186            "Dome Seeing PSSn V:{:?}, H:{:?}",
187            ds.pssn(Band::V),
188            ds.pssn(Band::H)
189        );
190    }
191}