use lazy_static::lazy_static;
use serde::Deserialize;
use thiserror::Error;
use uom::si::f64::{Angle, Length, Time};
#[derive(Debug, Error)]
pub enum TryDriftLookupError {
#[error("drift time `{0:?}` is out of range")]
DriftTimeOutOfRange(Time),
#[error("axial position `{0:?}` is out of range")]
AxialPositionOutOfRange(Length),
}
#[derive(Clone, Debug, Deserialize)]
pub(crate) struct DriftTable(Vec<(Time, Length, Angle)>);
impl DriftTable {
fn at(&self, t: Time) -> Result<(Length, Angle), TryDriftLookupError> {
if t < self.0[0].0 || t > self.0[self.0.len() - 1].0 {
return Err(TryDriftLookupError::DriftTimeOutOfRange(t));
}
let rhs_index = self
.0
.iter()
.position(|&(time, _, _)| time > t)
.unwrap_or(self.0.len() - 1);
let lhs_index = rhs_index - 1;
let (lhs_time, lhs_radius, lhs_correction) = self.0[lhs_index];
let (rhs_time, rhs_radius, rhs_correction) = self.0[rhs_index];
let fraction = (t - lhs_time) / (rhs_time - lhs_time);
let radius = lhs_radius + fraction * (rhs_radius - lhs_radius);
let correction = lhs_correction + Angle::from(fraction * (rhs_correction - lhs_correction));
Ok((radius, correction))
}
}
#[derive(Clone, Debug, Deserialize)]
pub(crate) struct DriftTables(Vec<(DriftTable, Length)>);
impl DriftTables {
pub(crate) fn at(&self, z: Length, t: Time) -> Result<(Length, Angle), TryDriftLookupError> {
let z_abs = z.abs();
if z_abs > self.0[self.0.len() - 1].1 {
return Err(TryDriftLookupError::AxialPositionOutOfRange(z));
}
let (table, _) = self
.0
.iter()
.find(|(_, z_upper_bound)| z_upper_bound >= &z_abs)
.unwrap();
table.at(t)
}
}
const TABLE_BYTES: &[u8] =
include_bytes!("../data/simulation/drift_table/drift_1T_70Ar_30CO2.json");
lazy_static! {
pub(crate) static ref DRIFT_TABLES: DriftTables = serde_json::from_slice(TABLE_BYTES).unwrap();
}
#[cfg(test)]
mod tests;