use snafu::ResultExt;
use super::{OrientationError, OrientationPhysicsSnafu};
use crate::almanac::Almanac;
use crate::constants::ARCSEC_TO_RAD;
use crate::constants::orientations::{
ECLIPJ2000, FRAME_BIAS_DEPSBI_ARCSEC, FRAME_BIAS_DPSIBI_ARCSEC, FRAME_BIAS_DRA0_ARCSEC, ICRS,
J2000, J2000_TO_ECLIPJ2000_ANGLE_RAD,
};
use crate::frames::DynamicFrame;
use crate::hifitime::Epoch;
use crate::math::rotation::{DCM, r1, r1_dot, r2, r3, r3_dot};
use crate::naif::daf::datatypes::Type2ChebyshevSet;
use crate::naif::daf::{DAFError, DafDataType, NAIFDataSet, NAIFSummaryRecord};
use crate::orientations::{BPCSnafu, OrientationInterpolationSnafu};
use crate::prelude::Frame;
impl Almanac {
pub fn rotation_to_parent(
&self,
source: Frame,
mut epoch: Epoch,
) -> Result<DCM, OrientationError> {
if let Some(frozen_epoch) = source.frozen_epoch {
epoch = frozen_epoch;
}
if source.orient_origin_id_match(J2000) {
return Ok(DCM::identity(J2000, J2000));
} else if source.orient_origin_id_match(ECLIPJ2000) {
return Ok(DCM {
rot_mat: r1(J2000_TO_ECLIPJ2000_ANGLE_RAD),
rot_mat_dt: None,
from: J2000,
to: ECLIPJ2000,
});
} else if source.orient_origin_id_match(ICRS) {
let dra0_rad = FRAME_BIAS_DRA0_ARCSEC * ARCSEC_TO_RAD;
let dpsibi_rad = FRAME_BIAS_DPSIBI_ARCSEC * ARCSEC_TO_RAD;
let depsbi_rad = FRAME_BIAS_DEPSBI_ARCSEC * ARCSEC_TO_RAD;
let rot_mat = r1(-depsbi_rad)
* r2(dpsibi_rad * J2000_TO_ECLIPJ2000_ANGLE_RAD.sin())
* r3(dra0_rad);
return Ok(DCM {
rot_mat,
rot_mat_dt: None,
from: J2000,
to: ICRS,
});
} else if let Ok(dyn_frame) = DynamicFrame::try_from(source.orientation_id as u32) {
let mut dcm = self.rotation_to_parent_dynamic(dyn_frame, epoch)?;
if source.force_inertial {
dcm.rot_mat_dt = None;
}
return Ok(dcm);
}
match self.bpc_summary_at_epoch(source.orientation_id, epoch) {
Ok((summary, bpc_no, daf_idx, idx_in_bpc)) => {
let (_, bpc_data) = self
.bpc_data
.get_index(bpc_no)
.ok_or(OrientationError::Unreachable)?;
let (ra_dec_w, d_ra_dec_w) = match summary.data_type()? {
DafDataType::Type2ChebyshevTriplet => {
let data = bpc_data
.nth_data::<Type2ChebyshevSet>(daf_idx, idx_in_bpc)
.context(BPCSnafu {
action: "fetching data for interpolation",
})?;
data.evaluate(epoch, summary)
.context(OrientationInterpolationSnafu)?
}
dtype => {
return Err(OrientationError::BPC {
action: "rotation to parent",
source: DAFError::UnsupportedDatatype {
dtype,
kind: "BPC computations",
},
});
}
};
let twist_rad = ra_dec_w[2];
let dec_rad = ra_dec_w[1];
let ra_rad = ra_dec_w[0];
let twist_dot_rad = d_ra_dec_w[2];
let dec_dot_rad = d_ra_dec_w[1];
let ra_dot_rad = d_ra_dec_w[0];
let rot_mat = r3(twist_rad) * r1(dec_rad) * r3(ra_rad);
let rot_mat_dt = if source.force_inertial {
None
} else {
Some(
twist_dot_rad * r3_dot(twist_rad) * r1(dec_rad) * r3(ra_rad)
+ dec_dot_rad * r3(twist_rad) * r1_dot(dec_rad) * r3(ra_rad)
+ ra_dot_rad * r3(twist_rad) * r1(dec_rad) * r3_dot(ra_rad),
)
};
Ok(DCM {
rot_mat,
rot_mat_dt,
from: summary.inertial_frame_id,
to: source.orientation_id,
})
}
Err(_) => {
for data in self.planetary_data.values().rev() {
if let Ok(planetary_data) = data.get_by_id(source.orientation_id) {
let system_data = match data.get_by_id(planetary_data.parent_id) {
Ok(parent) => parent,
Err(_) => planetary_data,
};
return planetary_data
.rotation_to_parent(
epoch,
&system_data,
source.force_inertial,
false,
false,
)
.context(OrientationPhysicsSnafu);
}
}
Ok(self.euler_param_from_id(source.orientation_id)?.into())
}
}
}
}