use crate::{
analysis::prelude::OrbitalElement,
astro::orbit_gradient::OrbitGrad,
errors::{NoCovarianceSnafu, PhysicsError},
prelude::Orbit,
};
use nalgebra::SMatrix;
use snafu::ensure;
#[cfg(feature = "python")]
use pyo3::prelude::*;
pub use super::{Covariance, LocalFrame};
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "python", pyclass)]
#[cfg_attr(feature = "python", pyo3(module = "anise.astro", get_all))]
pub struct EphemerisRecord {
pub orbit: Orbit,
pub covar: Option<Covariance>,
}
#[cfg_attr(feature = "python", pymethods)]
impl EphemerisRecord {
#[cfg(feature = "python")]
#[new]
fn py_new(orbit: Orbit, covar: Option<Covariance>) -> Self {
Self { orbit, covar }
}
pub fn covar_in_frame(
&self,
local_frame: LocalFrame,
) -> Result<Option<Covariance>, PhysicsError> {
match self.covar {
None => Ok(None),
Some(mut covar) => {
if covar.local_frame == local_frame {
return Ok(Some(covar));
}
let desired_frame_to_inertial = self.orbit.dcm_to_inertial(local_frame)?;
let cur_frame_to_inertial = self.orbit.dcm_to_inertial(covar.local_frame)?;
let dcm = (desired_frame_to_inertial.transpose() * cur_frame_to_inertial)?;
covar.matrix = dcm.state_dcm() * covar.matrix * dcm.state_dcm().transpose();
covar.local_frame = local_frame;
Ok(Some(covar))
}
}
}
pub fn sigma_for(&self, oe: OrbitalElement) -> Result<f64, PhysicsError> {
ensure!(
self.covar.is_some(),
NoCovarianceSnafu {
action: "compute orbital element uncertainty"
}
);
let covar = self.covar_in_frame(LocalFrame::Inertial)?.unwrap().matrix;
let orbit_dual = OrbitGrad::from(self.orbit);
let xf_partial = orbit_dual.partial_for(oe)?;
let rotmat = SMatrix::<f64, 1, 6>::new(
xf_partial.wrt_x(),
xf_partial.wrt_y(),
xf_partial.wrt_z(),
xf_partial.wrt_vx(),
xf_partial.wrt_vy(),
xf_partial.wrt_vz(),
);
Ok((rotmat * covar * rotmat.transpose())[(0, 0)].sqrt())
}
}