diff --git a/.github/ISSUE_TEMPLATE/stakeholder-need.md b/.github/ISSUE_TEMPLATE/stakeholder-need.md index 86b2c881..d0f064a6 100644 --- a/.github/ISSUE_TEMPLATE/stakeholder-need.md +++ b/.github/ISSUE_TEMPLATE/stakeholder-need.md @@ -23,7 +23,7 @@ How do we test that these requirements are fulfilled correctly? What are some ed Document, discuss, and optionally upload design diagram into this section. - - - \ No newline at end of file diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml new file mode 100644 index 00000000..3d868ec1 --- /dev/null +++ b/.github/workflows/coverage.yaml @@ -0,0 +1,40 @@ +name: Daily Workflow + +on: + push: + branches: + - master + workflow_dispatch: + +jobs: + full-coverage: + name: Unit test and integration test coverage analysis + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: rustfmt, clippy + + - name: Download data + run: | + wget -O data/de440s.bsp http://public-data.nyxspace.com/anise/de440s.bsp + wget -O data/de440s.bsp http://public-data.nyxspace.com/anise/de438.bsp # GMAT validation cases + wget -O data/pck08.pca http://public-data.nyxspace.com/anise/v0.4/pck08.pca + wget -O data/earth_latest_high_prec.bpc http://public-data.nyxspace.com/anise/ci/earth_latest_high_prec-2023-09-08.bpc + + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + - name: Generate full code coverage + run: cargo llvm-cov --lcov --output-path lcov.info + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos + files: lcov.info + fail_ci_if_error: false diff --git a/.github/workflows/daily.yaml b/.github/workflows/daily.yaml index da604e59..75bf9c4d 100644 --- a/.github/workflows/daily.yaml +++ b/.github/workflows/daily.yaml @@ -3,37 +3,24 @@ name: Daily Workflow on: workflow_dispatch: schedule: - - cron: '0 0 * * *' # Run at midnight every day + - cron: "0 0 * * *" # Run at midnight every day jobs: - full-coverage: - name: Unit test and integration test coverage analysis - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: rustfmt, clippy - - - name: Download data - run: | - wget -O data/de440s.bsp http://public-data.nyxspace.com/anise/de440s.bsp - wget -O data/de440s.bsp http://public-data.nyxspace.com/anise/de438.bsp # GMAT validation cases - wget -O data/pck08.pca http://public-data.nyxspace.com/anise/v0.4/pck08.pca - wget -O data/earth_latest_high_prec.bpc http://public-data.nyxspace.com/anise/ci/earth_latest_high_prec-2023-09-08.bpc + full-coverage: + name: Unit test and integration test coverage analysis + runs-on: ubuntu-latest - - name: Install cargo-llvm-cov - uses: taiki-e/install-action@cargo-llvm-cov - - name: Generate full code coverage - run: cargo llvm-cov --lcov --output-path lcov.info - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos - files: lcov.info - fail_ci_if_error: false + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: rustfmt, clippy + + - name: Cargo check + run: | + cargo update + cargo check diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index a8ac778b..677d11e5 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -50,7 +50,7 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust.version }} - + - name: Download data run: | wget -O data/de440s.bsp http://public-data.nyxspace.com/anise/de440s.bsp @@ -77,7 +77,7 @@ jobs: - name: All integration tests (release) run: cargo test --release --test "*" - + - name: Doc Test run: cargo test --doc @@ -102,39 +102,6 @@ jobs: - name: Run cargo clippy run: cargo clippy -- -D warnings - ut-coverage: - name: Coverage (unit tests only) - runs-on: ubuntu-latest - needs: [tests] - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install stable toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: rustfmt, clippy - - - name: Download data - run: | - wget -O data/de440s.bsp http://public-data.nyxspace.com/anise/de440s.bsp - wget -O data/de440s.bsp http://public-data.nyxspace.com/anise/de438.bsp # GMAT validation cases - wget -O data/pck08.pca http://public-data.nyxspace.com/anise/v0.4/pck08.pca - wget -O data/earth_latest_high_prec.bpc http://public-data.nyxspace.com/anise/ci/earth_latest_high_prec-2023-09-08.bpc - - - name: Install cargo-llvm-cov - uses: taiki-e/install-action@cargo-llvm-cov - - name: Generate unit tests code coverage - run: cargo llvm-cov --lib --lcov --output-path lcov.info - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos - files: lcov.info - fail_ci_if_error: false - release: name: Release runs-on: ubuntu-latest diff --git a/src/cosmic/eclipse.rs b/src/cosmic/eclipse.rs index 28c3a8c9..62d8ce2c 100644 --- a/src/cosmic/eclipse.rs +++ b/src/cosmic/eclipse.rs @@ -299,15 +299,7 @@ pub fn eclipse_state( // If the light source's radius is zero, just call the line of sight algorithm if ls_mean_eq_radius_km < f64::EPSILON { - // TODO(ANISE): I think I need the opposite data here! Hence the neg! - let observed = almanac.transform_to(observer, light_source, None)?; - - // let observed = almanac.celestial_state( - // &light_source.ephem_path(), - // observer.epoch, - // observer.frame, - // LightTimeCalc::None, - // ); + let observed = -almanac.transform_to(observer, light_source, None)?; return line_of_sight(observer, observed, eclipsing_body, almanac); } diff --git a/src/cosmic/frames.rs b/src/cosmic/frames.rs deleted file mode 100644 index 96358855..00000000 --- a/src/cosmic/frames.rs +++ /dev/null @@ -1,245 +0,0 @@ -/* - Nyx, blazing fast astrodynamics - Copyright (C) 2018-onwards Christopher Rabotin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -use super::Bodies; -use crate::time::{Duration, Unit}; -use std::cmp::PartialEq; -use std::convert::TryFrom; -use std::f64::consts::PI; -use std::fmt; - -#[allow(non_snake_case, clippy::upper_case_acronyms)] -#[derive(Copy, Clone, PartialEq)] -pub enum Frame { - /// Any celestial frame which only has a GM (e.g. 3 body frames) - Celestial { - gm: f64, - ephem_path: [Option; 3], - frame_path: [Option; 3], - }, - /// Any Geoid which has a GM, flattening value, etc. - Geoid { - gm: f64, - flattening: f64, - equatorial_radius: f64, - semi_major_radius: f64, - ephem_path: [Option; 3], - frame_path: [Option; 3], - }, - /// Velocity, Normal, Cross (called VNB in GMAT) - VNC, - /// Radial, Cross, Normal - RCN, - /// Radial, in-track, normal - RIC, - /// SEZ or topocentric frame. The positive horizontal vector S is due south , the positive horizontal vector E is east, and the vector Z normal to the surface of the earth (up) is the third axis. - SEZ, - /// Used as a placeholder only - Inertial, -} - -impl Frame { - pub fn is_geoid(&self) -> bool { - matches!(self, Frame::Geoid { .. }) - } - - pub fn is_celestial(&self) -> bool { - matches!(self, Frame::Celestial { .. }) - } - - pub fn ephem_path(&self) -> Vec { - match self { - Frame::Celestial { ephem_path, .. } | Frame::Geoid { ephem_path, .. } => { - let mut path = Vec::with_capacity(3); - for p in ephem_path.iter().flatten() { - path.push(*p) - } - path - } - _ => panic!("Frame is not Celestial or Geoid in kind"), - } - } - - pub fn frame_path(&self) -> Vec { - match self { - Frame::Celestial { frame_path, .. } | Frame::Geoid { frame_path, .. } => { - let mut path = Vec::with_capacity(3); - for p in frame_path.iter().flatten() { - path.push(*p) - } - path - } - _ => panic!("Frame is not Celestial or Geoid in kind"), - } - } - - pub fn gm(&self) -> f64 { - match self { - Frame::Celestial { gm, .. } | Frame::Geoid { gm, .. } => *gm, - _ => panic!("Frame is not Celestial or Geoid in kind"), - } - } - - /// Allows mutuating the GM for this frame - pub fn gm_mut(&mut self, new_gm: f64) { - match self { - Self::Geoid { ref mut gm, .. } | Self::Celestial { ref mut gm, .. } => *gm = new_gm, - _ => panic!("Frame is not Celestial or Geoid in kind"), - } - } - - pub fn equatorial_radius(&self) -> f64 { - match self { - Frame::Geoid { - equatorial_radius, .. - } => *equatorial_radius, - _ => panic!("Frame is not Geoid in kind"), - } - } - - pub fn flattening(&self) -> f64 { - match self { - Frame::Geoid { flattening, .. } => *flattening, - _ => panic!("Frame is not Geoid in kind"), - } - } - - pub fn flattening_mut(&mut self, new_flattening: f64) { - match self { - Self::Geoid { - ref mut flattening, .. - } => *flattening = new_flattening, - _ => panic!("Frame is not Geoid in kind"), - } - } - - pub fn semi_major_radius(&self) -> f64 { - match self { - Frame::Geoid { - semi_major_radius, .. - } => *semi_major_radius, - _ => panic!("Frame is not Geoid in kind"), - } - } - - /// Returns the angular velocity for _some_ planets and moons - /// Source for Earth: G. Xu and Y. Xu, "GPS", DOI 10.1007/978-3-662-50367-6_2, 2016 (confirmed by https://hpiers.obspm.fr/eop-pc/models/constants.html) - /// Source for everything else: https://en.wikipedia.org/w/index.php?title=Day&oldid=1008298887 - #[allow(clippy::identity_op)] - pub fn angular_velocity(&self) -> f64 { - let period_to_mean_motion = |dur: Duration| -> f64 { 2.0 * PI / dur.to_seconds() }; - match Bodies::try_from(self.ephem_path()).unwrap() { - Bodies::MercuryBarycenter | Bodies::Mercury => { - period_to_mean_motion(58 * Unit::Day + 15 * Unit::Hour + 30 * Unit::Minute) - } - Bodies::VenusBarycenter | Bodies::Venus => period_to_mean_motion(243 * Unit::Day), - Bodies::Earth => 7.292_115_146_706_4e-5, - Bodies::Luna => { - period_to_mean_motion(27 * Unit::Day + 7 * Unit::Hour + 12 * Unit::Minute) - } - Bodies::MarsBarycenter => period_to_mean_motion(1 * Unit::Day + 37 * Unit::Minute), - Bodies::JupiterBarycenter => period_to_mean_motion(9 * Unit::Hour + 56 * Unit::Minute), - Bodies::SaturnBarycenter => period_to_mean_motion(10 * Unit::Hour + 30 * Unit::Minute), - Bodies::UranusBarycenter => period_to_mean_motion(17 * Unit::Hour + 14 * Unit::Minute), - Bodies::NeptuneBarycenter => period_to_mean_motion(16 * Unit::Hour + 6 * Unit::Minute), - _ => unimplemented!(), - } - } - - /// Returns whether this frame is body fixed or not - pub fn is_body_fixed(&self) -> bool { - self.frame_path().len() == 2 || self.frame_path().len() == 3 - } -} - -impl fmt::Display for Frame { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Frame::Celestial { .. } | Frame::Geoid { .. } => { - if self.frame_path().len() == 2 { - write!( - f, - "IAU {}", - Bodies::try_from(self.ephem_path()).unwrap().name() - ) - } else { - write!( - f, - "{} {}", - Bodies::try_from(self.ephem_path()).unwrap().name(), - match self.frame_path().len() { - 0 | 1 => "J2000".to_string(), - 2 => "IAU Fixed".to_string(), - 3 => "IAU Poles Fixed".to_string(), - _ => "Custom".to_string(), - } - ) - } - } - othframe => write!(f, "{othframe:?}"), - } - } -} - -#[allow(non_snake_case, clippy::upper_case_acronyms)] -impl fmt::Debug for Frame { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Frame::Celestial { gm, .. } => { - write!( - f, - "{} {} (μ = {:.06} km^3/s^2)", - Bodies::try_from(self.ephem_path()).unwrap().name(), - match self.frame_path().len() { - 0 | 1 => "J2000".to_string(), - 2 => "IAU Fixed".to_string(), - 3 => "IAU Poles Fixed".to_string(), - _ => "Custom".to_string(), - }, - gm, - ) - } - Frame::Geoid { - gm, - equatorial_radius, - flattening, - .. - } => { - write!( - f, - "{} {} (μ = {:.06} km^3/s^2 , r = {:.06} km, f = {:.09})", - Bodies::try_from(self.ephem_path()).unwrap().name(), - match self.frame_path().len() { - 0 | 1 => "J2000".to_string(), - 2 => "IAU Fixed".to_string(), - 3 => "IAU Poles Fixed".to_string(), - _ => "Custom".to_string(), - }, - gm, - equatorial_radius, - flattening, - ) - } - Frame::VNC => write!(f, "VNC"), - Frame::RCN => write!(f, "RCN"), - Frame::RIC => write!(f, "RIC"), - Frame::SEZ => write!(f, "SEZ"), - Frame::Inertial => write!(f, "Inertial"), - } - } -} diff --git a/src/cosmic/mod.rs b/src/cosmic/mod.rs index dafb5597..bc30067a 100644 --- a/src/cosmic/mod.rs +++ b/src/cosmic/mod.rs @@ -26,7 +26,7 @@ use crate::errors::StateError; use crate::linalg::allocator::Allocator; use crate::linalg::{DefaultAllocator, DimName, OMatrix, OVector}; use crate::md::StateParameter; -use crate::time::{Duration, Epoch}; +use crate::time::Epoch; use snafu::Snafu; use std::fmt; @@ -36,11 +36,6 @@ pub trait TimeTagged { fn epoch(&self) -> Epoch; /// Set the Epoch fn set_epoch(&mut self, epoch: Epoch); - - /// Shift this epoch by a duration (can be negative) - fn shift_by(&mut self, duration: Duration) { - self.set_epoch(self.epoch() + duration); - } } /// A trait for generate propagation and estimation state. @@ -162,19 +157,12 @@ pub use self::bplane::*; mod spacecraft; pub use self::spacecraft::*; -// Re-Export frames -// mod frames; - -mod rotations; -pub use self::rotations::*; - /// The eclipse module allows finding eclipses and (conversely) visibility between a state and another one (e.g. a planet or the Sun). pub mod eclipse; /// Speed of light in meters per second -pub const SPEED_OF_LIGHT: f64 = 299_792_458.0; -/// Speed of light in kilometers per second -pub const SPEED_OF_LIGHT_KMS: f64 = SPEED_OF_LIGHT / 1000.0; +pub const SPEED_OF_LIGHT_M_S: f64 = SPEED_OF_LIGHT_KM_S * 1e3; +pub use anise::constants::SPEED_OF_LIGHT_KM_S; /// Astronomical unit, in kilometers, according to the [IAU](https://www.iau.org/public/themes/measuring/). pub const AU: f64 = 149_597_870.700; diff --git a/src/cosmic/rotations.rs b/src/cosmic/rotations.rs deleted file mode 100644 index 484ddf0a..00000000 --- a/src/cosmic/rotations.rs +++ /dev/null @@ -1,255 +0,0 @@ -/* - Nyx, blazing fast astrodynamics - Copyright (C) 2018-onwards Christopher Rabotin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -use crate::log::error; -use crate::na::Matrix3; -use crate::time::Epoch; -use crate::utils::{r1, r2, r3}; -use meval::{Context, Expr}; -use std::cmp::PartialEq; -use std::collections::HashMap; -use std::fmt; -use std::io::{Error as IoError, ErrorKind as IoErrorKind}; -use std::str::FromStr; - -pub trait ParentRotation: Send + Sync + fmt::Debug { - fn dcm_to_parent(&self, datetime: Epoch) -> Option>; -} - -#[derive(Debug)] -pub struct NoRotation; -impl ParentRotation for NoRotation { - fn dcm_to_parent(&self, _: Epoch) -> Option> { - Some(Matrix3::identity()) - } -} - -/// Defines an Euler rotation, angle must be in radians -#[derive(Clone, Copy, Debug)] -pub enum EulerRotation { - R1(f64), - R2(f64), - R3(f64), -} - -impl EulerRotation { - pub fn r1_from_degrees(angle_deg: f64) -> Self { - Self::R1(angle_deg.to_radians()) - } - pub fn r2_from_degrees(angle_deg: f64) -> Self { - Self::R2(angle_deg.to_radians()) - } - pub fn r3_from_degrees(angle_deg: f64) -> Self { - Self::R3(angle_deg.to_radians()) - } - /// Get the DCM from this Euler rotation - pub fn dcm(&self) -> Matrix3 { - match *self { - Self::R1(angle) => r1(angle), - Self::R2(angle) => r2(angle), - Self::R3(angle) => r3(angle), - } - } -} - -impl ParentRotation for EulerRotation { - fn dcm_to_parent(&self, _: Epoch) -> Option> { - Some(self.dcm()) - } -} - -/// A fixed three-axis Euler rotation -#[derive(Debug)] -pub struct Euler3Axis { - /// The first rotation (e.g. R3) - pub first: EulerRotation, - /// The second rotation (e.g. R1) - pub second: EulerRotation, - /// The third and final rotation (e.g. R3, to complete a 3-1-1 rotation) - pub third: EulerRotation, -} - -impl ParentRotation for Euler3Axis { - fn dcm_to_parent(&self, _: Epoch) -> Option> { - Some(self.third.dcm() * self.second.dcm() * self.first.dcm()) - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum AngleUnit { - Degrees, - Radians, -} - -impl FromStr for AngleUnit { - type Err = IoError; - fn from_str(s: &str) -> Result { - if s.to_lowercase().starts_with("deg") { - Ok(AngleUnit::Degrees) - } else if s.to_lowercase().starts_with("rad") { - Ok(AngleUnit::Radians) - } else { - Err(IoError::new( - IoErrorKind::InvalidData, - format!("unknown angles `{s}`"), - )) - } - } -} - -/// A time varying three-axis Euler rotation -#[derive(Clone)] -pub struct Euler3AxisDt -where - Self: Send + Sync, -{ - pub base_context: HashMap, - pub rot_order: [(EulerRotation, Expr); 3], - pub unit: AngleUnit, - pub is_ra_dec_w: bool, -} - -impl Euler3AxisDt { - /// Initialize a new time varying transformation. - /// Reserved keywords in the context are "T" for centuries past 2000 Jan 1 12h TBD - /// epoch (JDE 2451545.0), and "d" for days since that epoch. - fn new( - first_rot: (EulerRotation, Expr), - second_rot: (EulerRotation, Expr), - third_rot: (EulerRotation, Expr), - context: HashMap, - unit: AngleUnit, - is_ra_dec_w: bool, - ) -> Self { - let rot_order = [first_rot, second_rot, third_rot]; - Self { - base_context: context, - rot_order, - unit, - is_ra_dec_w, - } - } - - /// Specify how to compute this frame from the provided Euler angles and their time varying expressions. - /// Note that these angles define how to go from THIS frame TO the PARENT frame (e.g. Sun fixed to ICRF). - pub fn from_euler_angles( - first_rot: (EulerRotation, Expr), - second_rot: (EulerRotation, Expr), - third_rot: (EulerRotation, Expr), - context: HashMap, - unit: AngleUnit, - ) -> Self { - Self::new(first_rot, second_rot, third_rot, context, unit, false) - } - - /// A time varying Right ascension, Declination, and W frame - /// Conversion TO parent frame (e.g. Sun body to ICRF) defined as: - /// R3(-(alpha-90 deg)) * R1(delta - 90 deg) * R3(-W) - /// Where alpha is the right ascension and delta the declination - pub fn from_ra_dec_w( - alpha_right_asc: Expr, - delta_declin: Expr, - w_twist: Expr, - context: HashMap, - unit: AngleUnit, - ) -> Self { - // We initialize this backward on purpose, cf. https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/FORTRAN/req/rotation.html#Working%20with%20RA,%20Dec%20and%20Twist - Self::new( - (EulerRotation::R3(0.0), alpha_right_asc), - (EulerRotation::R1(0.0), delta_declin), - (EulerRotation::R3(0.0), w_twist), - context, - unit, - true, - ) - } -} - -impl fmt::Debug for Euler3AxisDt { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{:?}-{:?}-{:?}", - self.rot_order[0].0, self.rot_order[1].0, self.rot_order[2].0 - ) - } -} - -impl ParentRotation for Euler3AxisDt { - fn dcm_to_parent(&self, datetime: Epoch) -> Option> { - let days_d = datetime.to_tdb_days_since_j2000(); - let centuries_t = datetime.to_tdb_centuries_since_j2000(); - // Let's create a new context, add the time variables, and compute the rotation's context - // Note: this will be resolved with Chi/racr files. - let mut ctx = Context::default(); - ctx.var("d", days_d); - ctx.var("T", centuries_t); - for (var, expr_str) in &self.base_context { - let as_expr: Expr = expr_str.parse().unwrap(); - ctx.var( - var.to_owned(), - as_expr.eval_with_context(&ctx).unwrap_or_else(|_| { - error!("Could not evaluate variable `{var}` as `{expr_str}`"); - 0.0 - }), - ); - } - let mut dcm = Matrix3::identity(); - for (angle_no, (rot, expr)) in self.rot_order.iter().rev().enumerate() { - // Compute the correct angle - match expr.eval_with_context(&ctx) { - Ok(eval_angle) => { - // Convert the angle to radians if needed - let mut angle = if self.unit == AngleUnit::Degrees { - eval_angle.to_radians() - } else { - eval_angle - }; - // Apply the angle transformation if needed - if self.is_ra_dec_w { - if angle_no == 1 { - angle = std::f64::consts::FRAC_PI_2 - angle; - } else if angle_no == 2 { - angle += std::f64::consts::FRAC_PI_2; - } - } - let rot_with_angl = match rot { - EulerRotation::R1(_) => EulerRotation::R1(angle), - EulerRotation::R2(_) => EulerRotation::R2(angle), - EulerRotation::R3(_) => EulerRotation::R3(angle), - }; - dcm *= rot_with_angl.dcm(); - } - Err(e) => { - error!("{}", e); - // Stop here if something when wrong - return None; - } - } - } - Some(dcm) - } -} - -#[test] -fn test_angle_unit_deser() { - use std::str::FromStr; - assert_eq!(AngleUnit::from_str("DeGrEes").unwrap(), AngleUnit::Degrees); - assert_eq!(AngleUnit::from_str("RaDiaNs").unwrap(), AngleUnit::Radians); - assert!(AngleUnit::from_str("Gradian").is_err()); -} diff --git a/src/dynamics/drag.rs b/src/dynamics/drag.rs index ac5a2607..72d3204e 100644 --- a/src/dynamics/drag.rs +++ b/src/dynamics/drag.rs @@ -168,7 +168,8 @@ impl ForceModel for Drag { / ref_alt_m) .exp(); - //TODO(ANISE): Looks like there is a frame issue here abnd we're transforming into the original frame! + // TODO: Drag modeling will be improved in https://github.com/nyx-space/nyx/issues/317 + // The frame will be double checked in this PR as well. // let velocity_integr_frame = self.cosm.frame_chg(&osc, integration_frame).velocity(); let velocity_integr_frame = almanac .transform_to(osc_drag_frame, integration_frame, None) diff --git a/src/dynamics/guidance/mnvr.rs b/src/dynamics/guidance/mnvr.rs index 85d4945f..1a211611 100644 --- a/src/dynamics/guidance/mnvr.rs +++ b/src/dynamics/guidance/mnvr.rs @@ -252,7 +252,6 @@ impl GuidanceLaw for Mnvr { Ok(0.0) } } - // self.thrust_lvl } fn next(&self, sc: &mut Spacecraft) { diff --git a/src/dynamics/mod.rs b/src/dynamics/mod.rs index cb51174b..16ecc644 100644 --- a/src/dynamics/mod.rs +++ b/src/dynamics/mod.rs @@ -44,9 +44,6 @@ pub use self::orbital::*; /// This module allows loading gravity models from [PDS](http://pds-geosciences.wustl.edu/), [EGM2008](http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008/) and GMAT's own COF files. // pub mod gravity; -/// The drag module handles drag in a very basic fashion. Do not use for high fidelity dynamics. -// pub mod drag; - /// The spacecraft module allows for simulation of spacecraft dynamics in general, including propulsion/maneuvers. pub mod spacecraft; pub use self::spacecraft::*; @@ -61,7 +58,7 @@ pub mod deltavctrl; pub mod solarpressure; pub use self::solarpressure::*; -/// Define drag models +/// The drag module handles drag in a very basic fashion. Do not use for high fidelity dynamics. pub mod drag; pub use self::drag::*; @@ -123,7 +120,7 @@ where + Allocator, ::Size>, Owned: Copy, { - unimplemented!() + Err(DynamicsError::StateTransitionMatrixUnset) } /// Optionally performs some final changes after each successful integration of the equations of motion. diff --git a/src/dynamics/orbital.rs b/src/dynamics/orbital.rs index a10b4c30..994abd5a 100644 --- a/src/dynamics/orbital.rs +++ b/src/dynamics/orbital.rs @@ -62,17 +62,6 @@ impl OrbitalDynamics { pub fn from_model(accel_model: Arc) -> Self { Self::new(vec![accel_model]) } - - /// Add a model to the currently defined orbital dynamics - pub fn add_model(&mut self, accel_model: Arc) { - self.accel_models.push(accel_model); - } - - /// Clone these dynamics and add a model to the currently defined orbital dynamics - pub fn with_model(mut self, accel_model: Arc) -> Self { - self.add_model(accel_model); - self - } } impl fmt::Display for OrbitalDynamics { @@ -179,7 +168,6 @@ impl OrbitalDynamics { } } -/// TODO(ANISE): Switch to Builder trait /// PointMasses model pub struct PointMasses { pub celestial_objects: Vec, diff --git a/src/dynamics/solarpressure.rs b/src/dynamics/solarpressure.rs index a7642d7f..e7fffac8 100644 --- a/src/dynamics/solarpressure.rs +++ b/src/dynamics/solarpressure.rs @@ -18,7 +18,7 @@ use super::{DynamicsAlmanacSnafu, DynamicsError, DynamicsPlanetarySnafu, ForceModel}; use crate::cosmic::eclipse::EclipseLocator; -use crate::cosmic::{Frame, Spacecraft, AU, SPEED_OF_LIGHT}; +use crate::cosmic::{Frame, Spacecraft, AU, SPEED_OF_LIGHT_M_S}; use crate::linalg::{Const, Matrix3, Vector3}; use anise::almanac::Almanac; use anise::constants::frames::SUN_J2000; @@ -73,18 +73,12 @@ impl ForceModel for SolarPressure { fn eom(&self, ctx: &Spacecraft, almanac: Arc) -> Result, DynamicsError> { let osc = ctx.orbit; // Compute the position of the Sun as seen from the spacecraft - // TODO(ANISE): I think this needs to be flipped as well! let r_sun = almanac .transform_to(ctx.orbit, self.e_loc.light_source, None) .context(DynamicsAlmanacSnafu { action: "transforming state to vector seen from Sun", })? .radius_km; - // let r_sun = self - // .e_loc - // .cosm - // .frame_chg(osc, self.e_loc.light_source) - // .radius(); let r_sun_unit = r_sun / r_sun.norm(); @@ -99,7 +93,7 @@ impl ForceModel for SolarPressure { let r_sun_au = r_sun.norm() / AU; // in N/(m^2) - let flux_pressure = (k * self.phi / SPEED_OF_LIGHT) * (1.0 / r_sun_au).powi(2); + let flux_pressure = (k * self.phi / SPEED_OF_LIGHT_M_S) * (1.0 / r_sun_au).powi(2); // Note the 1e-3 is to convert the SRP from m/s^2 to km/s^2 Ok(1e-3 * ctx.srp.cr * ctx.srp.area_m2 * flux_pressure * r_sun_unit) @@ -112,7 +106,7 @@ impl ForceModel for SolarPressure { ) -> Result<(Vector3, Matrix3), DynamicsError> { let osc = ctx.orbit; - // TODO(ANISE): I think this needs to be flipped as well! + // Compute the position of the Sun as seen from the spacecraft let r_sun = almanac .transform_to(ctx.orbit, self.e_loc.light_source, None) .context(DynamicsAlmanacSnafu { @@ -120,13 +114,6 @@ impl ForceModel for SolarPressure { })? .radius_km; - // Compute the position of the Sun as seen from the spacecraft - // let r_sun = self - // .e_loc - // .cosm - // .frame_chg(osc, self.e_loc.light_source) - // .radius(); - let r_sun_d: Vector3>> = hyperspace_from_vector(&r_sun); let r_sun_unit = r_sun_d / norm(&r_sun_d); @@ -144,7 +131,8 @@ impl ForceModel for SolarPressure { let inv_r_sun_au_p2 = inv_r_sun_au.powi(2); // in N/(m^2) let flux_pressure = - OHyperdual::>::from_real(k * self.phi / SPEED_OF_LIGHT) * inv_r_sun_au_p2; + OHyperdual::>::from_real(k * self.phi / SPEED_OF_LIGHT_M_S) + * inv_r_sun_au_p2; // Note the 1e-3 is to convert the SRP from m/s^2 to km/s^2 let dual_force_scalar = diff --git a/src/dynamics/spacecraft.rs b/src/dynamics/spacecraft.rs index fbc0665e..18a401da 100644 --- a/src/dynamics/spacecraft.rs +++ b/src/dynamics/spacecraft.rs @@ -58,7 +58,7 @@ const NORM_ERR: f64 = 1e-4; #[cfg_attr(feature = "python", pyo3(module = "nyx_space.mission_design"))] pub struct SpacecraftDynamics { pub orbital_dyn: OrbitalDynamics, - // TODO(ANISE): Remove the force model generic OR find a way to serialize the name of what's loaded (BTreeMap with the name of the class to instantiate maybe?). Refer to DynamicsSerde as an example. + // TODO: https://github.com/nyx-space/nyx/issues/214 pub force_models: Vec>, pub guid_law: Option>, pub decrement_mass: bool, @@ -120,18 +120,6 @@ impl SpacecraftDynamics { me } - /// Add a model to the currently defined spacecraft dynamics - pub fn add_model(&mut self, force_model: Arc) { - self.force_models.push(force_model); - } - - /// Clone these dynamics and add a model to the currently defined orbital dynamics - pub fn with_model(self, force_model: Arc) -> Self { - let mut me = self; - me.add_model(force_model); - me - } - /// A shortcut to spacecraft.guid_law if a guidance law is defined for these dynamics pub fn guidance_achieved(&self, state: &Spacecraft) -> Result { match &self.guid_law { @@ -149,26 +137,6 @@ impl SpacecraftDynamics { decrement_mass: self.decrement_mass, } } - - /// Clone these spacecraft dynamics and update the control to the one provided. - pub fn with_guidance_law_no_decr(&self, guid_law: Arc) -> Self { - Self { - orbital_dyn: self.orbital_dyn.clone(), - guid_law: Some(guid_law), - force_models: self.force_models.clone(), - decrement_mass: false, - } - } - - /// Clone these spacecraft dynamics and remove any control model - pub fn without_guidance_law(&self) -> Self { - Self { - orbital_dyn: self.orbital_dyn.clone(), - guid_law: None, - force_models: self.force_models.clone(), - decrement_mass: self.decrement_mass, - } - } } #[cfg_attr(feature = "python", pymethods)] diff --git a/src/io/mod.rs b/src/io/mod.rs index 2d65dc1e..bbb32ff9 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -212,7 +212,6 @@ impl PartialEq for InputOutputError { } } -// TODO(ANISE): This should use the Trait thing from PyO3 pub trait ConfigRepr: Debug + Sized + Serialize + DeserializeOwned { /// Builds the configuration representation from the path to a yaml fn load

