From 766e74f4efbd6d6df79ea5ed8b034c08c4a8d01f Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 10 Dec 2024 19:35:12 -0700 Subject: [PATCH 1/8] Add MOON_ME_DE421 --- anise-py/src/constants.rs | 4 ++++ anise/src/constants.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/anise-py/src/constants.rs b/anise-py/src/constants.rs index d4deb819..1a6737ac 100644 --- a/anise-py/src/constants.rs +++ b/anise-py/src/constants.rs @@ -66,6 +66,8 @@ impl Frames { #[classattr] const MOON_ME_FRAME: Frame = MOON_ME_FRAME; #[classattr] + const MOON_ME_DE421_FRAME: Frame = MOON_ME_DE421_FRAME; + #[classattr] const MOON_PA_FRAME: Frame = MOON_PA_FRAME; #[classattr] const IAU_MOON_FRAME: Frame = IAU_MOON_FRAME; @@ -102,6 +104,8 @@ impl Orientations { #[classattr] const MOON_ME: i32 = MOON_ME; #[classattr] + const MOON_ME_DE421: i32 = MOON_ME_DE421; + #[classattr] const MOON_PA: i32 = MOON_PA; #[classattr] const ITRF93: i32 = ITRF93; diff --git a/anise/src/constants.rs b/anise/src/constants.rs index 8df960ad..d2db542a 100644 --- a/anise/src/constants.rs +++ b/anise/src/constants.rs @@ -226,6 +226,8 @@ pub mod orientations { pub const MOON_ME: NaifId = 31001; /// High fidelity Moon Principal Axes orientation frame (used for gravity field and mass concentrations), requires the Moon PA BPC kernel pub const MOON_PA: NaifId = 31000; + /// High fidelity Moon Principal Axes orientation frame of the DE421 (used for gravity field and mass concentrations), requires the Moon PA BPC kernel + pub const MOON_ME_DE421: NaifID = 31007; pub const IAU_MARS: NaifId = 499; pub const IAU_JUPITER: NaifId = 599; pub const IAU_SATURN: NaifId = 699; @@ -321,6 +323,8 @@ pub mod frames { pub const IAU_MOON_FRAME: Frame = Frame::new(MOON, IAU_MOON); /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel pub const MOON_ME_FRAME: Frame = Frame::new(MOON, MOON_ME); + /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel + pub const MOON_ME_DE421_FRAME: Frame = Frame::new(MOON, MOON_ME_DE421); /// High fidelity Moon Principal Axes body fixed frame (used for gravity field and mass concentrations), requires the Moon PA BPC kernel pub const MOON_PA_FRAME: Frame = Frame::new(MOON, MOON_PA); pub const IAU_MARS_FRAME: Frame = Frame::new(MARS, IAU_MARS); From 8a5d6688125715aea364095cd4c0b0dadafeee64 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 10 Dec 2024 23:00:45 -0700 Subject: [PATCH 2/8] Initial work on fetching EPs --- .github/workflows/rust.yml | 1 + anise/src/constants.rs | 2 +- anise/src/orientations/mod.rs | 4 +- anise/src/orientations/paths.rs | 15 ++++--- anise/src/orientations/rotate_to_parent.rs | 40 ++++++++++------- anise/src/orientations/rotations.rs | 2 + anise/tests/orientations/mod.rs | 52 +++++++++++++++++++++- data/moon_pa_de440_200625.bpc | 3 ++ 8 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 data/moon_pa_de440_200625.bpc diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2798a43b..f33edf96 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -53,6 +53,7 @@ jobs: wget -O data/variable-seg-size-hermite.bsp http://public-data.nyxspace.com/anise/ci/variable-seg-size-hermite.bsp wget -O data/earth_latest_high_prec.bpc http://public-data.nyxspace.com/anise/ci/earth_latest_high_prec-2023-09-08.bpc wget -O data/lro.bsp http://public-data.nyxspace.com/nyx/examples/lrorg_2023349_2024075_v01_LE.bsp + wget -O data/moon_pa_de440_200625.bpc http://public-data.nyxspace.com/anise/moon_pa_de440_200625.bpc - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable diff --git a/anise/src/constants.rs b/anise/src/constants.rs index d2db542a..0a2100ac 100644 --- a/anise/src/constants.rs +++ b/anise/src/constants.rs @@ -227,7 +227,7 @@ pub mod orientations { /// High fidelity Moon Principal Axes orientation frame (used for gravity field and mass concentrations), requires the Moon PA BPC kernel pub const MOON_PA: NaifId = 31000; /// High fidelity Moon Principal Axes orientation frame of the DE421 (used for gravity field and mass concentrations), requires the Moon PA BPC kernel - pub const MOON_ME_DE421: NaifID = 31007; + pub const MOON_ME_DE421: NaifId = 31007; pub const IAU_MARS: NaifId = 499; pub const IAU_JUPITER: NaifId = 599; pub const IAU_SATURN: NaifId = 699; diff --git a/anise/src/orientations/mod.rs b/anise/src/orientations/mod.rs index 186a8620..1239442e 100644 --- a/anise/src/orientations/mod.rs +++ b/anise/src/orientations/mod.rs @@ -23,7 +23,9 @@ mod rotations; #[derive(Debug, Snafu, PartialEq)] #[snafu(visibility(pub(crate)))] pub enum OrientationError { - /// Somehow you've entered code that should not be reachable, please file a bug. + #[snafu(display( + "somehow you've entered code that should not be reachable, please file a bug." + ))] Unreachable, #[snafu(display( "could not load BPC because all {max_slots} are used (modify `MAX_LOADED_BPCS` at build time)" diff --git a/anise/src/orientations/paths.rs b/anise/src/orientations/paths.rs index e3e5f242..ed66c038 100644 --- a/anise/src/orientations/paths.rs +++ b/anise/src/orientations/paths.rs @@ -99,11 +99,16 @@ impl Almanac { Ok((summary, _, _)) => summary.inertial_frame_id, Err(_) => { // Not available as a BPC, so let's see if there's planetary data for it. - let planetary_data = self - .planetary_data - .get_by_id(source.orientation_id) - .context(OrientationDataSetSnafu)?; - planetary_data.parent_id + match self.planetary_data.get_by_id(source.orientation_id) { + Ok(planetary_data) => planetary_data.parent_id, + Err(_) => { + // Finally, let's see if it's in the loaded Euler Parameters. + self.euler_param_data + .get_by_id(source.orientation_id) + .context(OrientationDataSetSnafu)? + .from + } + } } }; diff --git a/anise/src/orientations/rotate_to_parent.rs b/anise/src/orientations/rotate_to_parent.rs index 4056bdaa..04fbcfff 100644 --- a/anise/src/orientations/rotate_to_parent.rs +++ b/anise/src/orientations/rotate_to_parent.rs @@ -52,7 +52,7 @@ impl Almanac { trace!("rotate {source} wrt to {new_frame} @ {epoch:E}"); - // This should not fail because we've fetched the spk_no from above with the spk_summary_at_epoch call. + // This should not fail because we've fetched the bpc_no from above with the bpc_summary_at_epoch call. let bpc_data = self.bpc_data[bpc_no] .as_ref() .ok_or(OrientationError::Unreachable)?; @@ -103,22 +103,32 @@ impl Almanac { }) } Err(_) => { - trace!("query {source} wrt to its parent @ {epoch:E} using planetary data"); // Not available as a BPC, so let's see if there's planetary data for it. - let planetary_data = self - .planetary_data - .get_by_id(source.orientation_id) - .context(OrientationDataSetSnafu)?; + match self.planetary_data.get_by_id(source.orientation_id) { + Ok(planetary_data) => { + trace!("query {source} wrt to its parent @ {epoch:E} using planetary data"); + // Fetch the parent info + let system_data = + match self.planetary_data.get_by_id(planetary_data.parent_id) { + Ok(parent) => parent, + Err(_) => planetary_data, + }; - // Fetch the parent info - let system_data = match self.planetary_data.get_by_id(planetary_data.parent_id) { - Ok(parent) => parent, - Err(_) => planetary_data, - }; - - planetary_data - .rotation_to_parent(epoch, &system_data) - .context(OrientationPhysicsSnafu) + planetary_data + .rotation_to_parent(epoch, &system_data) + .context(OrientationPhysicsSnafu) + } + Err(_) => { + trace!("query {source} wrt to its parent @ {epoch:E} using Euler parameter data"); + // Finally, let's see if it's in the loaded Euler Parameters. + // We can call `into` because EPs can be converted directly into DCMs. + Ok(self + .euler_param_data + .get_by_id(source.orientation_id) + .context(OrientationDataSetSnafu)? + .into()) + } + } } } } diff --git a/anise/src/orientations/rotations.rs b/anise/src/orientations/rotations.rs index a7fa03a4..29326a6a 100644 --- a/anise/src/orientations/rotations.rs +++ b/anise/src/orientations/rotations.rs @@ -88,6 +88,8 @@ impl Almanac { } else if dcm_bwrd.to == cur_dcm.to { dcm_bwrd = (dcm_bwrd.transpose() * cur_dcm).context(OrientationPhysicsSnafu)?; } else { + dbg!(dcm_bwrd); + dbg!(dcm_fwrd); return Err(OrientationError::Unreachable); } diff --git a/anise/tests/orientations/mod.rs b/anise/tests/orientations/mod.rs index 1ffb9f1d..eaeb8497 100644 --- a/anise/tests/orientations/mod.rs +++ b/anise/tests/orientations/mod.rs @@ -2,8 +2,11 @@ use std::path::PathBuf; use anise::constants::frames::{ EARTH_ITRF93, EME2000, IAU_JUPITER_FRAME, IAU_MOON_FRAME, JUPITER_BARYCENTER_J2000, MOON_J2000, + MOON_ME_DE421_FRAME, MOON_PA_FRAME, +}; +use anise::constants::orientations::{ + ECLIPJ2000, IAU_JUPITER, IAU_MOON, ITRF93, J2000, MOON_ME_DE421, MOON_PA, }; -use anise::constants::orientations::{ECLIPJ2000, IAU_JUPITER, IAU_MOON, ITRF93, J2000}; use anise::math::rotation::DCM; use anise::math::Matrix3; use anise::naif::kpl::parser::convert_tpc; @@ -315,3 +318,50 @@ fn regression_test_issue_112_test_iau_jupiter() { dcm.rot_mat - spice_dcm.rot_mat ); } + +#[test] +fn regression_test_issue_357_test_moon_me_j2k() { + use core::str::FromStr; + + let almanac = Almanac::new("../data/pck11.pca") + .unwrap() + .load("../data/moon_fk.epa") + .unwrap() + .load("../data/moon_pa_de440_200625.bpc") + .unwrap(); + + let epoch = Epoch::from_str("2024-01-01 22:28:39").unwrap(); + + let dcm = almanac + .rotate(MOON_PA_FRAME, MOON_ME_DE421_FRAME, epoch) + .unwrap(); + + let spice_dcm = DCM { + from: MOON_PA, + to: MOON_ME_DE421, + rot_mat: Matrix3::new( + 9.99999873e-01, + 3.29286000e-04, + -3.80869119e-04, + -3.29285422e-04, + 9.99999946e-01, + 1.57985579e-06, + 3.80869619e-04, + -1.45444094e-06, + 9.99999927e-01, + ), + rot_mat_dt: None, + }; + + assert_eq!(dcm.to, IAU_JUPITER); + assert_eq!(dcm.from, J2000); + + assert!( + (dcm.rot_mat - spice_dcm.rot_mat).norm() < 1e-9, + "dcm error! got: {}want:{}err = {:.3e}: {:.3e}", + dcm.rot_mat, + spice_dcm.rot_mat, + (dcm.rot_mat - spice_dcm.rot_mat).norm(), + dcm.rot_mat - spice_dcm.rot_mat + ); +} diff --git a/data/moon_pa_de440_200625.bpc b/data/moon_pa_de440_200625.bpc new file mode 100644 index 00000000..ed2f32ac --- /dev/null +++ b/data/moon_pa_de440_200625.bpc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60cd55aa401ea2ea97360636f567554bfe4e37bb829f901b4460a455dfaf783f +size 12863488 From a5bd65dee44f4e840f9c79626a5fc6215749e7c1 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 17 Dec 2024 23:15:44 -0700 Subject: [PATCH 3/8] Build the moon_fk_de440 kernel and add a Moon PA to J2000 test for it. --- README.md | 6 + anise/src/constants.rs | 14 +- anise/src/naif/kpl/fk.rs | 38 +- anise/src/orientations/paths.rs | 25 +- anise/src/orientations/rotate_to_parent.rs | 6 +- anise/src/orientations/rotations.rs | 2 - anise/tests/orientations/mod.rs | 68 ++- data/moon_de440_220930.txt | 540 +++++++++++++++++++++ data/moon_fk.epa | Bin 232 -> 232 bytes 9 files changed, 666 insertions(+), 33 deletions(-) create mode 100644 data/moon_de440_220930.txt diff --git a/README.md b/README.md index 29b5a45c..5c32e5ce 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,12 @@ For convenience, Nyx Space provides a few important SPICE files on a public buck You may load any of these using the `load()` shortcut that will determine the file type upon loading, e.g. `let almanac = Almanac::new("pck08.pca").unwrap();` or in Python `almanac = Almanac("pck08.pca")`. To automatically download remote assets, from the Nyx Cloud or elsewhere, use the MetaAlmanac: `almanac = MetaAlmanac("ci_config.dhall").process()` in Python. +### Moon frames + +Astrodynamicists use three main body fixed frames at the Moon, all suitable for computing latitude and longitude that represent fixed points on the surface of the Moon. The IAU Moon frame is a low-fidelity body-fixed frame. The Moon Principal Axes frames, Moon PA, is used to represent the mass concentrations of the Moon, and therefore is the frame to use for gravity fields defined as spherical harmonics at the Moon. Finally, the Moon Mean Earth frame, Moon ME, is the cartographic frame: images of the Moon centered on a latitude and longitude are almost always provided in the Moon ME frame. + +As per the [`moon_de440_220930.txt`](./data/moon_de440_220930.txt) documentation, you should use the provided `moon_fk_de440.epa` file with the `moon_pa_de440_200625.bpc` and `de440.bsp` (or `de440s.bsp`). + ## Contributing Contributions to ANISE are welcome! Whether it's in the form of feature requests, bug reports, code contributions, or documentation improvements, every bit of help is greatly appreciated. diff --git a/anise/src/constants.rs b/anise/src/constants.rs index 0a2100ac..d9f077aa 100644 --- a/anise/src/constants.rs +++ b/anise/src/constants.rs @@ -226,8 +226,14 @@ pub mod orientations { pub const MOON_ME: NaifId = 31001; /// High fidelity Moon Principal Axes orientation frame (used for gravity field and mass concentrations), requires the Moon PA BPC kernel pub const MOON_PA: NaifId = 31000; - /// High fidelity Moon Principal Axes orientation frame of the DE421 (used for gravity field and mass concentrations), requires the Moon PA BPC kernel + /// High fidelity Moon Mean Earth orientation frame of the DE421 (used for cartography), requires the Moon PA BPC kernel pub const MOON_ME_DE421: NaifId = 31007; + /// High fidelity Moon Mean Earth orientation frame of the DE440 (used for cartography), requires the Moon PA BPC kernel moon_pa_de440_200625.bpc. + pub const MOON_ME_DE440_ME421: NaifId = 31009; + /// High fidelity Moon Principal Axes orientation frame of the DE421 (used for gravity field and mass concentrations), requires the Moon PA BPC kernel + pub const MOON_PA_DE421: NaifId = 31008; + /// High fidelity Moon Principal Axes orientation frame of the DE440 (used for gravity field and mass concentrations), requires the Moon PA BPC kernel moon_pa_de440_200625. Note that the ID is the same as the MOON_PA_DE421. + pub const MOON_PA_DE440: NaifId = 31008; pub const IAU_MARS: NaifId = 499; pub const IAU_JUPITER: NaifId = 599; pub const IAU_SATURN: NaifId = 699; @@ -325,8 +331,14 @@ pub mod frames { pub const MOON_ME_FRAME: Frame = Frame::new(MOON, MOON_ME); /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel pub const MOON_ME_DE421_FRAME: Frame = Frame::new(MOON, MOON_ME_DE421); + /// High fidelity Moon Mean Earth equator body fixed frame of the DE440 (used for cartography), requires the Moon PA BPC kernel moon_pa_de440_200625.bpc. + pub const MOON_ME_DE440_ME421_FRAME: Frame = Frame::new(MOON, MOON_ME_DE440_ME421); /// High fidelity Moon Principal Axes body fixed frame (used for gravity field and mass concentrations), requires the Moon PA BPC kernel pub const MOON_PA_FRAME: Frame = Frame::new(MOON, MOON_PA); + /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel + pub const MOON_PA_DE421_FRAME: Frame = Frame::new(MOON, MOON_PA_DE421); + /// High fidelity Moon Mean Earth equator body fixed frame (used for cartography), requires the Moon PA BPC kernel moon_pa_de440_200625. Note that the ID is the same as the MOON_PA_DE421. + pub const MOON_PA_DE440_FRAME: Frame = Frame::new(MOON, MOON_PA_DE440); pub const IAU_MARS_FRAME: Frame = Frame::new(MARS, IAU_MARS); pub const IAU_JUPITER_FRAME: Frame = Frame::new(JUPITER, IAU_JUPITER); pub const IAU_SATURN_FRAME: Frame = Frame::new(SATURN, IAU_SATURN); diff --git a/anise/src/naif/kpl/fk.rs b/anise/src/naif/kpl/fk.rs index 20307082..a8943fb2 100644 --- a/anise/src/naif/kpl/fk.rs +++ b/anise/src/naif/kpl/fk.rs @@ -78,7 +78,10 @@ impl KPLItem for FKItem { #[cfg(test)] mod fk_ut { - use crate::naif::kpl::parser::convert_fk; + use crate::{ + constants::orientations::{MOON_ME_DE421, MOON_ME_DE440_ME421}, + naif::kpl::parser::convert_fk, + }; use super::{FKItem, KPLValue, Parameter}; @@ -204,6 +207,8 @@ mod fk_ut { // Check that we've correctly set the names. let moon_me = dataset.get_by_name("MOON_ME_DE421").unwrap(); + let moon_me_by_id = dataset.get_by_id(MOON_ME_DE421).unwrap(); + assert_eq!(moon_me_by_id, moon_me); // From the file: // TKFRAME_31007_ANGLES = (67.92 78.56 0.30 ) // TKFRAME_31007_AXES = (3, 2, 1 ) @@ -217,4 +222,35 @@ mod fk_ut { .save_as(&PathBuf::from_str("../data/moon_fk.epa").unwrap(), true) .unwrap(); } + + #[test] + fn build_de440_moon_fk() { + use std::path::PathBuf; + use std::str::FromStr; + + use crate::math::rotation::{r1, r2, r3, DCM}; + let dataset = convert_fk("../data/moon_de440_220930.txt", false).unwrap(); + + assert_eq!(dataset.len(), 3, "expected three items"); + + // Check that we've correctly set the names. + let moon_me = dataset.get_by_name("MOON_ME_DE440_ME421").unwrap(); + let moon_me_by_id = dataset.get_by_id(MOON_ME_DE440_ME421).unwrap(); + assert_eq!(moon_me_by_id, moon_me); + // From the file: + // TKFRAME_31009_ANGLES = ( 67.8526 78.6944 0.2785 ) + // TKFRAME_31009_AXES = (3, 2, 1 ) + // These angles are in arcseconds. + let expected = r3((67.8526 / 3600.0_f64).to_radians()) + * r2((78.6944 / 3600.0_f64).to_radians()) + * r1((0.2785 / 3600.0_f64).to_radians()); + assert!((DCM::from(moon_me).rot_mat - expected).norm() < 1e-10); + println!("{}", dataset.crc32()); // 879707574 + dataset + .save_as( + &PathBuf::from_str("../data/moon_fk_de440.epa").unwrap(), + true, + ) + .unwrap(); + } } diff --git a/anise/src/orientations/paths.rs b/anise/src/orientations/paths.rs index ed66c038..e90919c2 100644 --- a/anise/src/orientations/paths.rs +++ b/anise/src/orientations/paths.rs @@ -131,17 +131,26 @@ impl Almanac { inertial_frame_id = match self.bpc_summary_at_epoch(inertial_frame_id, epoch) { Ok((summary, _, _)) => summary.inertial_frame_id, Err(_) => { - // Not available as a BPC, so let's see if there's planetary data for it. - let planetary_data = self - .planetary_data - .get_by_id(inertial_frame_id) - .context(OrientationDataSetSnafu)?; - planetary_data.parent_id + match self.planetary_data.get_by_id(source.orientation_id) { + Ok(planetary_data) => planetary_data.parent_id, + Err(_) => { + // Finally, let's see if it's in the loaded Euler Parameters. + self.euler_param_data + .get_by_id(source.orientation_id) + .context(OrientationDataSetSnafu)? + .from + } + } + // // Not available as a BPC, so let's see if there's planetary data for it. + // let planetary_data = self + // .planetary_data + // .get_by_id(inertial_frame_id) + // .context(OrientationDataSetSnafu)?; + // planetary_data.parent_id } }; + dbg!(inertial_frame_id); - // let summary = self.bpc_summary_at_epoch(inertial_frame_id, epoch)?.0; - // inertial_frame_id = summary.inertial_frame_id; of_path[of_path_len] = Some(inertial_frame_id); of_path_len += 1; if inertial_frame_id == common_center { diff --git a/anise/src/orientations/rotate_to_parent.rs b/anise/src/orientations/rotate_to_parent.rs index 04fbcfff..4df29fa6 100644 --- a/anise/src/orientations/rotate_to_parent.rs +++ b/anise/src/orientations/rotate_to_parent.rs @@ -106,7 +106,9 @@ impl Almanac { // Not available as a BPC, so let's see if there's planetary data for it. match self.planetary_data.get_by_id(source.orientation_id) { Ok(planetary_data) => { - trace!("query {source} wrt to its parent @ {epoch:E} using planetary data"); + println!( + "query {source} wrt to its parent @ {epoch:E} using planetary data" + ); // Fetch the parent info let system_data = match self.planetary_data.get_by_id(planetary_data.parent_id) { @@ -119,7 +121,7 @@ impl Almanac { .context(OrientationPhysicsSnafu) } Err(_) => { - trace!("query {source} wrt to its parent @ {epoch:E} using Euler parameter data"); + println!("query {source} wrt to its parent @ {epoch:E} using Euler parameter data"); // Finally, let's see if it's in the loaded Euler Parameters. // We can call `into` because EPs can be converted directly into DCMs. Ok(self diff --git a/anise/src/orientations/rotations.rs b/anise/src/orientations/rotations.rs index 29326a6a..a7fa03a4 100644 --- a/anise/src/orientations/rotations.rs +++ b/anise/src/orientations/rotations.rs @@ -88,8 +88,6 @@ impl Almanac { } else if dcm_bwrd.to == cur_dcm.to { dcm_bwrd = (dcm_bwrd.transpose() * cur_dcm).context(OrientationPhysicsSnafu)?; } else { - dbg!(dcm_bwrd); - dbg!(dcm_fwrd); return Err(OrientationError::Unreachable); } diff --git a/anise/tests/orientations/mod.rs b/anise/tests/orientations/mod.rs index eaeb8497..f5a2f566 100644 --- a/anise/tests/orientations/mod.rs +++ b/anise/tests/orientations/mod.rs @@ -2,10 +2,10 @@ use std::path::PathBuf; use anise::constants::frames::{ EARTH_ITRF93, EME2000, IAU_JUPITER_FRAME, IAU_MOON_FRAME, JUPITER_BARYCENTER_J2000, MOON_J2000, - MOON_ME_DE421_FRAME, MOON_PA_FRAME, + MOON_PA_DE421_FRAME, MOON_PA_DE440_FRAME, MOON_PA_FRAME, }; use anise::constants::orientations::{ - ECLIPJ2000, IAU_JUPITER, IAU_MOON, ITRF93, J2000, MOON_ME_DE421, MOON_PA, + ECLIPJ2000, IAU_JUPITER, IAU_MOON, ITRF93, J2000, MOON_PA, MOON_PA_DE421, MOON_PA_DE440, }; use anise::math::rotation::DCM; use anise::math::Matrix3; @@ -325,37 +325,67 @@ fn regression_test_issue_357_test_moon_me_j2k() { let almanac = Almanac::new("../data/pck11.pca") .unwrap() - .load("../data/moon_fk.epa") + .load("../data/moon_fk_de440.epa") .unwrap() .load("../data/moon_pa_de440_200625.bpc") .unwrap(); + // STATUS: + // The data in the Moon FK is incorrect because it relates the 31007 frame to 301. + // But the text file relates it to the Moon PA frame using its TKFRAME definition. + // At the very least, this will require a change in the EPA file. + let epoch = Epoch::from_str("2024-01-01 22:28:39").unwrap(); let dcm = almanac - .rotate(MOON_PA_FRAME, MOON_ME_DE421_FRAME, epoch) + .rotate(MOON_PA_DE440_FRAME, MOON_J2000, epoch) .unwrap(); + /* + In [10]: sp.sxform("MOON_PA_DE440", "J2000", my_et) + Out[10]: + array([[ 9.78289320e-01, 2.07027066e-01, -9.47625902e-03, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], + [-1.95463789e-01, 9.06520407e-01, -3.74185328e-01, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], + [-6.88760685e-02, 3.67913775e-01, 9.27305527e-01, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], + [ 5.51091888e-07, -2.60415126e-06, -2.62517851e-10, + 9.78289320e-01, 2.07027066e-01, -9.47625902e-03], + [ 2.41301211e-06, 5.20281183e-07, -2.93451776e-11, + -1.95463789e-01, 9.06520407e-01, -3.74185328e-01], + [ 9.79597415e-07, 1.83424192e-07, -1.45240394e-11, + -6.88760685e-02, 3.67913775e-01, 9.27305527e-01]]) + + */ + let spice_dcm = DCM { - from: MOON_PA, - to: MOON_ME_DE421, + from: MOON_PA_DE440, + to: J2000, rot_mat: Matrix3::new( - 9.99999873e-01, - 3.29286000e-04, - -3.80869119e-04, - -3.29285422e-04, - 9.99999946e-01, - 1.57985579e-06, - 3.80869619e-04, - -1.45444094e-06, - 9.99999927e-01, + 9.78289320e-01, + 2.07027066e-01, + -9.47625902e-03, + -1.95463789e-01, + 9.06520407e-01, + -3.74185328e-01, + -6.88760685e-02, + 3.67913775e-01, + 9.27305527e-01, ), - rot_mat_dt: None, + rot_mat_dt: Some(Matrix3::new( + 5.51091888e-07, + -2.60415126e-06, + -2.62517851e-10, + 2.41301211e-06, + 5.20281183e-07, + -2.93451776e-11, + 9.79597415e-07, + 1.83424192e-07, + -1.45240394e-11, + )), }; - assert_eq!(dcm.to, IAU_JUPITER); - assert_eq!(dcm.from, J2000); - assert!( (dcm.rot_mat - spice_dcm.rot_mat).norm() < 1e-9, "dcm error! got: {}want:{}err = {:.3e}: {:.3e}", diff --git a/data/moon_de440_220930.txt b/data/moon_de440_220930.txt new file mode 100644 index 00000000..dc86947e --- /dev/null +++ b/data/moon_de440_220930.txt @@ -0,0 +1,540 @@ +KPL/FK + + SPICE Lunar Reference Frame Specification Kernel + ===================================================================== + + Original file name: moon_de440_220930.tf + Creation date: 2022 September 30 15:05 + Created by: Nat Bachman (NAIF/JPL) + + Version description: + + This frame kernel contains lunar frame specifications compatible + with the current lunar binary PCK file + + moon_pa_de440_200625.bpc + + The above PCK contains lunar orientation data from the DE440 JPL + Planetary Ephemeris. + + This frame kernel extends the previous version + + moon_pa_de440_200625.tf + + by adding specifications of the alias frames + + MOON_PA + MOON_ME + + and by upgrading the documentation to include discussion of + frame association kernels and to present an example program + that uses this kernel. + + The description of the frame + + MOON_ME_DE440_ME421 + + has been updated as well. + + + Frames Specified by this Kernel + ===================================================================== + + Frame Name Relative to Type Frame ID + ------------------------- ----------------- ----- -------- + MOON_PA MOON_PA_DE440 FIXED 31010 + MOON_ME MOON_ME_DE440_ME421 FIXED 31011 + MOON_PA_DE440 ICRF/J2000 PCK 31008 + MOON_ME_DE440_ME421 MOON_PA_DE440 FIXED 31009 + + + Introduction + ===================================================================== + + This kernel specifies lunar body-fixed reference frames for use by + SPICE-based application software. These reference frames are + associated with high-accuracy lunar orientation data provided by the + JPL Solar System Dynamics Group's planetary ephemerides (both + trajectory and lunar orientation data are stored in these ephemeris + files). These ephemerides have names of the form DEnnn (DE stands + for "developmental ephemeris"). + + The frames specified by this kernel are realizations of two different + lunar reference systems: + + Principal Axes (PA) system + -------------------------- + The axes of this system are defined by the principal axes of the + Moon. Due to the nature of the Moon's orbit and rotation, the Z + axis of this system does not coincide with the Moon's mean spin + axis, nor does the X axis coincide with the mean direction to the + center of the Earth (in contrast with the ME system defined + below). + + Lunar principal axes frames realizing the lunar PA system and + specified by this kernel are associated with JPL planetary + ephemerides. Each new JPL planetary ephemeris can (but does not + necessarily) define a new realization of the lunar principal axes + system. Coordinates of lunar surface features expressed in lunar + PA frames can change slightly from one lunar ephemeris version to + the next. + + + Mean Earth/Polar Axis (ME) system + --------------------------------- + The Lunar mean Earth/polar axis system is a lunar body-fixed + reference system used in the IAU/IAG Working Group Report [2] to + describe the orientation of the Moon relative to the ICRF frame. + The +Z axis of this system is aligned with the north mean lunar + rotation axis, while the prime meridian contains the mean Earth + direction. + + This system is also sometimes called the "mean Earth/mean + rotation axis" system or "mean Earth" system. + + The mean directions used to define the axes of a mean Earth/polar + axis reference frame realizing the lunar ME system and specified + by this kernel are associated with a given JPL planetary + ephemeris version. The rotation between the mean Earth frame for + a given ephemeris version and the associated principal axes frame + is given by a constant matrix (see [1]). + + For the current JPL planetary ephemeris DE440, this kernel includes + specifications of the corresponding principal axes frame and a version of + a mean Earth/polar axis frame that is closely aligned with the + mean Earth/polar axis frame of DE421. The names of these frames are + + MOON_PA_DE440 + + and + + MOON_ME_DE440_ME421 + + The rotation from the former to the latter frame is described in [1] + as the rotation "from the DE440 PA frame to DE421 MER" (mean Earth/mean + rotation)" reference frame. + + For each of the two reference systems, there is a corresponding + "generic" frame specification: these generic frames are simply + aliases for the PA and ME frames associated with the latest DE. The + generic frame names are + + MOON_PA + MOON_ME + + These generic frame names are provided to enable SPICE-based + applications to refer to the latest DE-based (or other) lunar + rotation data without requiring code modifications as new kernels + become available. SPICE users may, if they wish, modify this kernel + to assign these frame aliases to other frames than those selected + here, for example, older DE-based frames. NAIF recommends that, if + this frame kernel is modified, the name of this file also be changed + to avoid confusion. + + + Comparison of DE440 and DE421 Lunar Reference Frames + ==================================================== + + Differences in the orientation of the frames + + MOON_ME_DE421 + MOON_ME_DE440_ME421 + + were measured as follows: + + For the time range + + 1900 JAN 01 00:00:00.000 TDB (-3155716800.0000 TDB seconds) + to 2051 JAN 01 00:00:00.000 TDB ( 1609416000.0000 TDB seconds) + + the maximum rotation angle, taken over ~1M samples, was + + 5.8204420784597E-07 radians + + This corresponds to a displacement of slightly over 1 m on a great + circle. + + For the time range + + 2000 JAN 01 00:00:00.000 TDB (-43200.000000000 TDB seconds) + to 2040 JAN 01 00:00:00.000 TDB ( 1262260800.0000 TDB seconds) + + the maximum rotation angle, taken over ~1M samples, was + + 3.0709840342771E-07 radians + + This corresponds to a displacement of approximately 53.4 cm on a + great circle on the Moon's surface. + + + Comparison of DE440 PA and ME frames + ==================================== + + The rotation between the mean Earth frame for a given DE and the + associated principal axes frame for the same DE is given by a + constant matrix (see [1]). For DE440, the rotation angle between + the MOON_ME and MOON_PA frames is approximately 0.02886 degrees; + this is equivalent to approximately 875 m when expressed as a + displacement along a great circle on the Moon's surface. + + + Comparison of DE440 and 2009 IAU/IAG report-based ME frame + ========================================================== + + Within the SPICE system, a lunar ME frame specified by the + rotational elements from a IAU/IAG Working Group report such as [2] + is given the name IAU_MOON; the data defining this frame are provided + in a generic text PCK. Note that these data are provided for + backward compatibility; the Working Group does not publish these + data in later versions of the report. + + The orientation of the lunar ME frame obtained by applying the + DE-based PA-to-ME rotation described above to the DE-based lunar + libration data does not agree closely with the lunar ME frame + orientation given by the rotational elements from the IAU/IAG + Working Group report (that is, the IAU_MOON frame). The difference + is due to truncation of the libration series used in the report's + formula for lunar orientation (see [1]). + + In the case of DE440, for the time period 2000-2040, the + time-dependent difference of these ME frame implementations has an + amplitude of approximately 0.0051 degrees, which is equivalent to + approximately 155 m, measured along a great circle on the Moon's + surface, while the average value is approximately 0.00239 degrees, + or 72 m. + + + Regarding Use of the ICRF in SPICE + ================================== + + The IERS Celestial Reference Frame (ICRF) is offset from the J2000 + reference frame (equivalent to EME 2000) by a small rotation: the + J2000 pole offset magnitude is about 18 milliarcseconds (mas) and + the equinox offset magnitude is approximately 78 milliarcseconds + (see [3]). + + Certain SPICE data products use the frame label "J2000" for data + that actually are referenced to the ICRF. This is the case for SPK + files containing JPL version DE4nn planetary ephemerides, for + orientation data from generic text PCKs, and for binary PCKs, + including binary lunar PCKs used in conjunction with this lunar + frame kernel. + + Consequently, when SPICE computes the rotation between the "J2000" + frame and either of the lunar PA or ME frames, what's computed is + actually the rotation between the ICRF and the respective lunar + frame. + + Similarly, when SPICE is used to compute the state given by a JPL DE + planetary ephemeris SPK file of one ephemeris object relative to + another (for example, the state of the Moon with respect to the + Earth), expressed relative to the frame "J2000," the state is + actually expressed relative to the ICRF. + + Because SPICE is already using the ICRF, users normally need not + use the J2000-to-ICRF transformation to adjust results computed + with SPICE. + + + Lunar body-fixed frame associations + ===================================================================== + + By default, the SPICE system considers the body-fixed reference + frame associated with the Moon to be the one named IAU_MOON. This + body-frame association affects the outputs of the SPICE frame system + routines + + CIDFRM + CNMFRM + + and of the SPICE time conversion and geometry routines + + ET2LST + ILLUM (deprecated; superseded by ILUMIN, ILLUMF, ILLUMG) + SRFXPT (deprecated; superseded by SINCPT) + SUBPT (deprecated; superseded by SUBPNT) + SUBSOL (deprecated; superseded by SUBSLR) + + Also, any code that calls these routines to obtain results involving + lunar body-fixed frames is affected. Within SPICE, the only + higher-level subsystem that is affected is the dynamic frame + subsystem; it currently uses SUBPT. + + NAIF provides "frame association" kernels that simplify changing the + body-fixed frame associated with the Moon. Using FURNSH to load + either of the kernels named below changes the Moon's body-fixed + frame from its current value, which initially is IAU_MOON, to that + shown in the right-hand column: + + Kernel name Lunar body-fixed frame + ----------- ---------------------- + moon_assoc_me.tf MOON_ME + moon_assoc_pa.tf MOON_PA + + For further information see the in-line comments in the association + kernels themselves. Also see the Frames Required Reading section + titled "Connecting an Object to its Body-fixed Frame." + + In the N0062 SPICE Toolkit, the routines + + ILLUM + SRFXPT + SUBPT + SUBSOL + + were superseded, respectively, by the routines + + ILLUMF, ILLUMG, ILUMIN + SINCPT + SUBPNT + SUBSLR + + The newer routines don't require frame association kernels: the name + of the target body's body-fixed reference frame is an input argument + to these routines. + + + Using this Kernel + ===================================================================== + + In order for a SPICE-based application to use reference frames + specified by this kernel, the application must load both this kernel + and a binary lunar PCK containing lunar orientation data for the + time of interest. Normally the kernels need be loaded only once + during program initialization. + + SPICE users may find it convenient to use a meta-kernel (also called + a "FURNSH kernel") to list the kernels to be loaded. Below, we show + an example of such a meta-kernel, as well as the source code of a + small Fortran program that uses lunar body fixed frames. The + program's output is included as well. + + The kernel names shown here are simply used as examples; users must + select the kernels appropriate for their applications. + + Numeric results shown below may differ very slightly from those + obtained on users' computer systems. + + + Meta-kernel + ----------- + + KPL/MK + + File name: ex1.tm + + Example meta-kernel showing use of + + - binary lunar PCK + - generic lunar frame kernel (FK) + - leapseconds kernel (LSK) + - planetary SPK + + 30-SEP-2022 (NJB) + + Note: to actually use this kernel, replace the @ characters + below with backslashes (\). The backslash character cannot be + used here, within the comments of this frame kernel, because the + begindata and begintext strings would be interpreted as + directives bracketing actual load commands. + + This meta-kernel assumes that the referenced kernels exist + in the user's current working directory. + + @begindata + + KERNELS_TO_LOAD = ( 'moon_pa_de440_200625.bpc' + 'moon_de440_220930.tf' + 'leapseconds.ker' + 'de440.bsp' ) + + @begintext + + + Example program + --------------- + + PROGRAM EX1 + IMPLICIT NONE + + INTEGER FILSIZ + PARAMETER ( FILSIZ = 255 ) + + CHARACTER*(FILSIZ) META + + DOUBLE PRECISION ET + DOUBLE PRECISION LT + DOUBLE PRECISION STME ( 6 ) + DOUBLE PRECISION STPA ( 6 ) + + C + C Prompt user for meta-kernel name. + C + CALL PROMPT ( 'Enter name of meta-kernel > ', META ) + + C + C Load lunar PCK, generic lunar frame kernel, + C leapseconds kernel, and planetary ephemeris + C via metakernel. + C + CALL FURNSH ( META ) + + C + C Convert a time of interest from UTC to ET. + C + CALL STR2ET ( '2022 SEP 30 TDB', ET ) + + WRITE (*,*) 'ET (sec past J2000 TDB): ', ET + WRITE (*,*) ' State of Earth relative to Moon' + + C + C Find the geometric state of the Earth relative to the + C Moon at ET, expressed relative to the ME frame. + C + CALL SPKEZR ( 'Earth', ET, 'MOON_ME', + . 'NONE', 'Moon', STME, LT ) + + WRITE (*,*) ' In MOON_ME frame:' + WRITE (*, '(3F12.3, 3F13.8)') STME + + C + C Find the geometric state of the Earth relative to the + C Moon at ET, expressed relative to the PA frame. + C + CALL SPKEZR ( 'Earth', ET, 'MOON_PA', + . 'NONE', 'Moon', STPA, LT ) + + WRITE (*,*) ' In MOON_PA frame:' + WRITE (*, '(3F12.3, 3F13.8)') STPA + + END + + + Program output + -------------- + + Enter name of meta-kernel > ex1.tm + ET (sec past J2000 TDB): 717768000.00000000 + State of Earth relative to Moon + In MOON_ME frame: + 374008.654 -23435.970 10141.334 -0.02638134 0.04532757 0.11894164 + In MOON_PA frame: + 373997.028 -23558.987 10284.057 -0.02641181 0.04533642 0.11893151 + + + References + ===================================================================== + + [1] Ryan S. Park, et al, "The JPL Planetary and Lunar Ephemerides + DE440 and DE441," The Astronomical Journal, Volume 161, Number 3. + 2021 AJ 161 105. + + URL: + + https://iopscience.iop.org/article/10.3847/1538-3881/abd414/meta + + [2] Archinal, B.A., A'Hearn, M.F., Bowell, E., Conrad, A., + Consolmagno, G.J., Courtin, R., Fukushima, T., + Hestroffer, D., Hilton, J.L., Krasinsky, G.A., + Neumann, G., Oberst, J., Seidelmann, P.K., Stooke, P., + Tholen, D.J., Thomas, P.C., and Williams, I.P. + "Report of the IAU Working Group on Cartographic Coordinates + and Rotational Elements: 2009." + + [3] Roncoli, R. (2005). "Lunar Constants and Models Document," + JPL D-32296. + + + Frame Specifications + ===================================================================== + + MOON_PA is the name of the generic lunar principal axes (PA) reference + frame. This frame is an alias for the principal axes frame defined + by the latest version of the JPL Solar System Dynamics Group's + planetary ephemeris. + + In this instance of the lunar reference frames kernel, MOON_PA is an + alias for the lunar principal axes frame associated with the + planetary ephemeris DE440. + + \begindata + + FRAME_MOON_PA = 31010 + FRAME_31010_NAME = 'MOON_PA' + FRAME_31010_CLASS = 4 + FRAME_31010_CLASS_ID = 31010 + FRAME_31010_CENTER = 301 + + TKFRAME_31010_SPEC = 'MATRIX' + TKFRAME_31010_RELATIVE = 'MOON_PA_DE440' + TKFRAME_31010_MATRIX = ( 1 0 0 + 0 1 0 + 0 0 1 ) + + \begintext + + MOON_ME is the name of the generic lunar mean Earth/polar axis (ME) + reference frame. + + In this instance of the lunar reference frames kernel, MOON_ME is an + alias for the frame MOON_ME_DE440_ME421, which is closely aligned + with the lunar mean Earth/polar axis frame associated with the + planetary ephemeris DE421. + + \begindata + + FRAME_MOON_ME = 31011 + FRAME_31011_NAME = 'MOON_ME' + FRAME_31011_CLASS = 4 + FRAME_31011_CLASS_ID = 31011 + FRAME_31011_CENTER = 301 + + TKFRAME_31011_SPEC = 'MATRIX' + TKFRAME_31011_RELATIVE = 'MOON_ME_DE440_ME421' + TKFRAME_31011_MATRIX = ( 1 0 0 + 0 1 0 + 0 0 1 ) + + + \begintext + + MOON_PA_DE440 is the name of the lunar principal axes reference + frame defined by JPL's DE440 planetary ephemeris. + + \begindata + + FRAME_MOON_PA_DE440 = 31008 + FRAME_31008_NAME = 'MOON_PA_DE440' + FRAME_31008_CLASS = 2 + FRAME_31008_CLASS_ID = 31008 + FRAME_31008_CENTER = 301 + + \begintext + + MOON_ME_DE440_ME421 is the name of a reference frame defined by a + constant rotational offset from the DE440 principal axes frame, + such that the frame is closely aligned with the DE421 lunar mean + Earth/polar axis frame. + + Rotation angles are from [1]. The angles and axis sequence shown + below represent the inverse of the cited rotation; hence the + angles are negated and the sequence is reversed. + + \begindata + + FRAME_MOON_ME_DE440_ME421 = 31009 + FRAME_31009_NAME = 'MOON_ME_DE440_ME421' + FRAME_31009_CLASS = 4 + FRAME_31009_CLASS_ID = 31009 + FRAME_31009_CENTER = 301 + + TKFRAME_31009_SPEC = 'ANGLES' + TKFRAME_31009_RELATIVE = 'MOON_PA_DE440' + TKFRAME_31009_ANGLES = ( 67.8526 78.6944 0.2785 ) + TKFRAME_31009_AXES = ( 3, 2, 1 ) + TKFRAME_31009_UNITS = 'ARCSECONDS' + + \begintext + + ===================================================================== + End of kernel diff --git a/data/moon_fk.epa b/data/moon_fk.epa index 7b668518ac366874beb2f18f927a3d87cb24dfc9..53588fcfba3e724032d6481af365b0fbba517de9 100644 GIT binary patch delta 146 zcmaFC_=3@cg_(hcfr*isN72Z@$VAuBNY~IJ#K6?bz|hLnSkKtp#KhRt*uq#LG{l*Q z!GMQ}sZs)n<$+kzfRl+4sFjh4k;y=DVxWW;3%jqszhAtsE0PQoi+TrtHBciXGZUjQ e6Qc!?WQm(N*IO851QQdZE+=yX10yE`2m%1OJrvjg literal 232 zcmZQ!W?*4pVr1q~G%_$U(KR&CH82b@G`0dF69YX%b5lbDQ=o)GXoxcpg8>f{Q>7#j zO8~LF0Vfk9P%9%7Ba?w53%jqszhAtsE0hu7$ifR1iFa`|F)~C_&BRi#-Z}?p7$Y+i zqc9j*u*5-(X4K_mZeU>KWB@^kP3S`MOiVx_uEziW`Tzfa_m`~_#D2|LA{Ec#xedhr V%<=W@G%qh%Fq?HseQ~^5Dgdz>F75yT From 45aca0efcb0b14c88c9d6d7ea35f49fe98495435 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Fri, 20 Dec 2024 12:13:57 -0700 Subject: [PATCH 4/8] Add path to log when loading data --- anise/src/almanac/mod.rs | 34 ++++++++++------ anise/tests/orientations/mod.rs | 69 ++++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/anise/src/almanac/mod.rs b/anise/src/almanac/mod.rs index 227d7d4a..a2e84671 100644 --- a/anise/src/almanac/mod.rs +++ b/anise/src/almanac/mod.rs @@ -116,7 +116,12 @@ impl Almanac { me } + /// Loads the provides bytes as one of the data types supported in ANISE. pub fn load_from_bytes(&self, bytes: Bytes) -> AlmanacResult { + self._load_from_bytes(bytes, None) + } + + fn _load_from_bytes(&self, bytes: Bytes, path: Option<&str>) -> AlmanacResult { // Try to load as a SPICE DAF first (likely the most typical use case) // Load the header only @@ -125,7 +130,7 @@ impl Almanac { if let Ok(fileid) = file_record.identification() { return match fileid { "PCK" => { - info!("Loading as DAF/PCK"); + info!("Loading {} as DAF/PCK", path.or(Some("bytes")).unwrap()); let bpc = BPC::parse(bytes) .context(BPCSnafu { action: "parsing bytes", @@ -138,7 +143,7 @@ impl Almanac { }) } "SPK" => { - info!("Loading as DAF/SPK"); + info!("Loading {} as DAF/SPK", path.or(Some("bytes")).unwrap()); let spk = SPK::parse(bytes) .context(SPKSnafu { action: "parsing bytes", @@ -169,6 +174,10 @@ impl Almanac { action: "loading as spacecraft data", } })?; + info!( + "Loading {} as ANISE spacecraft data", + path.or(Some("bytes")).unwrap() + ); Ok(self.with_spacecraft_data(dataset)) } DataSetType::PlanetaryData => { @@ -178,6 +187,7 @@ impl Almanac { action: "loading as planetary data", } })?; + info!("Loading {} as ANISE/PCA", path.or(Some("bytes")).unwrap()); Ok(self.with_planetary_data(dataset)) } DataSetType::EulerParameterData => { @@ -187,6 +197,7 @@ impl Almanac { action: "loading Euler parameters", } })?; + info!("Loading {} as ANISE/EPA", path.or(Some("bytes")).unwrap()); Ok(self.with_euler_parameters(dataset)) } } @@ -209,16 +220,17 @@ impl Almanac { let bytes = file2heap!(path).context(LoadingSnafu { path: path.to_string(), })?; - info!("Loading almanac from {path}"); - self.load_from_bytes(bytes).map_err(|e| match e { - AlmanacError::GenericError { err } => { - // Add the path to the error - AlmanacError::GenericError { - err: format!("with {path}: {err}"), + + self._load_from_bytes(bytes, Some(path)) + .map_err(|e| match e { + AlmanacError::GenericError { err } => { + // Add the path to the error + AlmanacError::GenericError { + err: format!("with {path}: {err}"), + } } - } - _ => e, - }) + _ => e, + }) } /// Initializes a new Almanac from the provided file path, guessing at the file type diff --git a/anise/tests/orientations/mod.rs b/anise/tests/orientations/mod.rs index f5a2f566..d9cf3c4b 100644 --- a/anise/tests/orientations/mod.rs +++ b/anise/tests/orientations/mod.rs @@ -2,7 +2,8 @@ use std::path::PathBuf; use anise::constants::frames::{ EARTH_ITRF93, EME2000, IAU_JUPITER_FRAME, IAU_MOON_FRAME, JUPITER_BARYCENTER_J2000, MOON_J2000, - MOON_PA_DE421_FRAME, MOON_PA_DE440_FRAME, MOON_PA_FRAME, + MOON_ME_DE440_ME421_FRAME, MOON_ME_FRAME, MOON_PA_DE421_FRAME, MOON_PA_DE440_FRAME, + MOON_PA_FRAME, }; use anise::constants::orientations::{ ECLIPJ2000, IAU_JUPITER, IAU_MOON, ITRF93, J2000, MOON_PA, MOON_PA_DE421, MOON_PA_DE440, @@ -330,11 +331,6 @@ fn regression_test_issue_357_test_moon_me_j2k() { .load("../data/moon_pa_de440_200625.bpc") .unwrap(); - // STATUS: - // The data in the Moon FK is incorrect because it relates the 31007 frame to 301. - // But the text file relates it to the Moon PA frame using its TKFRAME definition. - // At the very least, this will require a change in the EPA file. - let epoch = Epoch::from_str("2024-01-01 22:28:39").unwrap(); let dcm = almanac @@ -394,4 +390,65 @@ fn regression_test_issue_357_test_moon_me_j2k() { (dcm.rot_mat - spice_dcm.rot_mat).norm(), dcm.rot_mat - spice_dcm.rot_mat ); + + // STATUS: + // Max recursion depth because the inertial frame ID is probably incorrectly set. + // The test above only works because it's a single hop of 31008! + + /* + Frame Name Relative to Type Frame ID + ------------------------- ----------------- ----- -------- + MOON_PA MOON_PA_DE440 FIXED 31010 + MOON_ME MOON_ME_DE440_ME421 FIXED 31011 + MOON_PA_DE440 ICRF/J2000 PCK 31008 + MOON_ME_DE440_ME421 MOON_PA_DE440 FIXED 31009 + */ + + // Repeat for Moon ME + let dcm = almanac + .rotate(MOON_PA_DE440_FRAME, MOON_ME_DE440_ME421_FRAME, epoch) + .unwrap(); + + /* + In [9]: sp.sxform("MOON_PA_DE440", "MOON_ME", my_et) + Out[9]: + array([[ 9.99999873e-01, -3.28958658e-04, 3.81521208e-04, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], + [ 3.28959197e-04, 9.99999946e-01, -1.35020600e-06, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], + [-3.81520743e-04, 1.47571074e-06, 9.99999927e-01, + 0.00000000e+00, 0.00000000e+00, 0.00000000e+00], + [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 9.99999873e-01, -3.28958658e-04, 3.81521208e-04], + [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + 3.28959197e-04, 9.99999946e-01, -1.35020600e-06], + [ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00, + -3.81520743e-04, 1.47571074e-06, 9.99999927e-01]]) + */ + + let spice_dcm = DCM { + from: MOON_PA_DE440, + to: J2000, + rot_mat: Matrix3::new( + 9.99999873e-01, + -3.28958658e-04, + 3.81521208e-04, + 3.28959197e-04, + 9.99999946e-01, + -1.35020600e-06, + -3.81520743e-04, + 1.47571074e-06, + 9.99999927e-01, + ), + rot_mat_dt: None, + }; + + assert!( + (dcm.rot_mat - spice_dcm.rot_mat).norm() < 1e-9, + "dcm error! got: {}want:{}err = {:.3e}: {:.3e}", + dcm.rot_mat, + spice_dcm.rot_mat, + (dcm.rot_mat - spice_dcm.rot_mat).norm(), + dcm.rot_mat - spice_dcm.rot_mat + ); } From be88e5dc1999865cce5f7286e92414918e028027 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Fri, 20 Dec 2024 12:36:07 -0700 Subject: [PATCH 5/8] Add test for Moon ME/PA --- anise/src/orientations/paths.rs | 11 +++-------- anise/tests/orientations/mod.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/anise/src/orientations/paths.rs b/anise/src/orientations/paths.rs index e90919c2..8e18b64d 100644 --- a/anise/src/orientations/paths.rs +++ b/anise/src/orientations/paths.rs @@ -131,22 +131,17 @@ impl Almanac { inertial_frame_id = match self.bpc_summary_at_epoch(inertial_frame_id, epoch) { Ok((summary, _, _)) => summary.inertial_frame_id, Err(_) => { - match self.planetary_data.get_by_id(source.orientation_id) { + // Not available as a BPC, so let's see if there's planetary data for it. + match self.planetary_data.get_by_id(inertial_frame_id) { Ok(planetary_data) => planetary_data.parent_id, Err(_) => { // Finally, let's see if it's in the loaded Euler Parameters. self.euler_param_data - .get_by_id(source.orientation_id) + .get_by_id(inertial_frame_id) .context(OrientationDataSetSnafu)? .from } } - // // Not available as a BPC, so let's see if there's planetary data for it. - // let planetary_data = self - // .planetary_data - // .get_by_id(inertial_frame_id) - // .context(OrientationDataSetSnafu)?; - // planetary_data.parent_id } }; dbg!(inertial_frame_id); diff --git a/anise/tests/orientations/mod.rs b/anise/tests/orientations/mod.rs index d9cf3c4b..4f400477 100644 --- a/anise/tests/orientations/mod.rs +++ b/anise/tests/orientations/mod.rs @@ -405,6 +405,34 @@ fn regression_test_issue_357_test_moon_me_j2k() { */ // Repeat for Moon ME + + // Check the path first + let moon_pa_path = almanac + .orientation_path_to_root(MOON_PA_DE440_FRAME, epoch) + .unwrap(); + + assert_eq!(moon_pa_path.0, 1, "Moon PA is defined wrt J2000"); + assert_eq!( + moon_pa_path.1[0].unwrap(), + 1, + "Moon PA is defined wrt J2000" + ); + + let moon_me_path = almanac + .orientation_path_to_root(MOON_ME_DE440_ME421_FRAME, epoch) + .unwrap(); + assert_eq!(moon_me_path.0, 2, "Moon ME is defined wrt Moon PA"); + assert_eq!( + moon_pa_path.1[0].unwrap(), + 31008, + "Moon ME is defined wrt Moon PA" + ); + assert_eq!( + moon_pa_path.1[1].unwrap(), + 1, + "Moon PA is defined wrt J2000" + ); + let dcm = almanac .rotate(MOON_PA_DE440_FRAME, MOON_ME_DE440_ME421_FRAME, epoch) .unwrap(); From 573fd71533746e08bfc56b0dd4f3ec49619708e7 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Sat, 21 Dec 2024 22:43:08 -0700 Subject: [PATCH 6/8] Account for `RELATIVE_TO` when parsing FK, fix rotation from Moon ME to Moon PA Update moon_fk.epa file to the newer one moon_fk_de440.epa Fix #357 --- .github/workflows/rust.yml | 1 + README.md | 2 +- anise/README.md | 2 +- anise/src/almanac/metaload/metaalmanac.rs | 9 ++-- anise/src/almanac/metaload/mod.rs | 4 +- anise/src/naif/kpl/fk.rs | 8 +-- anise/src/naif/kpl/mod.rs | 7 +++ anise/src/naif/kpl/parser.rs | 59 ++++++++++++++++++++-- anise/src/orientations/paths.rs | 5 +- anise/tests/ephemerides/transform.rs | 1 - anise/tests/orientations/mod.rs | 8 +-- data/.gitignore | 3 +- data/latest.dhall | 4 +- data/moon_fk.epa | Bin 232 -> 328 bytes data/moon_fk_de440.epa | Bin 0 -> 285 bytes 15 files changed, 85 insertions(+), 28 deletions(-) create mode 100644 data/moon_fk_de440.epa diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f33edf96..f1e378d1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -47,6 +47,7 @@ jobs: wget -O data/pck08.pca http://public-data.nyxspace.com/anise/v0.5/pck08.pca wget -O data/pck11.pca http://public-data.nyxspace.com/anise/v0.5/pck11.pca wget -O data/moon_fk.epa http://public-data.nyxspace.com/anise/v0.5/moon_fk.epa + wget -O data/moon_fk_de440.epa http://public-data.nyxspace.com/anise/v0.5/moon_fk_de440.epa wget -O data/moon_pa_de440_200625.bpc http://public-data.nyxspace.com/anise/moon_pa_de440_200625.bpc wget -O data/gmat-hermite.bsp http://public-data.nyxspace.com/anise/ci/gmat-hermite.bsp wget -O data/gmat-hermite-big-endian.bsp http://public-data.nyxspace.com/anise/ci/gmat-hermite-big-endian.bsp diff --git a/README.md b/README.md index 5c32e5ce..aa921d7f 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ For convenience, Nyx Space provides a few important SPICE files on a public buck + [de440.bsp](http://public-data.nyxspace.com/anise/de440.bsp): JPL's latest long-term ephemeris dataset + [pck08.pca](http://public-data.nyxspace.com/anise/v0.5/pck08.pca): planetary constants ANISE (`pca`) kernel, built from the JPL gravitational data [gm_de431.tpc](http://public-data.nyxspace.com/anise/gm_de431.tpc) and JPL's plantary constants file [pck00008.tpc](http://public-data.nyxspace.com/anise/pck00008.tpc) + [pck11.pca](http://public-data.nyxspace.com/anise/v0.5/pck11.pca): planetary constants ANISE (`pca`) kernel, built from the JPL gravitational data [gm_de431.tpc](http://public-data.nyxspace.com/anise/gm_de431.tpc) and JPL's plantary constants file [pck00011.tpc](http://public-data.nyxspace.com/anise/pck00011.tpc) -+ [moon_fk.epa](http://public-data.nyxspace.com/anise/v0.5/moon_fk.epa): Euler Parameter ANISE (`epa`) kernel, built from the JPL Moon Frame Kernel `moon_080317.txt` ++ [moon_fk_de440.epa](http://public-data.nyxspace.com/anise/v0.5/moon_fk_de440.epa): Euler Parameter ANISE (`epa`) kernel, built from the JPL Moon Frame Kernel `moon_080317.txt` You may load any of these using the `load()` shortcut that will determine the file type upon loading, e.g. `let almanac = Almanac::new("pck08.pca").unwrap();` or in Python `almanac = Almanac("pck08.pca")`. To automatically download remote assets, from the Nyx Cloud or elsewhere, use the MetaAlmanac: `almanac = MetaAlmanac("ci_config.dhall").process()` in Python. diff --git a/anise/README.md b/anise/README.md index abfd3f50..45103f22 100644 --- a/anise/README.md +++ b/anise/README.md @@ -185,7 +185,7 @@ For convenience, Nyx Space provides a few important SPICE files on a public buck + [de440.bsp](http://public-data.nyxspace.com/anise/de440.bsp): JPL's latest long-term ephemeris dataset + [pck08.pca](http://public-data.nyxspace.com/anise/v0.5/pck08.pca): planetary constants ANISE (`pca`) kernel, built from the JPL gravitational data [gm_de431.tpc](http://public-data.nyxspace.com/anise/gm_de431.tpc) and JPL's plantary constants file [pck00008.tpc](http://public-data.nyxspace.com/anise/pck00008.tpc) + [pck11.pca](http://public-data.nyxspace.com/anise/v0.5/pck11.pca): planetary constants ANISE (`pca`) kernel, built from the JPL gravitational data [gm_de431.tpc](http://public-data.nyxspace.com/anise/gm_de431.tpc) and JPL's plantary constants file [pck00011.tpc](http://public-data.nyxspace.com/anise/pck00011.tpc) -+ [moon_fk.epa](http://public-data.nyxspace.com/anise/v0.5/moon_fk.epa): Euler Parameter ANISE (`epa`) kernel, built from the JPL Moon Frame Kernel `moon_080317.txt` ++ [moon_fk_de440.epa](http://public-data.nyxspace.com/anise/v0.5/moon_fk_de440.epa): Euler Parameter ANISE (`epa`) kernel, built from the JPL Moon Frame Kernel `moon_080317.txt` You may load any of these using the `load()` shortcut that will determine the file type upon loading, e.g. `let almanac = Almanac::new("pck08.pca").unwrap();` or in Python `almanac = Almanac("pck08.pca")`. To automatically download remote assets, from the Nyx Cloud or elsewhere, use the MetaAlmanac: `almanac = MetaAlmanac("ci_config.dhall").process(true)` in Python. diff --git a/anise/src/almanac/metaload/metaalmanac.rs b/anise/src/almanac/metaload/metaalmanac.rs index 0cd0e27c..54a2dba3 100644 --- a/anise/src/almanac/metaload/metaalmanac.rs +++ b/anise/src/almanac/metaload/metaalmanac.rs @@ -239,7 +239,7 @@ impl MetaAlmanac { /// # File list /// - /// - -/// - +/// - /// - /// - /// @@ -265,8 +265,11 @@ impl Default for MetaAlmanac { crc32: Some(0x8213b6e9), }, MetaFile { - uri: nyx_cloud_stor.join("v0.5/moon_fk.epa").unwrap().to_string(), - crc32: Some(0xb93ba21), + uri: nyx_cloud_stor + .join("v0.5/moon_fk_de440.epa") + .unwrap() + .to_string(), + crc32: Some(0x6f0ad74c), }, MetaFile { uri: nyx_cloud_stor diff --git a/anise/src/almanac/metaload/mod.rs b/anise/src/almanac/metaload/mod.rs index 720485ec..00c0b404 100644 --- a/anise/src/almanac/metaload/mod.rs +++ b/anise/src/almanac/metaload/mod.rs @@ -145,8 +145,8 @@ mod meta_test { , { crc32 = Some 0x8213b6e9 , uri = "http://public-data.nyxspace.com/anise/v0.5/pck11.pca" } - , { crc32 = Some 0xb93ba21 - , uri = "http://public-data.nyxspace.com/anise/v0.5/moon_fk.epa" + , { crc32 = Some 0x6f0ad74c + , uri = "http://public-data.nyxspace.com/anise/v0.5/moon_fk_de440.epa" } , { crc32 = Some 0xcde5ca7d , uri = "http://public-data.nyxspace.com/anise/moon_pa_de440_200625.bpc" diff --git a/anise/src/naif/kpl/fk.rs b/anise/src/naif/kpl/fk.rs index a8943fb2..5951a59b 100644 --- a/anise/src/naif/kpl/fk.rs +++ b/anise/src/naif/kpl/fk.rs @@ -203,7 +203,7 @@ mod fk_ut { use crate::math::rotation::{r1, r2, r3, DCM}; let dataset = convert_fk("../data/moon_080317.txt", false).unwrap(); - assert_eq!(dataset.len(), 3, "expected three items"); + assert_eq!(dataset.len(), 5, "expected three items"); // Check that we've correctly set the names. let moon_me = dataset.get_by_name("MOON_ME_DE421").unwrap(); @@ -217,7 +217,7 @@ mod fk_ut { * r2((78.56 / 3600.0_f64).to_radians()) * r1((0.30 / 3600.0_f64).to_radians()); assert!((DCM::from(moon_me).rot_mat - expected).norm() < 1e-10); - println!("{}", dataset.crc32()); + println!("CRC32 = {}", dataset.crc32()); dataset .save_as(&PathBuf::from_str("../data/moon_fk.epa").unwrap(), true) .unwrap(); @@ -231,7 +231,7 @@ mod fk_ut { use crate::math::rotation::{r1, r2, r3, DCM}; let dataset = convert_fk("../data/moon_de440_220930.txt", false).unwrap(); - assert_eq!(dataset.len(), 3, "expected three items"); + assert_eq!(dataset.len(), 4, "expected three items"); // Check that we've correctly set the names. let moon_me = dataset.get_by_name("MOON_ME_DE440_ME421").unwrap(); @@ -245,7 +245,7 @@ mod fk_ut { * r2((78.6944 / 3600.0_f64).to_radians()) * r1((0.2785 / 3600.0_f64).to_radians()); assert!((DCM::from(moon_me).rot_mat - expected).norm() < 1e-10); - println!("{}", dataset.crc32()); // 879707574 + println!("CRC32 = {}", dataset.crc32()); // 879707574 dataset .save_as( &PathBuf::from_str("../data/moon_fk_de440.epa").unwrap(), diff --git a/anise/src/naif/kpl/mod.rs b/anise/src/naif/kpl/mod.rs index 12b55102..2a74f23f 100644 --- a/anise/src/naif/kpl/mod.rs +++ b/anise/src/naif/kpl/mod.rs @@ -51,6 +51,13 @@ impl KPLValue { _ => whatever!("can only convert Integer to i32 but this is {self:?}"), } } + + pub fn to_string(&self) -> Result { + match self { + KPLValue::String(data) => Ok(data.clone()), + _ => whatever!("can only convert Integer to i32 but this is {self:?}"), + } + } } impl From for KPLValue { diff --git a/anise/src/naif/kpl/parser.rs b/anise/src/naif/kpl/parser.rs index b2a5de16..425c80cf 100644 --- a/anise/src/naif/kpl/parser.rs +++ b/anise/src/naif/kpl/parser.rs @@ -19,7 +19,7 @@ use std::path::Path; use log::{error, info, warn}; use crate::constants::orientations::J2000; -use crate::math::rotation::{r1, r2, r3, DCM}; +use crate::math::rotation::{r1, r2, r3, Quaternion, DCM}; use crate::math::Matrix3; use crate::naif::kpl::fk::FKItem; use crate::naif::kpl::tpc::TPCItem; @@ -320,13 +320,29 @@ pub fn convert_fk + fmt::Debug>( let assignments = parse_file::<_, FKItem>(fk_file_path, show_comments)?; + let mut ids_to_update = Vec::new(); + // Add all of the data into the data set for (id, item) in assignments { if !item.data.contains_key(&Parameter::Angles) && !item.data.contains_key(&Parameter::Matrix) { - warn!("{id} contains neither angles nor matrix, cannot convert to Euler Parameter"); - continue; + let mut warn = false; + if let Some(class) = item.data.get(&Parameter::Class) { + if class.to_i32().unwrap() == 2 { + // BPC based frame, insert as-is. + // Class 2 need a BPC for the full rotation. + dataset.push(Quaternion::identity(id, id), Some(id), item.name.as_deref())?; + } else { + warn = true; + } + } else { + warn = true; + } + if warn { + warn!("{id} contains neither angles nor matrix, cannot convert to Euler Parameter"); + continue; + } } else if let Some(angles) = item.data.get(&Parameter::Angles) { let unit = item .data @@ -343,7 +359,21 @@ pub fn convert_fk + fmt::Debug>( } // Build the quaternion from the Euler matrices let from = id; - let to = item.data[&Parameter::Center].to_i32().unwrap(); + let mut to = item.data[&Parameter::Center].to_i32().unwrap(); + if let Some(class) = item.data.get(&Parameter::Class) { + if class.to_i32().unwrap() == 4 { + // This is a relative frame. + let relative_to = item.data.get(&Parameter::Relative).ok_or(DataSetError::Conversion { + action: format!("frame {id} is class 4 relative to, but the RELATIVE_TO token was not found"), + })?.to_string().unwrap(); + if let Ok(parent) = dataset.get_by_name(&relative_to) { + to = parent.to; + } else { + // Not found yet, let's mark it as an ID to revisit. + ids_to_update.push((id, relative_to.clone())); + } + } + } let mut dcm = Matrix3::identity(); @@ -391,10 +421,31 @@ pub fn convert_fk + fmt::Debug>( rot_mat, rot_mat_dt: None, }; + dataset.push(dcm.into(), Some(id), item.name.as_deref())?; } } + // Finally, let's update the frames of the IDs defined as relative. + for (id, relative_to) in ids_to_update { + let parent_idx = dataset + .lut + .by_name + .get(&(relative_to.as_str().try_into().unwrap())) + .ok_or(DataSetError::Conversion { + action: format!( + "frame {id} is class 4 relative to `{relative_to}`, but that frame is not found" + ), + })?; + + let parent_id = dataset.data[(*parent_idx) as usize].to; + + // Modify this EP. + let this_idx = dataset.lut.by_id[&id]; + let mut this_q = dataset.data[this_idx as usize]; + this_q.from = parent_id; + } + dataset.set_crc32(); dataset.metadata = Metadata::default(); dataset.metadata.dataset_type = DataSetType::EulerParameterData; diff --git a/anise/src/orientations/paths.rs b/anise/src/orientations/paths.rs index 8e18b64d..33c3e354 100644 --- a/anise/src/orientations/paths.rs +++ b/anise/src/orientations/paths.rs @@ -106,7 +106,7 @@ impl Almanac { self.euler_param_data .get_by_id(source.orientation_id) .context(OrientationDataSetSnafu)? - .from + .to } } } @@ -139,12 +139,11 @@ impl Almanac { self.euler_param_data .get_by_id(inertial_frame_id) .context(OrientationDataSetSnafu)? - .from + .to } } } }; - dbg!(inertial_frame_id); of_path[of_path_len] = Some(inertial_frame_id); of_path_len += 1; diff --git a/anise/tests/ephemerides/transform.rs b/anise/tests/ephemerides/transform.rs index 0a984cce..24b65721 100644 --- a/anise/tests/ephemerides/transform.rs +++ b/anise/tests/ephemerides/transform.rs @@ -374,7 +374,6 @@ fn validate_gh_283_multi_barycenter_and_los(almanac: Almanac) { assert_eq!(access_times[access_count], access_duration); } } - // dbg!(prev_aer.is_obstructed(), aer.is_obstructed()); } else if !aer.is_obstructed() { access_start = Some(aer.epoch); } diff --git a/anise/tests/orientations/mod.rs b/anise/tests/orientations/mod.rs index 4f400477..d5f8827f 100644 --- a/anise/tests/orientations/mod.rs +++ b/anise/tests/orientations/mod.rs @@ -391,10 +391,6 @@ fn regression_test_issue_357_test_moon_me_j2k() { dcm.rot_mat - spice_dcm.rot_mat ); - // STATUS: - // Max recursion depth because the inertial frame ID is probably incorrectly set. - // The test above only works because it's a single hop of 31008! - /* Frame Name Relative to Type Frame ID ------------------------- ----------------- ----- -------- @@ -423,12 +419,12 @@ fn regression_test_issue_357_test_moon_me_j2k() { .unwrap(); assert_eq!(moon_me_path.0, 2, "Moon ME is defined wrt Moon PA"); assert_eq!( - moon_pa_path.1[0].unwrap(), + moon_me_path.1[0].unwrap(), 31008, "Moon ME is defined wrt Moon PA" ); assert_eq!( - moon_pa_path.1[1].unwrap(), + moon_me_path.1[1].unwrap(), 1, "Moon PA is defined wrt J2000" ); diff --git a/data/.gitignore b/data/.gitignore index 5cf5cd6b..569f2a4c 100644 --- a/data/.gitignore +++ b/data/.gitignore @@ -1,3 +1,4 @@ !pck08.pca !pck11.pca -!moon_fk.epa \ No newline at end of file +!moon_fk.epa +!moon_fk_de440.epa \ No newline at end of file diff --git a/data/latest.dhall b/data/latest.dhall index fa79dfae..5ad76c57 100644 --- a/data/latest.dhall +++ b/data/latest.dhall @@ -6,8 +6,8 @@ , { crc32 = Some 0x8213b6e9 , uri = "http://public-data.nyxspace.com/anise/v0.5/pck11.pca" } - , { crc32 = Some 0xb93ba21 - , uri = "http://public-data.nyxspace.com/anise/v0.5/moon_fk.epa" + , { crc32 = Some 0x6f0ad74c + , uri = "http://public-data.nyxspace.com/anise/v0.5/moon_fk_de440.epa" } , { crc32 = Some 0xcde5ca7d , uri = "http://public-data.nyxspace.com/anise/moon_pa_de440_200625.bpc" diff --git a/data/moon_fk.epa b/data/moon_fk.epa index 53588fcfba3e724032d6481af365b0fbba517de9..666899911303db51b2ad67161729560e7cc9b573 100644 GIT binary patch delta 231 zcmaFCc!DXFg_(hcfr*isN72Z@$VAuBNY}_H#K6?b*x1U%P|w8F$iUFl)WTdLG{l*Q z!9awGsZt7v<$+idh$Vnn&VZkZ5oiP>6C)GQASOl@1A7);Uw?nU_yEUv7grMl3RL>54jB@SXXqwd5rVE_#28A|{F diff --git a/data/moon_fk_de440.epa b/data/moon_fk_de440.epa new file mode 100644 index 0000000000000000000000000000000000000000..b8db61794770734eb0c08187f763f4c30da7329a GIT binary patch literal 285 zcmZQ!W?*4pVr1q~G%_$U(KR&EH8Kh@Ftswav@$l-Gc&U=H!w3aGE)c*apqw#5MW}e zR03iJAXWroWdj~2Mxb^^CPpTpAqHkF?7sf~e(?d0EWA)gyo;-ei2;i+nCt5b;l}&A zniv^E75loPn!?1A&vo6$fS-wxg^5uZj4Xg8OCiK^MqN(k1_nk>1`ve0MFCX^ Date: Sat, 21 Dec 2024 22:52:59 -0700 Subject: [PATCH 7/8] Add Earth J2k to Moon ME computation --- anise/tests/orientations/mod.rs | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/anise/tests/orientations/mod.rs b/anise/tests/orientations/mod.rs index d5f8827f..b6966f9e 100644 --- a/anise/tests/orientations/mod.rs +++ b/anise/tests/orientations/mod.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use anise::constants::frames::{ - EARTH_ITRF93, EME2000, IAU_JUPITER_FRAME, IAU_MOON_FRAME, JUPITER_BARYCENTER_J2000, MOON_J2000, - MOON_ME_DE440_ME421_FRAME, MOON_ME_FRAME, MOON_PA_DE421_FRAME, MOON_PA_DE440_FRAME, - MOON_PA_FRAME, + EARTH_ITRF93, EARTH_J2000, EME2000, IAU_JUPITER_FRAME, IAU_MOON_FRAME, + JUPITER_BARYCENTER_J2000, MOON_J2000, MOON_ME_DE440_ME421_FRAME, MOON_ME_FRAME, + MOON_PA_DE421_FRAME, MOON_PA_DE440_FRAME, MOON_PA_FRAME, }; use anise::constants::orientations::{ ECLIPJ2000, IAU_JUPITER, IAU_MOON, ITRF93, J2000, MOON_PA, MOON_PA_DE421, MOON_PA_DE440, @@ -329,6 +329,8 @@ fn regression_test_issue_357_test_moon_me_j2k() { .load("../data/moon_fk_de440.epa") .unwrap() .load("../data/moon_pa_de440_200625.bpc") + .unwrap() + .load("../data/de440s.bsp") .unwrap(); let epoch = Epoch::from_str("2024-01-01 22:28:39").unwrap(); @@ -475,4 +477,29 @@ fn regression_test_issue_357_test_moon_me_j2k() { (dcm.rot_mat - spice_dcm.rot_mat).norm(), dcm.rot_mat - spice_dcm.rot_mat ); + + // Verification of functionality. + // Build an orbit in the Earth J2000 frame and transform it into the Moon ME frame to get its latitude and longitude. + let epoch = Epoch::from_str("2024-09-22T08:45:22 UTC").unwrap(); + // This state is identical in ANISE and SPICE, queried from a BSP. + let orbit_moon_j2k = Orbit::new( + 638.053603, + -1776.813629, + 195.147575, + -0.017910, + -0.181449, + -1.584180, + epoch, + MOON_J2000, + ); + // Transform to Earth J2000. + let orbit_earth_j2k = almanac + .transform_to(orbit_moon_j2k, EARTH_J2000, None) + .unwrap(); + // Compute the LLA in the Moon ME frame, used for cartography. + let orbit_moon_me = almanac + .transform_to(orbit_earth_j2k, MOON_ME_DE440_ME421_FRAME, None) + .unwrap(); + let (lat, long, alt) = orbit_moon_me.latlongalt().unwrap(); + dbg!(lat, long, alt); } From 7c8c4031f6956a97eff45ceaece2f882627df340 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Sat, 21 Dec 2024 23:03:18 -0700 Subject: [PATCH 8/8] Clippy --- .github/workflows/rust.yml | 3 ++- anise/src/almanac/mod.rs | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f1e378d1..0de0effd 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -54,7 +54,6 @@ jobs: wget -O data/variable-seg-size-hermite.bsp http://public-data.nyxspace.com/anise/ci/variable-seg-size-hermite.bsp wget -O data/earth_latest_high_prec.bpc http://public-data.nyxspace.com/anise/ci/earth_latest_high_prec-2023-09-08.bpc wget -O data/lro.bsp http://public-data.nyxspace.com/nyx/examples/lrorg_2023349_2024075_v01_LE.bsp - wget -O data/moon_pa_de440_200625.bpc http://public-data.nyxspace.com/anise/moon_pa_de440_200625.bpc - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable @@ -194,6 +193,8 @@ jobs: wget -O data/variable-seg-size-hermite.bsp http://public-data.nyxspace.com/anise/ci/variable-seg-size-hermite.bsp wget -O data/earth_latest_high_prec.bpc http://public-data.nyxspace.com/anise/ci/earth_latest_high_prec-2023-09-08.bpc wget -O data/lro.bsp http://public-data.nyxspace.com/nyx/examples/lrorg_2023349_2024075_v01_LE.bsp + wget -O data/moon_fk_de440.epa http://public-data.nyxspace.com/anise/v0.5/moon_fk_de440.epa + wget -O data/moon_pa_de440_200625.bpc http://public-data.nyxspace.com/anise/moon_pa_de440_200625.bpc - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable diff --git a/anise/src/almanac/mod.rs b/anise/src/almanac/mod.rs index a2e84671..c709e468 100644 --- a/anise/src/almanac/mod.rs +++ b/anise/src/almanac/mod.rs @@ -130,7 +130,7 @@ impl Almanac { if let Ok(fileid) = file_record.identification() { return match fileid { "PCK" => { - info!("Loading {} as DAF/PCK", path.or(Some("bytes")).unwrap()); + info!("Loading {} as DAF/PCK", path.unwrap_or("bytes")); let bpc = BPC::parse(bytes) .context(BPCSnafu { action: "parsing bytes", @@ -143,7 +143,7 @@ impl Almanac { }) } "SPK" => { - info!("Loading {} as DAF/SPK", path.or(Some("bytes")).unwrap()); + info!("Loading {} as DAF/SPK", path.unwrap_or("bytes")); let spk = SPK::parse(bytes) .context(SPKSnafu { action: "parsing bytes", @@ -176,7 +176,7 @@ impl Almanac { })?; info!( "Loading {} as ANISE spacecraft data", - path.or(Some("bytes")).unwrap() + path.unwrap_or("bytes") ); Ok(self.with_spacecraft_data(dataset)) } @@ -187,7 +187,7 @@ impl Almanac { action: "loading as planetary data", } })?; - info!("Loading {} as ANISE/PCA", path.or(Some("bytes")).unwrap()); + info!("Loading {} as ANISE/PCA", path.unwrap_or("bytes")); Ok(self.with_planetary_data(dataset)) } DataSetType::EulerParameterData => { @@ -197,7 +197,7 @@ impl Almanac { action: "loading Euler parameters", } })?; - info!("Loading {} as ANISE/EPA", path.or(Some("bytes")).unwrap()); + info!("Loading {} as ANISE/EPA", path.unwrap_or("bytes")); Ok(self.with_euler_parameters(dataset)) } }