From a8edecaa0297582c67d7624569d0bbffd1c15bd2 Mon Sep 17 00:00:00 2001 From: NiklasVousten Date: Tue, 31 Oct 2023 14:29:51 +0100 Subject: [PATCH] Reduced code duplication by using generics. Updated Examples to use new Unit defintions. Already prepared generics for the use with f64. Co-Authored-By: Dominic --- Cargo.toml | 7 +- ...ts.rs => ephemeris_orbital_elements_si.rs} | 10 +- examples/ephemeris_vector.rs | 9 +- src/client.rs | 40 ++- src/ephemeris.rs | 186 ++++++++++-- src/lib.rs | 12 +- src/si/client.rs | 30 -- src/si/ephemeris.rs | 269 ------------------ src/si/mod.rs | 5 - src/units.rs | 56 ++++ tests/real_horizons.rs | 2 +- tests/real_horizons_si.rs | 6 +- 12 files changed, 293 insertions(+), 339 deletions(-) rename examples/{ephemeris_orbital_elements.rs => ephemeris_orbital_elements_si.rs} (62%) delete mode 100644 src/si/client.rs delete mode 100644 src/si/ephemeris.rs delete mode 100644 src/si/mod.rs create mode 100644 src/units.rs diff --git a/Cargo.toml b/Cargo.toml index 5d9d579..86b13e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,15 @@ serde = { version = "1.0.143", features = ["derive"] } thiserror = "1.0.32" tokio = { version = "1.20.1", features = ["time"] } uom = { version = "0.35.0", optional = true} +num-traits = "0.2.17" [dev-dependencies] env_logger = "0.10.0" tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread"] } [features] -si = ["dep:uom"] \ No newline at end of file +si = ["dep:uom"] + +[[example]] +name = "ephemeris_orbital_elements_si" +required-features = ["si"] diff --git a/examples/ephemeris_orbital_elements.rs b/examples/ephemeris_orbital_elements_si.rs similarity index 62% rename from examples/ephemeris_orbital_elements.rs rename to examples/ephemeris_orbital_elements_si.rs index 57656b9..bba0d7f 100644 --- a/examples/ephemeris_orbital_elements.rs +++ b/examples/ephemeris_orbital_elements_si.rs @@ -1,5 +1,7 @@ use chrono::{Duration, Utc}; -use rhorizons::{ephemeris_orbital_elements, major_bodies}; +use rhorizons::{ + ephemeris_orbital_elements_si, major_bodies, EphemerisOrbitalElementsItem, SiUnits, +}; #[tokio::main] async fn main() { @@ -22,10 +24,12 @@ async fn main() { start_time, stop_time ); - for elements in ephemeris_orbital_elements(earth.id, start_time, stop_time).await { + let elements: Vec> = + ephemeris_orbital_elements_si(earth.id, start_time, stop_time).await; + for item in elements { println!( "Eccentricity: {:?}, Semi-major axis: {:?}, Inclination: {:?}, Longitude of ascending node: {:?}, Argument of perifocus: {:?}, Mean anomaly: {:?}", - elements.eccentricity, elements.semi_major_axis, elements.inclination, elements.longitude_of_ascending_node, elements.argument_of_perifocus, elements.mean_anomaly + item.eccentricity, item.semi_major_axis, item.inclination, item.longitude_of_ascending_node, item.argument_of_perifocus, item.mean_anomaly ); } } diff --git a/examples/ephemeris_vector.rs b/examples/ephemeris_vector.rs index 4fa4ea3..3bc9bd9 100644 --- a/examples/ephemeris_vector.rs +++ b/examples/ephemeris_vector.rs @@ -1,5 +1,5 @@ use chrono::{Duration, Utc}; -use rhorizons::{ephemeris_vector, major_bodies}; +use rhorizons::{ephemeris_vector, major_bodies, DefaultUnits, EphemerisVectorItem}; #[tokio::main] async fn main() { @@ -22,10 +22,13 @@ async fn main() { start_time, stop_time ); - for vectors in ephemeris_vector(earth.id, start_time, stop_time).await { + let vectors: Vec> = + ephemeris_vector(earth.id, start_time, stop_time).await; + + for item in vectors { println!( "position: {:?}, velocity: {:?}", - vectors.position, vectors.velocity + item.position, item.velocity ); } } diff --git a/src/client.rs b/src/client.rs index ff81468..97f981f 100644 --- a/src/client.rs +++ b/src/client.rs @@ -78,7 +78,7 @@ pub async fn ephemeris_vector( id: i32, start_time: DateTime, stop_time: DateTime, -) -> Vec { +) -> Vec> { let result = query_with_retries(&[ ("COMMAND", id.to_string().as_str()), // Select Sun as a observer. Note that Solar System Barycenter is in a @@ -106,7 +106,7 @@ pub async fn ephemeris_orbital_elements( id: i32, start_time: DateTime, stop_time: DateTime, -) -> Vec { +) -> Vec> { let result = query_with_retries(&[ ("COMMAND", id.to_string().as_str()), // Select Sun as a observer. Note that Solar System Barycenter is in a @@ -128,3 +128,39 @@ pub async fn ephemeris_orbital_elements( EphemerisOrbitalElementsParser::parse(result.iter().map(String::as_str)).collect() } + +#[cfg(feature = "si")] +/// Get vector ephemeris (position and velocity) of a major body in SI-units. Coordinates are +/// relative to the Sun's center. +/// Needs the `si` feature to be enabled +/// +/// SI-units from the crate *uom*: +pub async fn ephemeris_vector_si( + id: i32, + start_time: DateTime, + stop_time: DateTime, +) -> Vec> { + crate::ephemeris_vector(id, start_time, stop_time) + .await + .into_iter() + .map(EphemerisVectorItem::from) + .collect() +} + +#[cfg(feature = "si")] +/// Get orbital element ephemeris (e.g. eccentricity, semi-major axis, ...) of a +/// major body relative to the Sun's center in SI-units. +/// Needs the `si` feature to be enabled +/// +/// SI-units from the crate *uom*: +pub async fn ephemeris_orbital_elements_si( + id: i32, + start_time: DateTime, + stop_time: DateTime, +) -> Vec> { + crate::ephemeris_orbital_elements(id, start_time, stop_time) + .await + .into_iter() + .map(EphemerisOrbitalElementsItem::from) + .collect() +} diff --git a/src/ephemeris.rs b/src/ephemeris.rs index c576168..b954c65 100644 --- a/src/ephemeris.rs +++ b/src/ephemeris.rs @@ -1,6 +1,10 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use crate::utilities::{take_expecting, take_or_empty}; +use num_traits::Float; + +#[cfg(feature = "si")] +use uom::si::{angle, angular_velocity, length, time, velocity}; /// Position (in km) and velocity (in km/s) of a body. /// @@ -16,19 +20,19 @@ use crate::utilities::{take_expecting, take_or_empty}; /// | RG | Range; distance from coordinate center | km | /// | RR | Range-rate; radial velocity wrt coord. center | km/sec | #[derive(Debug, PartialEq)] -pub struct EphemerisVectorItem { +pub struct EphemerisVectorItem> { /// Timestamp of the entry in UTC pub time: DateTime, /// Position int km of the moving body relative to the Sun /// /// [x, y, z] - pub position: [f32; 3], + pub position: [U::Length; 3], /// Velocity in km/s of the moving body relative to the Sun /// /// [v_x, v_y, v_z] - pub velocity: [f32; 3], + pub velocity: [U::Velocity; 3], } /// Orbital Elements of a body. Units are km, s and degrees @@ -50,7 +54,7 @@ pub struct EphemerisVectorItem { /// /// For a detailed explenation of keplarian orbital elements, visit [Wikipedia](https://en.wikipedia.org/wiki/Orbital_elements) #[derive(Debug, PartialEq)] -pub struct EphemerisOrbitalElementsItem { +pub struct EphemerisOrbitalElementsItem> { /// Timestamp of the entry in UTC pub time: DateTime, @@ -63,56 +67,131 @@ pub struct EphemerisOrbitalElementsItem { /// Distance from the center to the nearest point of the orbit in kilometer (km) /// /// See - pub periapsis_distance: f32, + pub periapsis_distance: U::Length, /// Tilt of the orbit /// /// Expressed in degrees in reference to the X-Y plane /// For futher information see - pub inclination: f32, + pub inclination: U::Angle, /// The point, were the orbit crosses the reference plane (X-Y plane) from south to north /// /// The unit of this value is in degrees. /// - pub longitude_of_ascending_node: f32, + pub longitude_of_ascending_node: U::Angle, /// Angle in degrees of the periapsis to the ascending node, in the direction of motion. /// /// - pub argument_of_perifocus: f32, + pub argument_of_perifocus: U::Angle, /// The timestamp (Julian Day Number) at which the body reaches the periapsis of the orbit /// /// - pub time_of_periapsis: f32, + pub time_of_periapsis: U::Time, /// The angular speed (degrees/sec) of a body to complete one orbit /// /// Assumes constant speed in a circular orbit. /// - pub mean_motion: f32, + pub mean_motion: U::AngularVelocity, /// Orbital distance from the periapsis to the moving body. /// /// The angle in degrees is in reference to a circular orbit. /// - pub mean_anomaly: f32, + pub mean_anomaly: U::Angle, /// Angle in degrees between the moving body and the periapsis of the orbit. /// /// The angle is defined in relation to the main focus point. /// - pub true_anomaly: f32, + pub true_anomaly: U::Angle, /// The sum of the periapsis and apoapsis distances divided by two in kilometer (km) /// /// - pub semi_major_axis: f32, + pub semi_major_axis: U::Length, /// Distance from the center to the farthest point of the orbit in kilometer (km) /// /// - pub apoapsis_distance: f32, + pub apoapsis_distance: U::Length, /// Time to complete on orbit in seconds /// /// Sidereal refers to the default period of an orbit. /// - pub siderral_orbit_period: f32, + pub siderral_orbit_period: U::Time, +} + +#[cfg(feature = "si")] +impl From> + for EphemerisVectorItem +where + F: Float + uom::Conversion + std::fmt::Debug, + uom::si::length::meter: uom::Conversion, + uom::si::length::kilometer: uom::Conversion, + uom::si::mass::kilogram: uom::Conversion, + uom::si::time::second: uom::Conversion, + uom::si::electric_current::ampere: uom::Conversion, + uom::si::thermodynamic_temperature::kelvin: uom::Conversion, + uom::si::amount_of_substance::mole: uom::Conversion, + uom::si::luminous_intensity::candela: uom::Conversion, + uom::si::velocity::kilometer_per_second: uom::Conversion, +{ + fn from(item: EphemerisVectorItem) -> Self { + let position: Vec, F>> = item + .position + .into_iter() + .map(length::Length::new::) + .collect(); + let velocity: Vec, F>> = item + .velocity + .into_iter() + .map(velocity::Velocity::new::) + .collect(); + + EphemerisVectorItem { + time: item.time, + position: position.try_into().unwrap(), + velocity: velocity.try_into().unwrap(), + } + } +} + +#[cfg(feature = "si")] +impl From> + for EphemerisOrbitalElementsItem +where + F: Float + uom::Conversion, + uom::si::length::meter: uom::Conversion, + uom::si::length::kilometer: uom::Conversion, + uom::si::mass::kilogram: uom::Conversion, + uom::si::time::second: uom::Conversion, + uom::si::time::day: uom::Conversion, + uom::si::electric_current::ampere: uom::Conversion, + uom::si::thermodynamic_temperature::kelvin: uom::Conversion, + uom::si::amount_of_substance::mole: uom::Conversion, + uom::si::luminous_intensity::candela: uom::Conversion, + uom::si::angle::degree: uom::Conversion, + uom::si::angular_velocity::degree_per_second: uom::Conversion, +{ + fn from(item: crate::EphemerisOrbitalElementsItem) -> Self { + EphemerisOrbitalElementsItem { + time: item.time, + eccentricity: item.eccentricity, + periapsis_distance: length::Length::new::(item.periapsis_distance), + inclination: angle::Angle::new::(item.inclination), + longitude_of_ascending_node: angle::Angle::new::( + item.longitude_of_ascending_node, + ), + argument_of_perifocus: angle::Angle::new::(item.argument_of_perifocus), + time_of_periapsis: time::Time::new::(item.time_of_periapsis), + mean_motion: angular_velocity::AngularVelocity::new::< + angular_velocity::degree_per_second, + >(item.mean_motion), + mean_anomaly: angle::Angle::new::(item.mean_anomaly), + true_anomaly: angle::Angle::new::(item.true_anomaly), + semi_major_axis: length::Length::new::(item.semi_major_axis), + apoapsis_distance: length::Length::new::(item.apoapsis_distance), + siderral_orbit_period: time::Time::new::(item.siderral_orbit_period), + } + } } enum EphemerisVectorParserState { @@ -200,7 +279,7 @@ impl<'a, Input: Iterator> EphemerisOrbitalElementsParser<'a, Inp } impl<'a, Input: Iterator> Iterator for EphemerisVectorParser<'a, Input> { - type Item = EphemerisVectorItem; + type Item = EphemerisVectorItem; fn next(&mut self) -> Option { loop { @@ -288,7 +367,7 @@ impl<'a, Input: Iterator> Iterator for EphemerisVectorParser<'a, } impl<'a, Input: Iterator> Iterator for EphemerisOrbitalElementsParser<'a, Input> { - type Item = EphemerisOrbitalElementsItem; + type Item = EphemerisOrbitalElementsItem; fn next(&mut self) -> Option { loop { @@ -476,8 +555,16 @@ fn parse_date_time(line: &str) -> DateTime { mod tests { use chrono::TimeZone; + #[cfg(feature = "si")] + use crate::units::SiUnits; + use super::*; + #[cfg(feature = "si")] + use uom::si::f32::*; + #[cfg(feature = "si")] + use uom::si::*; + #[test] fn test_parsing_ephemeris_vector() { let data = include_str!("vector.txt"); @@ -555,4 +642,69 @@ mod tests { assert_eq!(time, expected[i]); } } + + #[cfg(feature = "si")] + #[test] + fn test_parsing_ephemeris_vector_si() { + let data = include_str!("vector.txt"); + let ephem: Vec> = + EphemerisVectorParser::parse(data.lines()) + .map(EphemerisVectorItem::from) + .collect(); + assert_eq!(4, ephem.len()); + // TODO: This will probably fail intermittently due to float comparison. + assert_eq!( + EphemerisVectorItem { + time: Utc.with_ymd_and_hms(2022, 8, 13, 19, 55, 56).unwrap(), // A.D. 2022-Aug-13 19:55:56.0000 TDB + position: [ + Length::new::(1.870010427985840E+02), + Length::new::(2.484687803242536E+03), + Length::new::(-5.861602653492581E+03) + ], + + velocity: [ + Velocity::new::(-3.362664133558439E-01), + Velocity::new::(1.344100266143978E-02), + Velocity::new::(-5.030275220358716E-03) + ] + }, + ephem[0] + ); + } + + #[cfg(feature = "si")] + #[test] + fn test_parsing_ephemeris_orbital_elements_si() { + let data = include_str!("orbital_elements.txt"); + let ephem: Vec> = + EphemerisOrbitalElementsParser::parse(data.lines()) + .map(EphemerisOrbitalElementsItem::from) + .collect(); + assert_eq!(4, ephem.len()); + // TODO: This will probably fail intermittently due to float comparison. + assert_eq!( + EphemerisOrbitalElementsItem { + time: Utc.with_ymd_and_hms(2022, 6, 19, 18, 0, 0).unwrap(), // A.D. 2022-Jun-19 18:00:00.0000 TDB + + eccentricity: 1.711794334680415E-02, + periapsis_distance: Length::new::(1.469885520304013E+08), + inclination: Angle::new::(3.134746902320420E-03), + + longitude_of_ascending_node: Angle::new::(1.633896137466430E+02), + argument_of_perifocus: Angle::new::(3.006492364709574E+02), + time_of_periapsis: Time::new::(2459584.392523936927), + + mean_motion: AngularVelocity::new::( + 1.141316101270797E-05 + ), + mean_anomaly: Angle::new::(1.635515780663357E+02), + true_anomaly: Angle::new::(1.640958153023696E+02), + + semi_major_axis: Length::new::(1.495485150384278E+08), + apoapsis_distance: Length::new::(1.521084780464543E+08), + siderral_orbit_period: Time::new::(3.154253230977451E+07), + }, + ephem[0] + ); + } } diff --git a/src/lib.rs b/src/lib.rs index ed90884..c2f1a9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,15 +4,17 @@ mod client; mod ephemeris; mod major_bodies; +mod units; mod utilities; +pub use units::DefaultUnits; #[cfg(feature = "si")] -/// Ephemeris information based on SI-units. -/// Needs the `si` feature to be enabled -/// -/// SI-units from the crate *uom*: -pub mod si; +pub use units::SiUnits; pub use client::{ephemeris_orbital_elements, ephemeris_vector, major_bodies}; + +#[cfg(feature = "si")] +pub use client::{ephemeris_orbital_elements_si, ephemeris_vector_si}; + pub use ephemeris::{EphemerisOrbitalElementsItem, EphemerisVectorItem}; pub use major_bodies::MajorBody; diff --git a/src/si/client.rs b/src/si/client.rs deleted file mode 100644 index 687ace0..0000000 --- a/src/si/client.rs +++ /dev/null @@ -1,30 +0,0 @@ -use chrono::{DateTime, Utc}; - -use crate::si::ephemeris::{EphemerisOrbitalElementsItem, EphemerisVectorItem}; - -/// Get vector ephemeris (position and velocity) of a major body. Coordinates are -/// relative to the Sun's center. -pub async fn ephemeris_vector( - id: i32, - start_time: DateTime, - stop_time: DateTime, -) -> Vec { - crate::ephemeris_vector(id, start_time, stop_time) - .await - .into_iter() - .map(EphemerisVectorItem::from) - .collect() -} -/// Get orbital element ephemeris (e.g. eccentricity, semi-major axis, ...) of a -/// major body relative to the Sun's center -pub async fn ephemeris_orbital_elements( - id: i32, - start_time: DateTime, - stop_time: DateTime, -) -> Vec { - crate::ephemeris_orbital_elements(id, start_time, stop_time) - .await - .into_iter() - .map(EphemerisOrbitalElementsItem::from) - .collect() -} diff --git a/src/si/ephemeris.rs b/src/si/ephemeris.rs deleted file mode 100644 index c6ff497..0000000 --- a/src/si/ephemeris.rs +++ /dev/null @@ -1,269 +0,0 @@ -use chrono::{DateTime, Utc}; -use uom::si::f32::{Angle, AngularVelocity, Length, Time, Velocity}; -use uom::si::{angle, angular_velocity, length, time, velocity}; - -/// Position and velocity of a body. Units are SI-based -/// -/// | Horizons Symbol | Meaning | -/// |-----------------|-------------------------------------------------| -/// | X | X-component of position vector | -/// | Y | Y-component of position vector | -/// | Z | Z-component of position vector | -/// | VX | X-component of velocity vector | -/// | VY | Y-component of velocity vector | -/// | VZ | Z-component of velocity vector | -/// | LT | One-way down-leg Newtonian light-time | -/// | RG | Range; distance from coordinate center | -/// | RR | Range-rate; radial velocity wrt coord. center | -#[derive(Debug, PartialEq)] -pub struct EphemerisVectorItem { - /// Timestamp of the entry in UTC - pub time: DateTime, - - /// Position of the moving body relative to the Sun - /// - /// [x, y, z] - pub position: [Length; 3], - - /// Velocity of the moving body relative to the Sun - /// - /// [v_x, v_y, v_z] - pub velocity: [Velocity; 3], -} - -/// Orbital Elements of a body. Units are SI-based -/// -/// | Horizons Symbol | Meaning | Mathematical Symbol | -/// |-----------------|-----------------------------|---------------------| -/// | EC | Eccentricity | *e* | -/// | QR | Periapsis distance | *q* | -/// | IN | Inclination w.r.t X-Y plane | *i* | -/// | OM | Longitude of Ascending Node | Ω (Omega) | -/// | W | Argument of Perifocus | *ω* | -/// | Tp | Time of periapsis | | -/// | N | Mean motion | *n* | -/// | MA | Mean anomaly | *M* | -/// | TA | True anomaly | *ν*, nu | -/// | A | Semi-major axis | *a* | -/// | AD | Apoapsis distance | | -/// | PR | Sidereal orbit period | | -/// -/// For a detailed explenation of keplarian orbital elements, visit [Wikipedia](https://en.wikipedia.org/wiki/Orbital_elements) -#[derive(Debug, PartialEq)] -pub struct EphemerisOrbitalElementsItem { - /// Timestamp of the entry in UTC - pub time: DateTime, - - /// Describes the "roundness" of the orbit. - /// - /// Value of 0 means a circle, everything until 1 is an eliptic orbit. - /// A value of 1 is a parabolic trajectory and everythin greater 1 a hyperbolic trajectory. - /// See - pub eccentricity: f32, - /// Distance from the center to the nearest point of the orbit - /// - /// See - pub periapsis_distance: Length, - /// Tilt of the orbit - /// - /// In reference to the X-Y plane - /// For futher information see - pub inclination: Angle, - - /// The point, were the orbit crosses the reference plane (X-Y plane) from south to north - /// - /// - pub longitude_of_ascending_node: Angle, - /// Angle of the periapsis to the ascending node, in the direction of motion. - /// - /// - pub argument_of_perifocus: Angle, - /// The timestamp (Julian Day Number) at which the body reaches the periapsis of the orbit - /// - /// - pub time_of_periapsis: Time, - - /// The angular speed of a body to complete one orbit - /// - /// Assumes constant speed in a circular orbit. - /// - pub mean_motion: AngularVelocity, - /// Orbital distance from the periapsis to the moving body. - /// - /// The angle is in reference to a circular orbit. - /// - pub mean_anomaly: Angle, - /// Angle between the moving body and the periapsis of the orbit. - /// - /// The angle is defined in relation to the main focus point. - /// - pub true_anomaly: Angle, - - /// The sum of the periapsis and apoapsis distances divided by two - /// - /// - pub semi_major_axis: Length, - /// Distance from the center to the farthest point of the orbit - /// - /// - pub apoapsis_distance: Length, - /// Time to complete on orbit in seconds - /// - /// Sidereal refers to the default period of an orbit. - /// - pub siderral_orbit_period: Time, -} - -impl From for EphemerisVectorItem { - fn from(item: crate::EphemerisVectorItem) -> Self { - let position: Vec = item - .position - .into_iter() - .map(Length::new::) - .collect(); - let velocity: Vec = item - .velocity - .into_iter() - .map(Velocity::new::) - .collect(); - - EphemerisVectorItem { - time: item.time, - position: position.try_into().unwrap(), - velocity: velocity.try_into().unwrap(), - } - } -} - -impl From for EphemerisOrbitalElementsItem { - fn from(item: crate::EphemerisOrbitalElementsItem) -> Self { - EphemerisOrbitalElementsItem { - time: item.time, - eccentricity: item.eccentricity, - periapsis_distance: Length::new::(item.periapsis_distance), - inclination: Angle::new::(item.inclination), - longitude_of_ascending_node: Angle::new::( - item.longitude_of_ascending_node, - ), - argument_of_perifocus: Angle::new::(item.argument_of_perifocus), - time_of_periapsis: Time::new::(item.time_of_periapsis), - mean_motion: AngularVelocity::new::( - item.mean_motion, - ), - mean_anomaly: Angle::new::(item.mean_anomaly), - true_anomaly: Angle::new::(item.true_anomaly), - semi_major_axis: Length::new::(item.semi_major_axis), - apoapsis_distance: Length::new::(item.apoapsis_distance), - siderral_orbit_period: Time::new::(item.siderral_orbit_period), - } - } -} -/* -pub struct EphemerisVectorParser<'a, Input: Iterator> { - parser: crate::ephemeris::EphemerisVectorParser<'a, Input>, -} - -pub struct EphemerisOrbitalElementsParser<'a, Input: Iterator> { - parser: crate::ephemeris::EphemerisOrbitalElementsParser<'a, Input>, -} - -impl<'a, Input: Iterator> EphemerisVectorParser<'a, Input> { - pub fn parse(input: Input) -> Self { - Self { - parser: crate::ephemeris::EphemerisVectorParser::parse(input), - } - } -} - -impl<'a, Input: Iterator> EphemerisOrbitalElementsParser<'a, Input> { - pub fn parse(input: Input) -> Self { - Self { - parser: crate::ephemeris::EphemerisOrbitalElementsParser::parse(input), - } - } -} - -impl<'a, Input: Iterator> Iterator for EphemerisVectorParser<'a, Input> { - type Item = EphemerisVectorItem; - - fn next(&mut self) -> Option { - self.parser.next().map(|v| Self::Item::from(v)) - } -} - -impl<'a, Input: Iterator> Iterator for EphemerisOrbitalElementsParser<'a, Input> { - type Item = EphemerisOrbitalElementsItem; - - fn next(&mut self) -> Option { - self.parser.next().map(|v| Self::Item::from(v)) - } -} - */ -#[cfg(test)] -mod tests { - use chrono::TimeZone; - - use super::*; - use crate::ephemeris::{EphemerisOrbitalElementsParser, EphemerisVectorParser}; - - #[test] - fn test_parsing_ephemeris_vector() { - let data = include_str!("../vector.txt"); - let ephem: Vec<_> = EphemerisVectorParser::parse(data.lines()) - .map(|e| EphemerisVectorItem::from(e)) - .collect(); - assert_eq!(4, ephem.len()); - // TODO: This will probably fail intermittently due to float comparison. - assert_eq!( - EphemerisVectorItem { - time: Utc.with_ymd_and_hms(2022, 8, 13, 19, 55, 56).unwrap(), // A.D. 2022-Aug-13 19:55:56.0000 TDB - position: [ - Length::new::(1.870010427985840E+02), - Length::new::(2.484687803242536E+03), - Length::new::(-5.861602653492581E+03) - ], - - velocity: [ - Velocity::new::(-3.362664133558439E-01), - Velocity::new::(1.344100266143978E-02), - Velocity::new::(-5.030275220358716E-03) - ] - }, - ephem[0] - ); - } - - #[test] - fn test_parsing_ephemeris_orbital_elements() { - let data = include_str!("../orbital_elements.txt"); - let ephem: Vec<_> = EphemerisOrbitalElementsParser::parse(data.lines()) - .map(|e| EphemerisOrbitalElementsItem::from(e)) - .collect(); - assert_eq!(4, ephem.len()); - // TODO: This will probably fail intermittently due to float comparison. - assert_eq!( - EphemerisOrbitalElementsItem { - time: Utc.with_ymd_and_hms(2022, 6, 19, 18, 0, 0).unwrap(), // A.D. 2022-Jun-19 18:00:00.0000 TDB - - eccentricity: 1.711794334680415E-02, - periapsis_distance: Length::new::(1.469885520304013E+08), - inclination: Angle::new::(3.134746902320420E-03), - - longitude_of_ascending_node: Angle::new::(1.633896137466430E+02), - argument_of_perifocus: Angle::new::(3.006492364709574E+02), - time_of_periapsis: Time::new::(2459584.392523936927), - - mean_motion: AngularVelocity::new::( - 1.141316101270797E-05 - ), - mean_anomaly: Angle::new::(1.635515780663357E+02), - true_anomaly: Angle::new::(1.640958153023696E+02), - - semi_major_axis: Length::new::(1.495485150384278E+08), - apoapsis_distance: Length::new::(1.521084780464543E+08), - siderral_orbit_period: Time::new::(3.154253230977451E+07), - }, - ephem[0] - ); - } -} diff --git a/src/si/mod.rs b/src/si/mod.rs deleted file mode 100644 index 0c98933..0000000 --- a/src/si/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod client; -mod ephemeris; - -pub use client::{ephemeris_orbital_elements, ephemeris_vector}; -pub use ephemeris::{EphemerisOrbitalElementsItem, EphemerisVectorItem}; diff --git a/src/units.rs b/src/units.rs new file mode 100644 index 0000000..8255857 --- /dev/null +++ b/src/units.rs @@ -0,0 +1,56 @@ +use num_traits::Float; + +pub trait Units { + type Angle; + type AngularVelocity; + type Length; + type Time; + type Velocity; +} + +#[derive(Debug, PartialEq)] +/// Used to define default floating units for Ephemeris Items. +/// +/// ```rust +/// use rhorizons::*; +/// let vector: EphemerisVectorItem; +/// ``` +pub struct DefaultUnits; + +#[cfg(feature = "si")] +#[derive(Debug, PartialEq)] +/// Used to define SI-based floating units for Ephemeris Items. +/// Needs the `si` feature to be enabled. +/// +/// ```rust +/// use rhorizons::*; +/// let orbital_elements: EphemerisOrbitalElementsItem; +/// ``` +pub struct SiUnits; + +impl Units for DefaultUnits { + type Angle = F; + type AngularVelocity = F; + type Length = F; + type Time = F; + type Velocity = F; +} + +#[cfg(feature = "si")] +impl Units for SiUnits +where + F: Float + uom::Conversion, + uom::si::length::meter: uom::Conversion, + uom::si::mass::kilogram: uom::Conversion, + uom::si::time::second: uom::Conversion, + uom::si::electric_current::ampere: uom::Conversion, + uom::si::thermodynamic_temperature::kelvin: uom::Conversion, + uom::si::amount_of_substance::mole: uom::Conversion, + uom::si::luminous_intensity::candela: uom::Conversion, +{ + type Angle = uom::si::angle::Angle, F>; + type AngularVelocity = uom::si::angular_velocity::AngularVelocity, F>; + type Length = uom::si::length::Length, F>; + type Time = uom::si::time::Time, F>; + type Velocity = uom::si::velocity::Velocity, F>; +} diff --git a/tests/real_horizons.rs b/tests/real_horizons.rs index b87b241..bc9ba50 100644 --- a/tests/real_horizons.rs +++ b/tests/real_horizons.rs @@ -25,7 +25,7 @@ async fn getting_earths_ephemeris() { // X = 1.379561021896053E+08 Y = 5.667156012930278E+07 Z =-2.601196352168918E+03 // VX=-1.180102398133564E+01 VY= 2.743089439727051E+01 VZ= 3.309367894566151E-05 // LT= 4.974865749957088E+02 RG= 1.491427231399648E+08 RR=-4.926267109444211E-01 - let vectors = ephemeris_vector( + let vectors: Vec> = ephemeris_vector( 399, Utc.with_ymd_and_hms(2016, 10, 15, 12, 0, 0).unwrap(), Utc.with_ymd_and_hms(2016, 10, 15, 13, 0, 0).unwrap(), diff --git a/tests/real_horizons_si.rs b/tests/real_horizons_si.rs index a88670c..c3f6e93 100644 --- a/tests/real_horizons_si.rs +++ b/tests/real_horizons_si.rs @@ -4,7 +4,7 @@ mod si { /// Tests in this module connect to the real Horizons system. As such, they /// require Internet access and might start failing if Horizon's API changes. use chrono::{TimeZone, Utc}; - use rhorizons::si::*; + use rhorizons::*; use uom::si::f32::Length; use uom::si::length; @@ -22,7 +22,7 @@ mod si { // X = 1.379561021896053E+08 Y = 5.667156012930278E+07 Z =-2.601196352168918E+03 // VX=-1.180102398133564E+01 VY= 2.743089439727051E+01 VZ= 3.309367894566151E-05 // LT= 4.974865749957088E+02 RG= 1.491427231399648E+08 RR=-4.926267109444211E-01 - let vectors = ephemeris_vector( + let vectors: Vec> = ephemeris_vector_si( 399, Utc.with_ymd_and_hms(2016, 10, 15, 12, 0, 0).unwrap(), Utc.with_ymd_and_hms(2016, 10, 15, 13, 0, 0).unwrap(), @@ -46,7 +46,7 @@ mod si { // X =-8.125930353044792E+08 Y =-6.890018021386522E+07 Z = 1.846888215010012E+07 // VX= 9.479984730623543E-01 VY=-1.241342015681963E+01 VZ= 3.033885124560420E-02 // LT= 2.720942202383012E+03 RG= 8.157179509283365E+08 RR= 1.048282114626244E-01 - let vectors = ephemeris_vector( + let vectors = ephemeris_vector_si( 599, Utc.with_ymd_and_hms(2016, 10, 15, 12, 0, 0).unwrap(), Utc.with_ymd_and_hms(2016, 10, 15, 13, 0, 0).unwrap(),