use serde::{Deserialize, Serialize};
use serde_pickle as pickle;
use std::{
fs::File,
ops::{Deref, DerefMut},
path::Path,
};
#[derive(thiserror::Error, Debug)]
pub enum DomeSeeingError {
#[error("Failed to open `domeseeing_PSSN.rs.pkl`")]
Io(#[from] std::io::Error),
#[error("Failed to read `domeseeing_PSSN.rs.pkl`")]
Pickle(#[from] pickle::Error),
}
pub enum Band {
V,
H,
}
#[derive(Deserialize, Debug, Clone, Default, Serialize)]
pub struct Data {
#[serde(rename = "Time")]
pub time: f64,
#[serde(rename = "V SE PSSn")]
pub v_se_pssn: f64,
#[serde(rename = "H SE PSSn")]
pub h_se_pssn: f64,
#[serde(rename = "WFE RMS")]
pub wfe_rms: Vec<f64>,
#[serde(rename = "tip-tilt")]
pub tip_tilt: Vec<f64>,
#[serde(rename = "segment tip-tilt")]
pub segment_tip_tilt: Vec<f64>,
#[serde(rename = "segment piston")]
pub segment_piston: Vec<f64>,
#[serde(rename = "V LE PSSn")]
pub v_le_pssn: Option<f64>,
#[serde(rename = "H LE PSSn")]
pub h_le_pssn: Option<f64>,
#[serde(rename = "V FRAME")]
pub v_frame: Option<Vec<f64>>,
#[serde(rename = "H FRAME")]
pub h_frame: Option<Vec<f64>>,
}
#[derive(Deserialize, Default, Serialize)]
pub struct DomeSeeing(Vec<Data>);
impl Deref for DomeSeeing {
type Target = Vec<Data>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for DomeSeeing {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<Vec<Data>> for DomeSeeing {
fn from(value: Vec<Data>) -> Self {
Self(value)
}
}
impl DomeSeeing {
pub fn new() -> Self {
Default::default()
}
pub fn load<P>(path: P) -> Result<Self, DomeSeeingError>
where
P: AsRef<Path>,
{
let path = path.as_ref().join("domeseeing_PSSN.rs.pkl");
log::info!("Logging dome seeing OPD related data from {:?}", path);
let mut file = File::open(Path::new(&path))?;
Ok(Self(pickle::from_reader(&mut file, Default::default())?))
}
pub fn push(&mut self, data: Data) {
self.0.push(data);
}
pub fn truncates(&mut self, len: usize) {
self.0.truncate(len);
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn wfe_rms(&self) -> impl Iterator<Item = f64> + '_ {
self.iter().map(|ds| ds.wfe_rms[0])
}
pub fn wfe_rms_series(&self) -> (Vec<f64>, Vec<f64>) {
self.iter().map(|ds| (ds.time, ds.wfe_rms[0])).unzip()
}
pub fn wfe_rms_iter(&self) -> impl IntoIterator<Item = (f64, Vec<f64>)> + '_ {
self.iter()
.cloned()
.map(|ds| (ds.time, vec![ds.wfe_rms[0]]))
}
pub fn wfe_rms_iter_10e(
&self,
exponent: i32,
) -> impl IntoIterator<Item = (f64, Vec<f64>)> + '_ {
self.iter()
.cloned()
.map(move |ds| (ds.time, vec![10f64.powi(-exponent) * ds.wfe_rms[0]]))
}
pub fn se_pssn(&self, band: Band) -> (Vec<f64>, Vec<f64>) {
match band {
Band::V => self.iter().map(|ds| (ds.time, ds.v_se_pssn)).unzip(),
Band::H => self.iter().map(|ds| (ds.time, ds.h_se_pssn)).unzip(),
}
}
pub fn se_pssn_iter(&self, band: Band) -> Vec<(f64, Vec<f64>)> {
match band {
Band::V => self
.iter()
.cloned()
.map(|ds| (ds.time, vec![ds.v_se_pssn]))
.collect(),
Band::H => self
.iter()
.cloned()
.map(|ds| (ds.time, vec![ds.h_se_pssn]))
.collect(),
}
}
pub fn le_pssn(&self, band: Band) -> (Vec<f64>, Vec<f64>) {
match band {
Band::V => self
.iter()
.filter_map(|ds| ds.v_le_pssn.map(|x| (ds.time, x)))
.unzip(),
Band::H => self
.iter()
.filter_map(|ds| ds.h_le_pssn.map(|x| (ds.time, x)))
.unzip(),
}
}
pub fn le_pssn_iter(&self, band: Band) -> Vec<(f64, Vec<f64>)> {
match band {
Band::V => self
.iter()
.cloned()
.filter_map(|ds| ds.v_le_pssn.map(|x| (ds.time, vec![x])))
.collect(),
Band::H => self
.iter()
.cloned()
.filter_map(|ds| ds.h_le_pssn.map(|x| (ds.time, vec![x])))
.collect(),
}
}
pub fn pssn(&self, band: Band) -> Option<f64> {
match band {
Band::V => self.iter().map(|ds| ds.v_se_pssn).last(),
Band::H => self.iter().map(|ds| ds.h_se_pssn).last(),
}
}
}
#[cfg(test)]
pub mod tests {
use super::*;
#[test]
fn load_dome_seeing() {
let ds = DomeSeeing::load("data").unwrap();
println!("Dome Seeing entry #1: {:?}", ds[0]);
}
#[test]
fn dome_seeing_pssn() {
let ds = DomeSeeing::load("data").unwrap();
println!(
"Dome Seeing PSSn V:{:?}, H:{:?}",
ds.pssn(Band::V),
ds.pssn(Band::H)
);
}
}