(path: P) -> Result diff --git a/src/md/trajectory/sc_traj.rs b/src/md/trajectory/sc_traj.rs index fb1540a7..27dafe2f 100644 --- a/src/md/trajectory/sc_traj.rs +++ b/src/md/trajectory/sc_traj.rs @@ -198,12 +198,13 @@ impl Traj { warn!("[line: {}] Skipping covariance in OEM parsing", lno + 1); parse = false; } else if parse { - // TODO(ANISE): Add error handling let frame = Frame::from_name( center_name.clone().unwrap().as_str(), orient_name.clone().unwrap().as_str(), ) - .unwrap(); + .map_err(|e| NyxError::CCSDS { + msg: format!("frame error `{center_name:?} {orient_name:?}`: {e}"), + })?; // Split the line into components let parts: Vec<&str> = line.split_whitespace().collect(); diff --git a/src/od/noise/gauss_markov.rs b/src/od/noise/gauss_markov.rs index 8effc208..ce48d53c 100644 --- a/src/od/noise/gauss_markov.rs +++ b/src/od/noise/gauss_markov.rs @@ -16,7 +16,7 @@ along with this program. If not, see . */ -use crate::cosmic::SPEED_OF_LIGHT_KMS; +use crate::cosmic::SPEED_OF_LIGHT_KM_S; use crate::io::watermark::pq_writer; use crate::io::{duration_from_str, duration_to_str, ConfigError, ConfigRepr}; #[cfg(feature = "python")] @@ -219,7 +219,7 @@ impl GaussMarkov { /// /// Where c is the speed of light, B is the bandwidth in Hz, and the Pr/N0 is the signal-to-noise ratio. pub fn from_pr_n0(pr_n0: f64, bandwidth_hz: f64) -> Self { - let sigma = SPEED_OF_LIGHT_KMS / (2.0 * bandwidth_hz * (pr_n0).sqrt()); + let sigma = SPEED_OF_LIGHT_KM_S / (2.0 * bandwidth_hz * (pr_n0).sqrt()); Self::white_noise(sigma) } diff --git a/tests/cosmic/bplane.rs b/tests/cosmic/bplane.rs index 8bafc883..f75b6f75 100644 --- a/tests/cosmic/bplane.rs +++ b/tests/cosmic/bplane.rs @@ -142,11 +142,10 @@ fn val_b_plane_gmat(almanac: Arc) { ]; // Iterate through the truth data - // TODO(ANISE): Make this verification tighter after switching to ANISE for data in &datum { let eme2k_state = traj.at(data.epoch).unwrap().orbit; let state = almanac.transform_to(eme2k_state, MOON_J2000, None).unwrap(); - // TODO(ANISE): The transformed state is _not_ hyperbolic indeed! Eccentricity is 0.17. + // NOTE: The transformed state is _not_ hyperbolic with de440s! Eccentricity is 0.17. // Compare with Cosm to understand why this state is no longer hyperbolic, the code looks to be identical. println!("EME2K = {}\nEME2K = {:x}", eme2k_state, eme2k_state); println!("STATE = {}\nSTATE = {:x}", state, state); diff --git a/tests/propagation/propagators.rs b/tests/propagation/propagators.rs index 2ee83e27..958f45d8 100644 --- a/tests/propagation/propagators.rs +++ b/tests/propagation/propagators.rs @@ -127,7 +127,6 @@ fn regress_leo_day_adaptive(almanac: Arc) { ); let mut prop = setup.with(init, almanac.clone()); prop.for_duration(prop_time).unwrap(); - // TODO(ANISE): This was a rel check! assert_orbit_eq_or_abs( &prop.state.orbit, &all_rslts[2], @@ -451,7 +450,6 @@ fn gmat_val_leo_day_fixed(almanac: Arc) { ); let mut prop = setup.with(init, almanac.clone()); prop.for_duration(prop_time).unwrap(); - // TODO(ANISE): This was a rel check! assert_orbit_eq_or_abs( &prop.state.orbit, &all_rslts[1],