Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refine time representations #44

Merged
merged 14 commits into from
Jan 28, 2024
Merged
1 change: 1 addition & 0 deletions .idea/lox-space.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions crates/lox-space/examples/iss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*/

use lox_core::time::utc::UTC;
use lox_space::prelude::*;

fn main() {
let date = Date::new(2016, 5, 30).unwrap();
let time = Time::new(12, 0, 0).unwrap();
let epoch = Epoch::from_date_and_time(TimeScale::TDB, date, time);
let utc = UTC::new(12, 0, 0).unwrap();
let time = Time::from_date_and_utc_timestamp(TimeScale::TDB, date, utc);
let position = DVec3::new(6068279.27, -1692843.94, -2516619.18) * 1e-3;
let velocity = DVec3::new(-660.415582, 5495.938726, -5303.093233) * 1e-3;
let iss_cartesian = Cartesian::new(epoch, Earth, Icrf, position, velocity);
let iss_cartesian = Cartesian::new(time, Earth, Icrf, position, velocity);
let iss = Keplerian::from(iss_cartesian);

println!("ISS Orbit for Julian Day {}", iss.time().days_since_j2000(),);
Expand Down
3 changes: 2 additions & 1 deletion crates/lox-space/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
*/

pub use lox_core::bodies::*;

pub use lox_core::coords::two_body::{Cartesian, Keplerian};
pub use lox_core::coords::DVec3;
pub use lox_core::frames::*;
pub use lox_core::time::continuous::*;
pub use lox_core::time::dates::*;
pub use lox_core::time::epochs::*;
30 changes: 15 additions & 15 deletions crates/lox_core/src/coords/states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ use float_eq::float_eq;
use glam::{DMat3, DVec3};

use crate::math::{mod_two_pi, normalize_two_pi};
use crate::time::epochs::Epoch;
use crate::time::continuous::Time;

pub trait TwoBodyState {
fn time(&self) -> Epoch;
fn time(&self) -> Time;
fn to_cartesian_state(&self, grav_param: f64) -> CartesianState;
fn to_keplerian_state(&self, grav_param: f64) -> KeplerianState;
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct CartesianState {
time: Epoch,
time: Time,
position: DVec3,
velocity: DVec3,
}

impl CartesianState {
pub fn new(time: Epoch, position: DVec3, velocity: DVec3) -> Self {
pub fn new(time: Time, position: DVec3, velocity: DVec3) -> Self {
Self {
time,
position,
Expand All @@ -44,7 +44,7 @@ impl CartesianState {
}

impl TwoBodyState for CartesianState {
fn time(&self) -> Epoch {
fn time(&self) -> Time {
self.time
}

Expand Down Expand Up @@ -120,7 +120,7 @@ impl TwoBodyState for CartesianState {

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct KeplerianState {
time: Epoch,
time: Time,
semi_major: f64,
eccentricity: f64,
inclination: f64,
Expand All @@ -131,7 +131,7 @@ pub struct KeplerianState {

impl KeplerianState {
pub fn new(
time: Epoch,
time: Time,
semi_major: f64,
eccentricity: f64,
inclination: f64,
Expand Down Expand Up @@ -196,7 +196,7 @@ impl KeplerianState {
}

impl TwoBodyState for KeplerianState {
fn time(&self) -> Epoch {
fn time(&self) -> Time {
self.time
}

Expand Down Expand Up @@ -231,15 +231,15 @@ fn is_circular(eccentricity: f64) -> bool {

#[cfg(test)]
mod tests {
use crate::time::epochs::TimeScale;
use crate::time::continuous::TimeScale;
use float_eq::assert_float_eq;
use glam::DVec3;

use super::*;

#[test]
fn test_elliptic() {
let time = Epoch::j2000(TimeScale::TDB);
let time = Time::j2000(TimeScale::TDB);
let grav_param = 3.9860047e14;
let semi_major = 24464560.0;
let eccentricity = 0.7311;
Expand Down Expand Up @@ -295,7 +295,7 @@ mod tests {

#[test]
fn test_circular() {
let time = Epoch::j2000(TimeScale::TDB);
let time = Time::j2000(TimeScale::TDB);
let grav_param = 3.986004418e14;
let semi_major = 6778136.6;
let eccentricity = 0.0;
Expand Down Expand Up @@ -336,7 +336,7 @@ mod tests {

#[test]
fn test_circular_orekit() {
let time = Epoch::j2000(TimeScale::TDB);
let time = Time::j2000(TimeScale::TDB);
let grav_param = 3.9860047e14;
let semi_major = 24464560.0;
let eccentricity = 0.0;
Expand Down Expand Up @@ -368,7 +368,7 @@ mod tests {

#[test]
fn test_hyperbolic_orekit() {
let time = Epoch::j2000(TimeScale::TDB);
let time = Time::j2000(TimeScale::TDB);
let grav_param = 3.9860047e14;
let semi_major = -24464560.0;
let eccentricity = 1.7311;
Expand Down Expand Up @@ -400,7 +400,7 @@ mod tests {

#[test]
fn test_equatorial() {
let time = Epoch::j2000(TimeScale::TDB);
let time = Time::j2000(TimeScale::TDB);
let grav_param = 3.9860047e14;
let semi_major = 24464560.0;
let eccentricity = 0.7311;
Expand Down Expand Up @@ -432,7 +432,7 @@ mod tests {

#[test]
fn test_circular_equatorial() {
let time = Epoch::j2000(TimeScale::TDB);
let time = Time::j2000(TimeScale::TDB);
let grav_param = 3.9860047e14;
let semi_major = 24464560.0;
let eccentricity = 0.0;
Expand Down
34 changes: 17 additions & 17 deletions crates/lox_core/src/coords/two_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::bodies::PointMass;
use crate::coords::states::{CartesianState, KeplerianState, TwoBodyState};
use crate::coords::CoordinateSystem;
use crate::frames::{InertialFrame, ReferenceFrame};
use crate::time::epochs::Epoch;
use crate::time::continuous::Time;

pub trait TwoBody<T, S>
where
Expand Down Expand Up @@ -40,7 +40,7 @@ where
T: PointMass + Copy,
S: ReferenceFrame + Copy,
{
pub fn new(time: Epoch, origin: T, frame: S, position: DVec3, velocity: DVec3) -> Self {
pub fn new(time: Time, origin: T, frame: S, position: DVec3, velocity: DVec3) -> Self {
let state = CartesianState::new(time, position, velocity);
Self {
state,
Expand All @@ -49,7 +49,7 @@ where
}
}

pub fn time(&self) -> Epoch {
pub fn time(&self) -> Time {
self.state.time()
}

Expand Down Expand Up @@ -127,7 +127,7 @@ where
{
#[allow(clippy::too_many_arguments)]
pub fn new(
time: Epoch,
time: Time,
origin: T,
frame: S,
semi_major: f64,
Expand All @@ -153,7 +153,7 @@ where
}
}

pub fn time(&self) -> Epoch {
pub fn time(&self) -> Time {
self.state.time()
}

Expand Down Expand Up @@ -233,18 +233,18 @@ where
mod tests {
use float_eq::assert_float_eq;

use super::*;
use crate::bodies::Earth;
use crate::frames::Icrf;
use crate::time::dates::{Date, Time};
use crate::time::epochs::TimeScale;

use super::*;
use crate::time::continuous::{Time, TimeScale};
use crate::time::dates::Date;
use crate::time::utc::UTC;

#[test]
fn test_cartesian() {
let date = Date::new(2023, 3, 25).expect("Date should be valid");
let time = Time::new(21, 8, 0).expect("Time should be valid");
let epoch = Epoch::from_date_and_time(TimeScale::TDB, date, time);
let utc = UTC::new(21, 8, 0).expect("Time should be valid");
let time = Time::from_date_and_utc_timestamp(TimeScale::TDB, date, utc);
let pos = DVec3::new(
-0.107622532467967e7,
-0.676589636432773e7,
Expand All @@ -256,12 +256,12 @@ mod tests {
-0.118801577532701e4,
) * 1e-3;

let cartesian = Cartesian::new(epoch, Earth, Icrf, pos, vel);
let cartesian = Cartesian::new(time, Earth, Icrf, pos, vel);
assert_eq!(cartesian.to_cartesian(), cartesian);

let cartesian1 = cartesian.to_keplerian().to_cartesian();

assert_eq!(cartesian1.time(), epoch);
assert_eq!(cartesian1.time(), time);
assert_eq!(cartesian1.origin(), Earth);
assert_eq!(cartesian1.reference_frame(), Icrf);

Expand All @@ -276,8 +276,8 @@ mod tests {
#[test]
fn test_keplerian() {
let date = Date::new(2023, 3, 25).expect("Date should be valid");
let time = Time::new(21, 8, 0).expect("Time should be valid");
let epoch = Epoch::from_date_and_time(TimeScale::TDB, date, time);
let utc = UTC::new(21, 8, 0).expect("Time should be valid");
let time = Time::from_date_and_utc_timestamp(TimeScale::TDB, date, utc);
let semi_major = 24464560.0e-3;
let eccentricity = 0.7311;
let inclination = 0.122138;
Expand All @@ -286,7 +286,7 @@ mod tests {
let true_anomaly = 0.44369564302687126;

let keplerian = Keplerian::new(
epoch,
time,
Earth,
Icrf,
semi_major,
Expand All @@ -300,7 +300,7 @@ mod tests {

let keplerian1 = keplerian.to_cartesian().to_keplerian();

assert_eq!(keplerian1.time(), epoch);
assert_eq!(keplerian1.time(), time);
assert_eq!(keplerian1.origin(), Earth);
assert_eq!(keplerian1.reference_frame(), Icrf);

Expand Down
27 changes: 13 additions & 14 deletions crates/lox_core/src/earth/nutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::earth::nutation::iau2000::nutation_iau2000a;
use crate::earth::nutation::iau2000::nutation_iau2000b;
use crate::earth::nutation::iau2006::nutation_iau2006a;
use crate::math::RADIANS_IN_ARCSECOND;
use crate::time::epochs::Epoch;
use crate::time::continuous::Time;
use crate::time::intervals::tdb_julian_centuries_since_j2000;
use crate::types::Radians;

Expand Down Expand Up @@ -63,9 +63,9 @@ impl Add<&Self> for Nutation {
}
}

/// Calculate nutation coefficients at `epoch` using the given [Model].
pub fn nutation(model: Model, epoch: Epoch) -> Nutation {
let t = tdb_julian_centuries_since_j2000(epoch);
/// Calculate nutation coefficients at `time` using the given [Model].
pub fn nutation(model: Model, time: Time) -> Nutation {
let t = tdb_julian_centuries_since_j2000(time);
match model {
Model::IAU1980 => nutation_iau1980(t),
Model::IAU2000A => nutation_iau2000a(t),
Expand Down Expand Up @@ -98,57 +98,56 @@ fn point1_microarcsec_to_rad(p1_uas: Point1Microarcsec) -> Radians {

#[cfg(test)]
mod tests {
use crate::time::continuous::TimeScale;
use float_eq::assert_float_eq;

use crate::time::epochs::{Epoch, TimeScale};

use super::*;

const TOLERANCE: f64 = 1e-12;

#[test]
fn test_nutation_iau1980() {
let epoch = Epoch::j2000(TimeScale::TT);
let time = Time::j2000(TimeScale::TT);
let expected = Nutation {
longitude: -0.00006750247617532478,
obliquity: -0.00002799221238377013,
};
let actual = nutation(Model::IAU1980, epoch);
let actual = nutation(Model::IAU1980, time);
assert_float_eq!(expected.longitude, actual.longitude, rel <= TOLERANCE);
assert_float_eq!(expected.obliquity, actual.obliquity, rel <= TOLERANCE);
}
#[test]
fn test_nutation_iau2000a() {
let epoch = Epoch::j2000(TimeScale::TT);
let time = Time::j2000(TimeScale::TT);
let expected = Nutation {
longitude: -0.00006754422426417299,
obliquity: -0.00002797083119237414,
};
let actual = nutation(Model::IAU2000A, epoch);
let actual = nutation(Model::IAU2000A, time);
assert_float_eq!(expected.longitude, actual.longitude, rel <= TOLERANCE);
assert_float_eq!(expected.obliquity, actual.obliquity, rel <= TOLERANCE);
}

#[test]
fn test_nutation_iau2000b() {
let epoch = Epoch::j2000(TimeScale::TT);
let time = Time::j2000(TimeScale::TT);
let expected = Nutation {
longitude: -0.00006754261253992235,
obliquity: -0.00002797092331098565,
};
let actual = nutation(Model::IAU2000B, epoch);
let actual = nutation(Model::IAU2000B, time);
assert_float_eq!(expected.longitude, actual.longitude, rel <= TOLERANCE);
assert_float_eq!(expected.obliquity, actual.obliquity, rel <= TOLERANCE);
}

#[test]
fn test_nutation_iau2006a() {
let epoch = Epoch::j2000(TimeScale::TT);
let time = Time::j2000(TimeScale::TT);
let expected = Nutation {
longitude: -0.00006754425598969513,
obliquity: -0.00002797083119237414,
};
let actual = nutation(Model::IAU2006A, epoch);
let actual = nutation(Model::IAU2006A, time);
assert_float_eq!(expected.longitude, actual.longitude, rel <= TOLERANCE);
assert_float_eq!(expected.obliquity, actual.obliquity, rel <= TOLERANCE);
}
Expand Down
10 changes: 6 additions & 4 deletions crates/lox_core/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@

use thiserror::Error;

#[derive(Error, Debug)]
#[derive(Error, Debug, PartialEq)]
pub enum LoxError {
#[error("invalid date `{0}-{1}-{2}`")]
InvalidDate(i64, i64, i64),
#[error("invalid time `{0}:{1}:{2}`")]
InvalidTime(i64, i64, i64),
#[error("invalid time `{0}:{1}:{2}`")]
InvalidSeconds(i64, i64, f64),
InvalidTime(u8, u8, u8),
#[error("seconds must be in the range [0.0, 60.0], but was `{0}`")]
InvalidSeconds(f64),
#[error("PerMille value must be in the range [0, 999], but was `{0}`")]
InvalidPerMille(u16),
#[error("day of year cannot be 366 for a non-leap year")]
NonLeapYear,
#[error("unknown body `{0}`")]
Expand Down
Loading
Loading