use crate::astro::orbit::Orbit;
use qtty::*;
use std::fmt::Debug;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
pub use affn::centers::ReferenceCenter;
pub use affn::{AffineCenter, CenterParamsMismatchError, NoCenter};
use affn::prelude::ReferenceCenter as DeriveReferenceCenter;
use super::frames::ECEF;
pub type Geodetic<F, U = qtty::Meter> = affn::ellipsoidal::Position<Geocentric, F, U>;
#[derive(Debug, Copy, Clone, DeriveReferenceCenter)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Heliocentric;
#[derive(Debug, Copy, Clone, DeriveReferenceCenter)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Barycentric;
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Topocentric;
impl ReferenceCenter for Topocentric {
type Params = Geodetic<ECEF>;
fn center_name() -> &'static str {
"Topocentric"
}
}
impl affn::AffineCenter for Topocentric {}
impl Topocentric {
#[inline]
pub fn horizontal<U: qtty::LengthUnit, T: Into<qtty::Quantity<U>>>(
site: Geodetic<ECEF>,
alt: qtty::Degrees,
az: qtty::Degrees,
distance: T,
) -> affn::spherical::Position<Topocentric, super::frames::Horizontal, U> {
affn::spherical::Position::new_raw_with_params(
site,
alt.wrap_quarter_fold(),
az.normalize(),
distance.into(),
)
}
}
#[derive(Debug, Copy, Clone, DeriveReferenceCenter)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Geocentric;
pub type Mercurycentric = crate::bodies::solar_system::Mercury;
pub type Venuscentric = crate::bodies::solar_system::Venus;
pub type Marscentric = crate::bodies::solar_system::Mars;
pub type Selenocentric = crate::bodies::solar_system::Moon;
pub type Jovicentric = crate::bodies::solar_system::Jupiter;
pub type Saturnocentric = crate::bodies::solar_system::Saturn;
pub type Uranocentric = crate::bodies::solar_system::Uranus;
pub type Neptunocentric = crate::bodies::solar_system::Neptune;
pub type Plutocentric = crate::bodies::solar_system::Pluto;
macro_rules! impl_planet_center_marker {
($ty:path, $name:literal) => {
impl ReferenceCenter for $ty {
type Params = ();
fn center_name() -> &'static str {
$name
}
}
impl affn::AffineCenter for $ty {}
};
}
impl_planet_center_marker!(crate::bodies::solar_system::Mercury, "Mercurycentric");
impl_planet_center_marker!(crate::bodies::solar_system::Venus, "Venuscentric");
impl_planet_center_marker!(crate::bodies::solar_system::Mars, "Marscentric");
impl_planet_center_marker!(crate::bodies::solar_system::Moon, "Selenocentric");
impl_planet_center_marker!(crate::bodies::solar_system::Jupiter, "Jovicentric");
impl_planet_center_marker!(crate::bodies::solar_system::Saturn, "Saturnocentric");
impl_planet_center_marker!(crate::bodies::solar_system::Uranus, "Uranocentric");
impl_planet_center_marker!(crate::bodies::solar_system::Neptune, "Neptunocentric");
impl_planet_center_marker!(crate::bodies::solar_system::Pluto, "Plutocentric");
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OrbitReferenceCenter {
Barycentric,
#[default]
Heliocentric,
Geocentric,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BodycentricParams {
pub orbit: Orbit,
pub orbit_center: OrbitReferenceCenter,
}
impl BodycentricParams {
pub const fn new(orbit: Orbit, orbit_center: OrbitReferenceCenter) -> Self {
Self {
orbit,
orbit_center,
}
}
pub const fn heliocentric(orbit: Orbit) -> Self {
Self::new(orbit, OrbitReferenceCenter::Heliocentric)
}
pub const fn geocentric(orbit: Orbit) -> Self {
Self::new(orbit, OrbitReferenceCenter::Geocentric)
}
pub const fn barycentric(orbit: Orbit) -> Self {
Self::new(orbit, OrbitReferenceCenter::Barycentric)
}
}
impl Default for BodycentricParams {
fn default() -> Self {
use crate::time::JulianDate;
use qtty::AstronomicalUnits;
Self {
orbit: Orbit::new(
AstronomicalUnits::new(1.0),
0.0,
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
JulianDate::J2000,
),
orbit_center: OrbitReferenceCenter::Heliocentric,
}
}
}
#[derive(Debug, Copy, Clone, DeriveReferenceCenter)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[center(params = BodycentricParams)]
pub struct Bodycentric;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn center_names_are_correct() {
assert_eq!(Barycentric::center_name(), "Barycentric");
assert_eq!(Heliocentric::center_name(), "Heliocentric");
assert_eq!(Topocentric::center_name(), "Topocentric");
assert_eq!(Geocentric::center_name(), "Geocentric");
assert_eq!(Bodycentric::center_name(), "Bodycentric");
assert_eq!(<() as ReferenceCenter>::center_name(), "");
assert_eq!(Mercurycentric::center_name(), "Mercurycentric");
assert_eq!(Venuscentric::center_name(), "Venuscentric");
assert_eq!(Marscentric::center_name(), "Marscentric");
assert_eq!(Selenocentric::center_name(), "Selenocentric");
assert_eq!(Jovicentric::center_name(), "Jovicentric");
assert_eq!(Saturnocentric::center_name(), "Saturnocentric");
assert_eq!(Uranocentric::center_name(), "Uranocentric");
assert_eq!(Neptunocentric::center_name(), "Neptunocentric");
assert_eq!(Plutocentric::center_name(), "Plutocentric");
}
#[test]
fn standard_centers_have_unit_params() {
let _: <Barycentric as ReferenceCenter>::Params = ();
let _: <Heliocentric as ReferenceCenter>::Params = ();
let _: <Geocentric as ReferenceCenter>::Params = ();
let _: <() as ReferenceCenter>::Params = ();
assert_eq!(
std::mem::size_of::<<Barycentric as ReferenceCenter>::Params>(),
0
);
assert_eq!(
std::mem::size_of::<<Heliocentric as ReferenceCenter>::Params>(),
0
);
assert_eq!(
std::mem::size_of::<<Geocentric as ReferenceCenter>::Params>(),
0
);
}
#[test]
fn topocentric_has_geodetic_coord_params() {
let site = Geodetic::<ECEF>::new(0.0 * DEG, 51.4769 * DEG, 0.0 * M);
let _: <Topocentric as ReferenceCenter>::Params = site;
assert!(std::mem::size_of::<<Topocentric as ReferenceCenter>::Params>() > 0);
}
#[test]
fn geodetic_coord_default() {
let coord = Geodetic::<ECEF>::default();
assert_eq!(coord.lon, 0.0);
assert_eq!(coord.lat, 0.0);
assert_eq!(coord.height, 0.0);
}
#[test]
fn geodetic_coord_equality() {
let c1 = Geodetic::<ECEF>::new(10.0 * DEG, 20.0 * DEG, 100.0 * M);
let c2 = Geodetic::<ECEF>::new(10.0 * DEG, 20.0 * DEG, 100.0 * M);
let c3 = Geodetic::<ECEF>::new(10.0 * DEG, 20.0 * DEG, 200.0 * M);
assert_eq!(c1, c2);
assert_ne!(c1, c3);
}
#[test]
fn bodycentric_has_params() {
use crate::time::JulianDate;
let orbit = Orbit::new(
1.524 * AU,
0.0934,
Degrees::new(1.85),
Degrees::new(49.56),
Degrees::new(286.5),
Degrees::new(19.41),
JulianDate::J2000,
);
let params = BodycentricParams::heliocentric(orbit);
let _: <Bodycentric as ReferenceCenter>::Params = params;
assert!(std::mem::size_of::<<Bodycentric as ReferenceCenter>::Params>() > 0);
}
#[test]
fn bodycentric_params_constructors() {
use crate::time::JulianDate;
let orbit = Orbit::new(
1.0 * AU,
0.0,
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
JulianDate::J2000,
);
let helio = BodycentricParams::heliocentric(orbit);
assert_eq!(helio.orbit_center, OrbitReferenceCenter::Heliocentric);
let geo = BodycentricParams::geocentric(orbit);
assert_eq!(geo.orbit_center, OrbitReferenceCenter::Geocentric);
let bary = BodycentricParams::barycentric(orbit);
assert_eq!(bary.orbit_center, OrbitReferenceCenter::Barycentric);
}
#[test]
fn bodycentric_params_default() {
let params = BodycentricParams::default();
assert_eq!(params.orbit_center, OrbitReferenceCenter::Heliocentric);
assert_eq!(params.orbit.eccentricity, 0.0);
}
#[test]
fn bodycentric_params_equality() {
use crate::time::JulianDate;
let orbit1 = Orbit::new(
1.0 * AU,
0.0,
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
JulianDate::J2000,
);
let orbit2 = Orbit::new(
2.0 * AU, 0.0,
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
Degrees::new(0.0),
JulianDate::J2000,
);
let params1 = BodycentricParams::heliocentric(orbit1);
let params2 = BodycentricParams::heliocentric(orbit1);
let params3 = BodycentricParams::heliocentric(orbit2);
assert_eq!(params1, params2);
assert_ne!(params1, params3);
}
#[test]
fn center_params_mismatch_error_reexported() {
let err = CenterParamsMismatchError { operation: "test" };
let _: &dyn std::error::Error = &err;
}
}