use super::Bodies;
use crate::time::{Duration, Unit};
use std::cmp::PartialEq;
use std::convert::TryFrom;
use std::f64::consts::PI;
use std::fmt;
#[allow(non_snake_case, clippy::upper_case_acronyms)]
#[derive(Copy, Clone, PartialEq)]
pub enum Frame {
Celestial {
gm: f64,
ephem_path: [Option<usize>; 3],
frame_path: [Option<usize>; 3],
},
Geoid {
gm: f64,
flattening: f64,
equatorial_radius: f64,
semi_major_radius: f64,
ephem_path: [Option<usize>; 3],
frame_path: [Option<usize>; 3],
},
VNC,
RCN,
RIC,
SEZ,
Inertial,
}
impl Frame {
pub fn is_geoid(&self) -> bool {
matches!(self, Frame::Geoid { .. })
}
pub fn is_celestial(&self) -> bool {
matches!(self, Frame::Celestial { .. })
}
pub fn ephem_path(&self) -> Vec<usize> {
match self {
Frame::Celestial { ephem_path, .. } | Frame::Geoid { ephem_path, .. } => {
let mut path = Vec::with_capacity(3);
for p in ephem_path.iter().flatten() {
path.push(*p)
}
path
}
_ => panic!("Frame is not Celestial or Geoid in kind"),
}
}
pub fn frame_path(&self) -> Vec<usize> {
match self {
Frame::Celestial { frame_path, .. } | Frame::Geoid { frame_path, .. } => {
let mut path = Vec::with_capacity(3);
for p in frame_path.iter().flatten() {
path.push(*p)
}
path
}
_ => panic!("Frame is not Celestial or Geoid in kind"),
}
}
pub fn gm(&self) -> f64 {
match self {
Frame::Celestial { gm, .. } | Frame::Geoid { gm, .. } => *gm,
_ => panic!("Frame is not Celestial or Geoid in kind"),
}
}
pub fn gm_mut(&mut self, new_gm: f64) {
match self {
Self::Geoid { ref mut gm, .. } | Self::Celestial { ref mut gm, .. } => *gm = new_gm,
_ => panic!("Frame is not Celestial or Geoid in kind"),
}
}
pub fn equatorial_radius(&self) -> f64 {
match self {
Frame::Geoid {
equatorial_radius, ..
} => *equatorial_radius,
_ => panic!("Frame is not Geoid in kind"),
}
}
pub fn flattening(&self) -> f64 {
match self {
Frame::Geoid { flattening, .. } => *flattening,
_ => panic!("Frame is not Geoid in kind"),
}
}
pub fn flattening_mut(&mut self, new_flattening: f64) {
match self {
Self::Geoid {
ref mut flattening, ..
} => *flattening = new_flattening,
_ => panic!("Frame is not Geoid in kind"),
}
}
pub fn semi_major_radius(&self) -> f64 {
match self {
Frame::Geoid {
semi_major_radius, ..
} => *semi_major_radius,
_ => panic!("Frame is not Geoid in kind"),
}
}
#[allow(clippy::identity_op)]
pub fn angular_velocity(&self) -> f64 {
let period_to_mean_motion = |dur: Duration| -> f64 { 2.0 * PI / dur.in_seconds() };
match Bodies::try_from(self.ephem_path()).unwrap() {
Bodies::MercuryBarycenter | Bodies::Mercury => {
period_to_mean_motion(58 * Unit::Day + 15 * Unit::Hour + 30 * Unit::Minute)
}
Bodies::VenusBarycenter | Bodies::Venus => period_to_mean_motion(243 * Unit::Day),
Bodies::Earth => 7.292_115_146_706_4e-5,
Bodies::Luna => {
period_to_mean_motion(27 * Unit::Day + 7 * Unit::Hour + 12 * Unit::Minute)
}
Bodies::MarsBarycenter => period_to_mean_motion(1 * Unit::Day + 37 * Unit::Minute),
Bodies::JupiterBarycenter => period_to_mean_motion(9 * Unit::Hour + 56 * Unit::Minute),
Bodies::SaturnBarycenter => period_to_mean_motion(10 * Unit::Hour + 30 * Unit::Minute),
Bodies::UranusBarycenter => period_to_mean_motion(17 * Unit::Hour + 14 * Unit::Minute),
Bodies::NeptuneBarycenter => period_to_mean_motion(16 * Unit::Hour + 6 * Unit::Minute),
_ => unimplemented!(),
}
}
pub fn is_body_fixed(&self) -> bool {
self.frame_path().len() == 2 || self.frame_path().len() == 3
}
}
impl fmt::Display for Frame {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Frame::Celestial { .. } | Frame::Geoid { .. } => {
write!(
f,
"{} {}",
Bodies::try_from(self.ephem_path()).unwrap().name(),
match self.frame_path().len() {
0 | 1 => "J2000".to_string(),
2 => "IAU Fixed".to_string(),
3 => "IAU Poles Fixed".to_string(),
_ => "Custom".to_string(),
}
)
}
othframe => write!(f, "{:?}", othframe),
}
}
}
#[allow(non_snake_case, clippy::upper_case_acronyms)]
impl fmt::Debug for Frame {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Frame::Celestial { gm, .. } => {
write!(
f,
"{} {} (μ = {:.06} km^3/s^2)",
Bodies::try_from(self.ephem_path()).unwrap().name(),
match self.frame_path().len() {
0 | 1 => "J2000".to_string(),
2 => "IAU Fixed".to_string(),
3 => "IAU Poles Fixed".to_string(),
_ => "Custom".to_string(),
},
gm,
)
}
Frame::Geoid {
gm,
equatorial_radius,
flattening,
..
} => {
write!(
f,
"{} {} (μ = {:.06} km^3/s^2 , r = {:.06} km, f = {:.09})",
Bodies::try_from(self.ephem_path()).unwrap().name(),
match self.frame_path().len() {
0 | 1 => "J2000".to_string(),
2 => "IAU Fixed".to_string(),
3 => "IAU Poles Fixed".to_string(),
_ => "Custom".to_string(),
},
gm,
equatorial_radius,
flattening,
)
}
Frame::VNC => write!(f, "VNC"),
Frame::RCN => write!(f, "RCN"),
Frame::RIC => write!(f, "RIC"),
Frame::SEZ => write!(f, "SEZ"),
Frame::Inertial => write!(f, "Inertial"),
}
}
}