use core::fmt;
use serde::{Deserialize, Serialize};
use snafu::ResultExt;
#[cfg(feature = "python")]
use pyo3::exceptions::PyException;
#[cfg(feature = "python")]
use pyo3::prelude::*;
use super::{AnalysisError, PhysicsOrbitElSnafu};
use crate::prelude::Orbit;
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[cfg_attr(feature = "python", pyclass)]
#[cfg_attr(feature = "python", pyo3(module = "anise.analysis"))]
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum OrbitalElement {
AoL,
AoP,
ApoapsisRadius,
ApoapsisAltitude,
BrouwerMeanShortSemiMajorAxis,
BrouwerMeanShortEccentricity,
BrouwerMeanShortInclination,
BrouwerMeanShortRAAN,
BrouwerMeanShortAoP,
BrouwerMeanShortMeanAnomaly,
C3,
Declination,
EccentricAnomaly,
Eccentricity,
EquinoctialH,
EquinoctialK,
EquinoctialP,
EquinoctialQ,
EquinoctialLambda,
Energy,
FlightPathAngle,
Height,
Latitude,
Longitude,
Hmag,
HX,
HY,
HZ,
HyperbolicAnomaly,
Inclination,
MeanAnomaly,
PeriapsisRadius,
PeriapsisAltitude,
Period,
RightAscension,
RAAN,
Rmag,
SemiParameter,
SemiMajorAxis,
SemiMinorAxis,
TrueAnomaly,
TrueLongitude,
VelocityDeclination,
Vmag,
X,
Y,
Z,
VX,
VY,
VZ,
Custom,
}
impl OrbitalElement {
pub fn evaluate(self, orbit: Orbit) -> Result<f64, AnalysisError> {
match self {
Self::AoL => orbit
.aol_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::AoP => orbit
.aop_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::ApoapsisRadius => orbit
.apoapsis_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::ApoapsisAltitude => orbit
.apoapsis_altitude_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::BrouwerMeanShortSemiMajorAxis => orbit
.sma_brouwer_short_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::BrouwerMeanShortInclination => orbit
.inc_brouwer_short_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::BrouwerMeanShortEccentricity => orbit
.ecc_brouwer_short()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::BrouwerMeanShortAoP => orbit
.aop_brouwer_short_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::BrouwerMeanShortRAAN => orbit
.raan_brouwer_short_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::BrouwerMeanShortMeanAnomaly => orbit
.ma_brouwer_short_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::C3 => orbit
.c3_km2_s2()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Declination => Ok(orbit.declination_deg()),
Self::EccentricAnomaly => orbit
.ea_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Eccentricity => orbit.ecc().context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Energy => orbit
.energy_km2_s2()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::EquinoctialH => orbit
.equinoctial_h()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::EquinoctialK => orbit
.equinoctial_k()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::EquinoctialP => orbit
.equinoctial_p()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::EquinoctialQ => orbit
.equinoctial_q()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::EquinoctialLambda => orbit
.equinoctial_lambda_mean_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::FlightPathAngle => orbit
.fpa_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Height => orbit
.height_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Latitude => orbit
.latitude_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Longitude => Ok(orbit.longitude_deg()),
Self::Hmag => orbit
.hmag()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::HX => orbit.hx().context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::HY => orbit.hy().context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::HZ => orbit.hz().context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::HyperbolicAnomaly => orbit
.hyperbolic_anomaly_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Inclination => orbit
.inc_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::MeanAnomaly => orbit
.ma_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::PeriapsisRadius => orbit
.periapsis_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::PeriapsisAltitude => orbit
.periapsis_altitude_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Period => Ok(orbit
.period()
.context(PhysicsOrbitElSnafu { el: self, orbit })?
.to_seconds()),
Self::RightAscension => Ok(orbit.right_ascension_deg()),
Self::RAAN => orbit
.raan_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::Rmag => Ok(orbit.rmag_km()),
Self::SemiParameter => orbit
.semi_parameter_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::SemiMajorAxis => orbit
.sma_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::SemiMinorAxis => orbit
.semi_minor_axis_km()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::TrueAnomaly => orbit
.ta_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::TrueLongitude => orbit
.tlong_deg()
.context(PhysicsOrbitElSnafu { el: self, orbit }),
Self::VelocityDeclination => Ok(orbit.velocity_declination_deg()),
Self::Vmag => Ok(orbit.vmag_km_s()),
Self::VX => Ok(orbit.velocity_km_s.x),
Self::VY => Ok(orbit.velocity_km_s.y),
Self::VZ => Ok(orbit.velocity_km_s.z),
Self::X => Ok(orbit.radius_km.x),
Self::Y => Ok(orbit.radius_km.y),
Self::Z => Ok(orbit.radius_km.z),
Self::Custom => Err(AnalysisError::YetUnimplemented {
err: "cannot compute value for custom element",
}),
}
}
pub const fn is_angle(&self) -> bool {
matches!(
self,
Self::AoL
| Self::AoP
| Self::BrouwerMeanShortInclination
| Self::BrouwerMeanShortRAAN
| Self::BrouwerMeanShortAoP
| Self::BrouwerMeanShortMeanAnomaly
| Self::Declination
| Self::EccentricAnomaly
| Self::EquinoctialLambda
| Self::FlightPathAngle
| Self::HyperbolicAnomaly
| Self::Inclination
| Self::Latitude
| Self::Longitude
| Self::MeanAnomaly
| Self::RightAscension
| Self::RAAN
| Self::TrueAnomaly
| Self::TrueLongitude
| Self::VelocityDeclination
)
}
pub const fn unit(&self) -> &'static str {
match self {
Self::AoL
| Self::AoP
| Self::BrouwerMeanShortInclination
| Self::BrouwerMeanShortRAAN
| Self::BrouwerMeanShortAoP
| Self::BrouwerMeanShortMeanAnomaly
| Self::Declination
| Self::EccentricAnomaly
| Self::EquinoctialLambda
| Self::FlightPathAngle
| Self::HyperbolicAnomaly
| Self::Inclination
| Self::Latitude
| Self::Longitude
| Self::MeanAnomaly
| Self::RightAscension
| Self::RAAN
| Self::TrueAnomaly
| Self::TrueLongitude
| Self::VelocityDeclination => "deg",
Self::ApoapsisRadius
| Self::ApoapsisAltitude
| Self::BrouwerMeanShortSemiMajorAxis
| Self::Height
| Self::Hmag
| Self::HX
| Self::HY
| Self::HZ
| Self::PeriapsisRadius
| Self::PeriapsisAltitude
| Self::Rmag
| Self::SemiParameter
| Self::SemiMajorAxis
| Self::SemiMinorAxis
| Self::X
| Self::Y
| Self::Z => "km",
Self::VX | Self::VY | Self::VZ | Self::Vmag => "km/s",
Self::C3 | Self::BrouwerMeanShortEccentricity | Self::Energy => "km^2/s^2",
Self::Eccentricity
| Self::EquinoctialH
| Self::EquinoctialK
| Self::EquinoctialP
| Self::EquinoctialQ
| Self::Custom => "unitless",
Self::Period => "s",
}
}
}
#[cfg(feature = "python")]
#[cfg_attr(feature = "python", pymethods)]
impl OrbitalElement {
#[pyo3(name = "evaluate", signature=(orbit))]
pub fn py_evaluate(&self, orbit: Orbit) -> Result<f64, PyErr> {
self.evaluate(orbit)
.map_err(|e| PyException::new_err(e.to_string()))
}
fn __eq__(&self, other: &Self) -> bool {
self == other
}
fn __ne__(&self, other: &Self) -> bool {
self != other
}
}
impl fmt::Display for OrbitalElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?} ({})", self.unit())
}
}