diff --git a/crates/lox-orbits/src/frames.rs b/crates/lox-orbits/src/frames.rs index 9651d96c..a3d76405 100644 --- a/crates/lox-orbits/src/frames.rs +++ b/crates/lox-orbits/src/frames.rs @@ -8,18 +8,19 @@ use std::{convert::Infallible, str::FromStr}; -use crate::frames::iau::icrf_to_bodyfixed; +use crate::frames::iau::{icrf_to_bodyfixed, IcrfToBodyFixedError}; use crate::frames::iers::{cirf_to_tirf, icrf_to_cirf, tirf_to_itrf}; use crate::rotations::Rotation; use glam::{DMat3, DVec3}; use lox_bodies::{DynOrigin, MaybeRotationalElements, Origin, RotationalElements}; use lox_math::types::units::Seconds; -use lox_time::transformations::OffsetProvider; +use lox_time::time_scales::Tdb; +use lox_time::transformations::{OffsetProvider, TryToScale}; use lox_time::TimeLike; use thiserror::Error; -mod iau; -mod iers; +pub mod iau; +pub mod iers; pub trait ReferenceFrame { fn name(&self) -> String; @@ -239,7 +240,7 @@ impl FromStr for DynFrame { pub trait TryRotateTo { type Error; - fn try_rotation( + fn try_rotation + Clone>( &self, frame: &R, time: T, @@ -249,9 +250,9 @@ pub trait TryRotateTo { impl TryRotateTo for DynFrame { // FIXME - type Error = String; + type Error = IcrfToBodyFixedError; - fn try_rotation( + fn try_rotation + Clone>( &self, frame: &DynFrame, time: T, @@ -270,9 +271,7 @@ impl TryRotateTo for DynFrame { DynFrame::Itrf => Ok(icrf_to_cirf(centuries_j2000) .compose(&cirf_to_tirf(seconds_j2000)) .compose(&tirf_to_itrf(centuries_j2000))), - DynFrame::BodyFixed(target) => icrf_to_bodyfixed(target, seconds_j2000).ok_or( - format!("no rotational parameters available for {}", target.name()), - ), + DynFrame::BodyFixed(target) => icrf_to_bodyfixed(time, target, provider), }, DynFrame::Cirf => match frame { DynFrame::Icrf => Ok(icrf_to_cirf(centuries_j2000).transpose()), @@ -311,12 +310,7 @@ impl TryRotateTo for DynFrame { .compose(&DynFrame::Icrf.try_rotation(frame, time, provider)?)), }, DynFrame::BodyFixed(origin) => match frame { - DynFrame::Icrf => Ok(icrf_to_bodyfixed(origin, seconds_j2000) - .ok_or(format!( - "no rotational parameters available for {}", - origin.name() - ))? - .transpose()), + DynFrame::Icrf => Ok(icrf_to_bodyfixed(time, origin, provider)?.transpose()), DynFrame::Cirf => Ok(self .try_rotation(&DynFrame::Icrf, time.clone(), provider)? .compose(&DynFrame::Icrf.try_rotation(frame, time, provider)?)), @@ -343,7 +337,6 @@ impl TryRotateTo for DynFrame { #[cfg(test)] mod tests { use super::*; - use lox_math::is_close::IsClose; use rstest::rstest; #[rstest] diff --git a/crates/lox-orbits/src/frames/iau.rs b/crates/lox-orbits/src/frames/iau.rs index 14f7b7dc..1183ed3c 100644 --- a/crates/lox-orbits/src/frames/iau.rs +++ b/crates/lox-orbits/src/frames/iau.rs @@ -5,42 +5,63 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::frames::{BodyFixed, FrameTransformationProvider, Icrf, TryRotateTo}; use crate::rotations::Rotation; use glam::{DMat3, DVec3}; -use lox_bodies::{MaybeRotationalElements, RotationalElements}; -use lox_math::types::units::Seconds; +use lox_bodies::MaybeRotationalElements; +use lox_time::julian_dates::JulianDate; +use lox_time::time_scales::Tdb; +use lox_time::transformations::{OffsetProvider, TryToScale}; use lox_time::TimeLike; +use thiserror::Error; -pub(crate) fn icrf_to_bodyfixed( - body: &T, - seconds: Seconds, -) -> Option { +#[derive(Clone, Debug, Error)] +pub enum IcrfToBodyFixedError { + #[error("no rotational elements are defined for body {0}")] + UndefinedRotationalElements(String), + #[error("time error: {0}")] + TimeError(String), +} + +pub(crate) fn icrf_to_bodyfixed< + P: OffsetProvider, + T: TimeLike + TryToScale, + O: MaybeRotationalElements, +>( + time: T, + body: &O, + provider: &P, +) -> Result { + let seconds = time + .try_to_scale(Tdb, provider) + .map_err(|err| IcrfToBodyFixedError::TimeError(err.to_string()))? + .seconds_since_j2000(); let (Some((right_ascension, declination, prime_meridian)), Some(rates)) = ( body.maybe_rotational_elements(seconds), body.maybe_rotational_element_rates(seconds), ) else { - return None; + return Err(IcrfToBodyFixedError::UndefinedRotationalElements( + body.name().to_string(), + )); }; let m1 = DMat3::from_rotation_z(-right_ascension); let m2 = DMat3::from_rotation_x(-declination); let m3 = DMat3::from_rotation_z(-prime_meridian); let m = m3 * m2 * m1; let v = DVec3::from(rates); - Some(Rotation::new(m).with_angular_velocity(v)) -} - -impl TryRotateTo, P> for Icrf { - type Error = (); - - fn try_rotation( - &self, - frame: &BodyFixed, - time: T, - _provider: &P, - ) -> Result { - // FIXME - let seconds = time.seconds_since_j2000(); - Ok(icrf_to_bodyfixed(&frame.0, seconds).unwrap()) - } + Ok(Rotation::new(m).with_angular_velocity(v)) } +// +// impl TryRotateTo, P> for Icrf { +// type Error = (); +// +// fn try_rotation( +// &self, +// frame: &BodyFixed, +// time: T, +// _provider: &P, +// ) -> Result { +// // FIXME +// let seconds = time.seconds_since_j2000(); +// Ok(icrf_to_bodyfixed(&frame.0, seconds).unwrap()) +// } +// } diff --git a/crates/lox-orbits/src/ground.rs b/crates/lox-orbits/src/ground.rs index e4c4d165..60458901 100644 --- a/crates/lox-orbits/src/ground.rs +++ b/crates/lox-orbits/src/ground.rs @@ -17,6 +17,7 @@ use lox_time::prelude::Tdb; use lox_time::transformations::TryToScale; use lox_time::TimeLike; +use crate::frames::iau::IcrfToBodyFixedError; use crate::frames::{ BodyFixed, CoordinateSystem, DynFrame, FrameTransformationProvider, Icrf, ReferenceFrame, TryRotateTo, TryToFrame, @@ -193,6 +194,8 @@ pub enum GroundPropagatorError { FrameTransformationError(String), #[error(transparent)] TrajectoryError(#[from] TrajectoryError), + #[error(transparent)] + IcrfToBodyFixedError(#[from] IcrfToBodyFixedError), } pub struct GroundPropagator { @@ -227,7 +230,10 @@ impl DynGroundPropagator

{ } } - pub fn propagate_dyn(&self, time: T) -> Result, String> { + pub fn propagate_dyn + Clone>( + &self, + time: T, + ) -> Result, GroundPropagatorError> { let s = State::new( time.clone(), self.location.body_fixed_position(), @@ -235,24 +241,23 @@ impl DynGroundPropagator

{ self.location.body, DynFrame::BodyFixed(self.location.body), ); - let rot = s - .reference_frame() - .try_rotation(&DynFrame::Icrf, time.clone(), &self.provider)? - .transpose(); + let rot = + s.reference_frame() + .try_rotation(&DynFrame::Icrf, time.clone(), &self.provider)?; let (r1, v1) = rot.rotate_state(s.position(), s.velocity()); Ok(State::new(time, r1, v1, self.location.body, DynFrame::Icrf)) } - pub(crate) fn propagate_all_dyn( + pub(crate) fn propagate_all_dyn + Clone>( &self, times: impl IntoIterator, - ) -> Result, String> { + ) -> Result, GroundPropagatorError> { let mut states = vec![]; for time in times { let state = self.propagate_dyn(time)?; states.push(state); } - Trajectory::new(&states).map_err(|err| err.to_string()) + Ok(Trajectory::new(&states)?) } } diff --git a/crates/lox-orbits/src/states.rs b/crates/lox-orbits/src/states.rs index 3b817ce3..ea9ac519 100644 --- a/crates/lox-orbits/src/states.rs +++ b/crates/lox-orbits/src/states.rs @@ -22,8 +22,8 @@ use thiserror::Error; use crate::anomalies::{eccentric_to_true, hyperbolic_to_true}; use crate::elements::{is_circular, is_equatorial, DynKeplerian, Keplerian, KeplerianElements}; use crate::frames::{ - BodyFixed, CoordinateSystem, DynFrame, FrameTransformationProvider, Icrf, ReferenceFrame, - TryToFrame, + BodyFixed, CoordinateSystem, DynFrame, FrameTransformationProvider, Icrf, + NoOpFrameTransformationProvider, ReferenceFrame, TryRotateTo, TryToFrame, }; use crate::ground::{DynGroundLocation, GroundLocation}; @@ -166,10 +166,18 @@ pub enum StateToDynGroundError { UndefinedSpheroid(DynOrigin), #[error(transparent)] BracketError(#[from] BracketError), + #[error("not a body-fixed frame {0}")] + NonBodyFixedFrame(String), } -impl DynState { +impl DynState { pub fn to_dyn_ground_location(&self) -> Result { + if !self.frame.is_rotating() { + return Err(StateToDynGroundError::NonBodyFixedFrame( + self.frame.name().to_string(), + )); + } + let r = self.position(); // TODO: Check/transform frame let (Some(r_eq), Some(f)) = ( self.origin.maybe_equatorial_radius(), @@ -177,7 +185,6 @@ impl DynState { ) else { return Err(StateToDynGroundError::UndefinedSpheroid(self.origin)); }; - let r = self.position(); let (lon, lat, alt) = rv_to_lla(r, r_eq, f)?;