use astrodyn_math::OrbitalElements;
use astrodyn_quantities::aliases::{Position, Velocity};
use astrodyn_quantities::dims::GravParam;
use astrodyn_quantities::ext::Vec3Ext;
use astrodyn_quantities::frame::{Earth, Mars, Planet, PlanetInertial, Sun};
use super::constants::{mu_ggm05c, mu_mars, mu_sun};
fn from_pos_vel_with_mu<P: Planet>(
pos: Position<PlanetInertial<P>>,
vel: Velocity<PlanetInertial<P>>,
mu: GravParam<P>,
) -> OrbitalElements<P> {
OrbitalElements::<P>::from_cartesian_typed(mu, pos, vel)
.expect("preset state vector must produce well-defined orbital elements")
}
pub fn iss() -> OrbitalElements<Earth> {
leo_400km_circular_iss_inclination()
}
fn circular_orbit_with_mu<P: Planet>(r: f64, inc: f64, mu: GravParam<P>) -> OrbitalElements<P> {
let v = (mu.value / r).sqrt();
from_pos_vel_with_mu::<P>(
glam::DVec3::new(r, 0.0, 0.0).m_at::<PlanetInertial<P>>(),
glam::DVec3::new(0.0, v * inc.cos(), v * inc.sin()).m_per_s_at::<PlanetInertial<P>>(),
mu,
)
}
pub fn geostationary() -> OrbitalElements<Earth> {
circular_orbit_with_mu::<Earth>(42_164_172.0_f64, 0.0, mu_ggm05c())
}
pub fn leo_400km_circular_iss_inclination() -> OrbitalElements<Earth> {
let r_eq = 6_378_137.0_f64;
circular_orbit_with_mu::<Earth>(r_eq + 400_000.0, 51.6_f64.to_radians(), mu_ggm05c())
}
pub fn leo_polar_600km() -> OrbitalElements<Earth> {
let r_eq = 6_378_137.0_f64;
circular_orbit_with_mu::<Earth>(r_eq + 600_000.0, 90.0_f64.to_radians(), mu_ggm05c())
}
pub fn mercury_perihelion() -> OrbitalElements<Sun> {
from_pos_vel_with_mu::<Sun>(
glam::DVec3::new(46.0e9, 0.0, 0.0).m_at::<PlanetInertial<Sun>>(),
glam::DVec3::new(0.0, 58_980.0, 0.0).m_per_s_at::<PlanetInertial<Sun>>(),
mu_sun(),
)
}
pub fn mars_dawn_orbit() -> OrbitalElements<Mars> {
from_pos_vel_with_mu::<Mars>(
glam::DVec3::new(11_563_355.680_2, -14_356_668.897_7, 6_293_704.616_9)
.m_at::<PlanetInertial<Mars>>(),
glam::DVec3::new(-2_273.107_8, 2_380.132_4, -22.911).m_per_s_at::<PlanetInertial<Mars>>(),
mu_mars(),
)
}
pub fn apollo_parking() -> OrbitalElements<Earth> {
let r_eq = 6_378_137.0_f64;
circular_orbit_with_mu::<Earth>(r_eq + 185_000.0, 32.5_f64.to_radians(), mu_ggm05c())
}
pub fn geo_inclined(inclination: uom::si::f64::Angle) -> OrbitalElements<Earth> {
use uom::si::angle::radian;
circular_orbit_with_mu::<Earth>(42_164_172.0_f64, inclination.get::<radian>(), mu_ggm05c())
}
#[cfg(test)]
mod tests {
use super::*;
use astrodyn_quantities::ext::F64Ext;
#[test]
fn mercury_perihelion_is_heliocentric_and_eccentric() {
let oe = mercury_perihelion();
assert!(oe.semi_major_axis > 5.5e10 && oe.semi_major_axis < 6.1e10);
assert!(oe.e_mag > 0.1 && oe.e_mag < 0.3);
}
#[test]
fn mars_dawn_orbit_hyperbolic_at_arrival() {
let oe = mars_dawn_orbit();
assert!(oe.semi_major_axis < 0.0);
assert!(oe.e_mag > 1.0);
}
#[test]
fn apollo_parking_low_earth_orbit() {
let oe = apollo_parking();
assert!((oe.semi_major_axis - 6_563_137.0).abs() < 1.0);
assert!(oe.e_mag.abs() < 1e-10);
assert!((oe.inclination - 32.5_f64.to_radians()).abs() < 1e-12);
}
#[test]
fn geo_inclined_zero_matches_geostationary() {
let g0 = geo_inclined(0.0_f64.rad());
let g = geostationary();
assert!((g0.semi_major_axis - g.semi_major_axis).abs() < 1.0);
assert!(g0.inclination.abs() < 1e-12);
}
#[test]
fn geo_inclined_nonzero_carries_inclination() {
use uom::si::angle::radian;
let g = geo_inclined(uom::si::f64::Angle::new::<radian>(0.1));
assert!((g.inclination - 0.1).abs() < 1e-12);
assert!(g.semi_major_axis > 4.2e7 && g.semi_major_axis < 4.3e7);
}
}