use hifitime::Epoch;
use snafu::ensure;
use super::{NoOrientationsLoadedSnafu, OrientationError};
use crate::almanac::Almanac;
use crate::constants::orientations::{ECLIPJ2000, J2000};
use crate::frames::Frame;
use crate::naif::daf::{DAFError, NAIFSummaryRecord};
use crate::NaifId;
pub const MAX_TREE_DEPTH: usize = 8;
impl Almanac {
pub fn try_find_orientation_root(&self) -> Result<NaifId, OrientationError> {
ensure!(
self.num_loaded_bpc() > 0 || !self.planetary_data.is_empty(),
NoOrientationsLoadedSnafu
);
let mut common_center = i32::MAX;
for bpc in self.bpc_data.values().rev() {
for these_summaries in bpc.iter_summary_blocks().flatten() {
for summary in these_summaries {
if !summary.is_empty() && summary.inertial_frame_id.abs() < common_center.abs()
{
common_center = summary.inertial_frame_id;
if common_center == J2000 {
return Ok(common_center);
}
}
}
}
}
for data in self.planetary_data.values().rev() {
for id in data.lut.by_id.keys() {
if let Ok(pc) = data.get_by_id(*id) {
if pc.parent_id < common_center {
common_center = pc.parent_id;
if common_center == J2000 {
return Ok(common_center);
}
}
}
}
}
if common_center == ECLIPJ2000 {
common_center = J2000;
}
Ok(common_center)
}
pub fn orientation_path_to_root(
&self,
source: Frame,
epoch: Epoch,
) -> Result<(usize, [Option<NaifId>; MAX_TREE_DEPTH]), OrientationError> {
let common_center = self.try_find_orientation_root()?;
let mut of_path = [None; MAX_TREE_DEPTH];
let mut of_path_len = 0;
if common_center == source.orientation_id {
return Ok((of_path_len, of_path));
}
let mut inertial_frame_id = match self.bpc_summary_at_epoch(source.orientation_id, epoch) {
Ok((summary, _, _, _)) => summary.inertial_frame_id,
Err(_) => {
match self.get_planetary_data_from_id(source.orientation_id) {
Ok(planetary_data) => planetary_data.parent_id,
Err(_) => {
self.euler_param_from_id(source.orientation_id)?.to
}
}
}
};
of_path[of_path_len] = Some(inertial_frame_id);
of_path_len += 1;
if inertial_frame_id == ECLIPJ2000 {
inertial_frame_id = J2000;
of_path[of_path_len] = Some(inertial_frame_id);
of_path_len += 1;
}
if inertial_frame_id == common_center {
return Ok((of_path_len, of_path));
}
for _ in 0..MAX_TREE_DEPTH - 1 {
inertial_frame_id = match self.bpc_summary_at_epoch(inertial_frame_id, epoch) {
Ok((summary, _, _, _)) => summary.inertial_frame_id,
Err(_) => {
match self.get_planetary_data_from_id(inertial_frame_id) {
Ok(planetary_data) => planetary_data.parent_id,
Err(_) => {
self.euler_param_from_id(inertial_frame_id)?.to
}
}
}
};
of_path[of_path_len] = Some(inertial_frame_id);
of_path_len += 1;
if inertial_frame_id == common_center {
return Ok((of_path_len, of_path));
}
}
Err(OrientationError::BPC {
action: "computing path to common node",
source: DAFError::MaxRecursionDepth,
})
}
pub fn common_orientation_path(
&self,
from_frame: Frame,
to_frame: Frame,
epoch: Epoch,
) -> Result<(usize, [Option<NaifId>; MAX_TREE_DEPTH], NaifId), OrientationError> {
if from_frame == to_frame {
return Ok((0, [None; MAX_TREE_DEPTH], from_frame.orientation_id));
}
let (from_len, from_path) = self.orientation_path_to_root(from_frame, epoch)?;
let (to_len, to_path) = self.orientation_path_to_root(to_frame, epoch)?;
if from_len == 0 && to_len == 0 {
Err(OrientationError::RotationOrigin {
from: from_frame.into(),
to: to_frame.into(),
epoch,
})
} else if from_len != 0 && to_len == 0 {
Ok((from_len, from_path, to_frame.orientation_id))
} else if to_len != 0 && from_len == 0 {
Ok((to_len, to_path, from_frame.orientation_id))
} else {
let mut common_path = to_path;
let mut items: usize = to_len;
let common_node = to_path[to_len - 1].unwrap();
for from_obj in from_path.iter().take(from_len).rev().skip(1) {
common_path[items] = Some(from_obj.unwrap());
items += 1;
}
Ok((items, common_path, common_node))
}
}
}