use std::{
fmt::{self},
sync::OnceLock,
};
use crate::{
observation_dataset::ObsDatasetError,
observer::{
Observer,
error_model::ObsErrorModel,
mpc::{MpcCode, MpcCodeObs, init_observatories},
},
};
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ObserverId {
IntId(usize),
MpcCode(MpcCode),
}
impl fmt::Display for ObserverId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ObserverId::MpcCode(code) => {
match std::str::from_utf8(code) {
Ok(s) => write!(f, "MPC observatory code: {s}"),
Err(_) => write!(f, "MPC observatory code: {:?}", code),
}
}
ObserverId::IntId(idx) => {
write!(f, "Custom observer (dataset index: {idx})")
}
}
}
}
#[derive(Debug)]
pub struct ObserverDataset {
pub(crate) custom_observers: Vec<Observer>,
mpc_observers: OnceLock<MpcCodeObs>,
pub(crate) mpc_error_model: Option<ObsErrorModel>,
}
impl Clone for ObserverDataset {
fn clone(&self) -> Self {
Self {
custom_observers: self.custom_observers.clone(),
mpc_observers: OnceLock::new(), mpc_error_model: self.mpc_error_model,
}
}
}
impl ObserverDataset {
#[cfg_attr(not(feature = "polars"), allow(dead_code))]
pub(crate) fn new(
custom_observers: Vec<Observer>,
mpc_error_model: Option<ObsErrorModel>,
) -> Self {
Self {
custom_observers,
mpc_observers: OnceLock::new(),
mpc_error_model,
}
}
pub fn empty(error_model: Option<ObsErrorModel>) -> Self {
Self {
custom_observers: Vec::new(),
mpc_observers: OnceLock::new(),
mpc_error_model: error_model,
}
}
pub fn get(&self, obs_id: &ObserverId) -> Option<&Observer> {
match obs_id {
ObserverId::IntId(idx) => self.custom_observers.get(*idx),
ObserverId::MpcCode(code) => self.mpc_observers().ok()?.get(code),
}
}
#[cfg_attr(not(any(feature = "ades", feature = "mpc_80_col")), allow(dead_code))]
pub(crate) fn merge_custom_observers(&mut self, other: ObserverDataset) -> usize {
let offset = self.custom_observers.len();
self.custom_observers.extend(other.custom_observers);
offset
}
pub(crate) fn mpc_observers(&self) -> Result<&MpcCodeObs, ObsDatasetError> {
if let Some(obs) = self.mpc_observers.get() {
return Ok(obs);
}
let error_model_data = self
.mpc_error_model
.as_ref()
.ok_or(ObsDatasetError::ErrorModelNotFound)?
.read_error_model_file()?;
let obs = init_observatories(&error_model_data)?;
let _ = self.mpc_observers.set(obs);
Ok(self
.mpc_observers
.get()
.expect("just set above or by a concurrent thread"))
}
}