use snafu::ResultExt;
use super::OrientationError;
use super::OrientationPhysicsSnafu;
use crate::almanac::Almanac;
use crate::constants::orientations::J2000;
use crate::hifitime::Epoch;
use crate::math::cartesian::CartesianState;
use crate::math::rotation::DCM;
use crate::math::units::*;
use crate::math::Vector3;
use crate::prelude::Frame;
impl Almanac {
pub fn rotate(
&self,
from_frame: Frame,
mut to_frame: Frame,
epoch: Epoch,
) -> Result<DCM, OrientationError> {
if let Ok(to_frame_info) = self.frame_info(to_frame) {
to_frame = to_frame_info;
}
if from_frame.orient_origin_match(to_frame) {
return Ok(DCM::identity(
from_frame.orientation_id,
to_frame.orientation_id,
));
}
let (node_count, path, common_node) =
self.common_orientation_path(from_frame, to_frame, epoch)?;
let mut dcm_fwrd = if from_frame.orient_origin_id_match(common_node) {
DCM::identity(common_node, common_node)
} else {
self.rotation_to_parent(from_frame, epoch)?
};
let mut dcm_bwrd = if to_frame.orient_origin_id_match(common_node) {
DCM::identity(common_node, common_node)
} else {
self.rotation_to_parent(to_frame, epoch)?.transpose()
};
for cur_node_id in path.iter().take(node_count) {
let next_parent = cur_node_id.unwrap();
if next_parent == J2000 {
continue;
}
let cur_dcm = self.rotation_to_parent(Frame::from_orient_ssb(next_parent), epoch)?;
if dcm_fwrd.from == cur_dcm.from {
dcm_fwrd = (cur_dcm * dcm_fwrd.transpose()).context(OrientationPhysicsSnafu)?;
} else if dcm_fwrd.from == cur_dcm.to {
dcm_fwrd = (dcm_fwrd * cur_dcm)
.context(OrientationPhysicsSnafu)?
.transpose();
} else if dcm_bwrd.to == cur_dcm.from {
dcm_bwrd = (cur_dcm * dcm_bwrd).context(OrientationPhysicsSnafu)?;
} else if dcm_bwrd.to == cur_dcm.to {
dcm_bwrd = (dcm_bwrd.transpose() * cur_dcm).context(OrientationPhysicsSnafu)?;
} else {
return Err(OrientationError::Unreachable);
}
if next_parent == common_node {
break;
}
}
if dcm_fwrd.from == dcm_bwrd.from {
(dcm_bwrd * dcm_fwrd.transpose()).context(OrientationPhysicsSnafu)
} else if dcm_fwrd.from == dcm_bwrd.to {
Ok((dcm_fwrd * dcm_bwrd)
.context(OrientationPhysicsSnafu)?
.transpose())
} else if dcm_fwrd.to == dcm_bwrd.to {
Ok((dcm_fwrd.transpose() * dcm_bwrd)
.context(OrientationPhysicsSnafu)?
.transpose())
} else {
(dcm_bwrd * dcm_fwrd).context(OrientationPhysicsSnafu)
}
}
#[allow(clippy::too_many_arguments)]
pub fn rotate_to(
&self,
state: CartesianState,
observer_frame: Frame,
) -> Result<CartesianState, OrientationError> {
let dcm = self.rotate(state.frame, observer_frame, state.epoch)?;
(dcm * state).context(OrientationPhysicsSnafu {})
}
pub fn angular_velocity_rad_s(
&self,
from_frame: Frame,
to_frame: Frame,
epoch: Epoch,
) -> Result<Vector3, OrientationError> {
let dcm = self.rotate(from_frame, to_frame, epoch)?;
if let Some(omega_rad_s) = dcm.angular_velocity_rad_s() {
Ok(omega_rad_s)
} else {
Err(OrientationError::OrientationPhysics {
source: crate::errors::PhysicsError::DCMMissingDerivative {
action: "computing the angular velocity",
},
})
}
}
pub fn angular_velocity_wrt_j2000_rad_s(
&self,
from_frame: Frame,
epoch: Epoch,
) -> Result<Vector3, OrientationError> {
self.angular_velocity_rad_s(from_frame, from_frame.with_orient(J2000), epoch)
}
pub fn angular_velocity_deg_s(
&self,
from_frame: Frame,
to_frame: Frame,
epoch: Epoch,
) -> Result<Vector3, OrientationError> {
let dcm = self.rotate(from_frame, to_frame, epoch)?;
if let Some(omega_deg_s) = dcm.angular_velocity_deg_s() {
Ok(omega_deg_s)
} else {
Err(OrientationError::OrientationPhysics {
source: crate::errors::PhysicsError::DCMMissingDerivative {
action: "computing the angular velocity",
},
})
}
}
pub fn angular_velocity_wrt_j2000_deg_s(
&self,
from_frame: Frame,
epoch: Epoch,
) -> Result<Vector3, OrientationError> {
self.angular_velocity_deg_s(from_frame, from_frame.with_orient(J2000), epoch)
}
#[allow(clippy::too_many_arguments)]
pub fn rotate_state_to(
&self,
position: Vector3,
velocity: Vector3,
from_frame: Frame,
to_frame: Frame,
epoch: Epoch,
distance_unit: LengthUnit,
time_unit: TimeUnit,
) -> Result<CartesianState, OrientationError> {
let dcm = self.rotate(from_frame, to_frame, epoch)?;
let dist_unit_factor = LengthUnit::Kilometer.from_meters() * distance_unit.to_meters();
let time_unit_factor = time_unit.in_seconds();
let input_state = CartesianState {
radius_km: position * dist_unit_factor,
velocity_km_s: velocity * dist_unit_factor / time_unit_factor,
epoch,
frame: from_frame,
};
(dcm * input_state).context(OrientationPhysicsSnafu {})
}
}