From 28aea9e83b098146bf81931bf8291d352868adf3 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 29 Aug 2023 20:12:52 -0600 Subject: [PATCH 1/4] Relax tracking sim from Python Python ground tracking arc sim now supports both orbit and spacecraft trajectories --- src/python/orbit_determination/arc.rs | 38 ++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/python/orbit_determination/arc.rs b/src/python/orbit_determination/arc.rs index f3453b49..9e82814d 100644 --- a/src/python/orbit_determination/arc.rs +++ b/src/python/orbit_determination/arc.rs @@ -16,22 +16,26 @@ along with this program. If not, see . */ -use std::collections::HashMap; - use crate::cosmic::Cosm; use crate::io::trajectory_data::TrajectoryLoader; use crate::io::ExportCfg; +use crate::md::prelude::Traj; use crate::od::msr::RangeDoppler; use crate::od::simulator::TrackingArcSim; pub use crate::od::simulator::TrkConfig; pub use crate::{io::ConfigError, od::prelude::GroundStation}; -use crate::{NyxError, Spacecraft}; +use crate::{NyxError, Orbit, Spacecraft}; +use either::Either; use pyo3::prelude::*; +use std::collections::HashMap; #[derive(Clone)] #[pyclass] pub struct GroundTrackingArcSim { - inner: TrackingArcSim, + inner: Either< + TrackingArcSim, + TrackingArcSim, + >, } #[pymethods] @@ -44,13 +48,20 @@ impl GroundTrackingArcSim { configs: HashMap, seed: u64, ) -> Result { - // Try to convert the dynamic trajectory into an Orbit trajectory - let traj = trajectory - .to_traj() - .map_err(|e| NyxError::CustomError(e.to_string()))?; - - let inner = TrackingArcSim::with_seed(devices, traj, configs, seed) - .map_err(NyxError::ConfigError)?; + // Try to convert the dynamic trajectory into a trajectory + let inner = if let Ok(sc_traj) = trajectory.to_traj::() { + Either::Left( + TrackingArcSim::with_seed(devices, sc_traj, configs, seed) + .map_err(NyxError::ConfigError)?, + ) + } else if let Ok(traj) = trajectory.to_traj::() { + Either::Right( + TrackingArcSim::with_seed(devices, traj, configs, seed) + .map_err(NyxError::ConfigError)?, + ) + } else { + return Err(NyxError::CustomError("Provided trajectory could neither be parsed as an orbit trajectory or a spacecraft trajectory".to_string())); + }; Ok(Self { inner }) } @@ -64,7 +75,10 @@ impl GroundTrackingArcSim { export_cfg: ExportCfg, ) -> Result { let cosm = Cosm::de438(); - let arc = self.inner.generate_measurements(cosm)?; + let arc = match &mut self.inner { + Either::Left(arc_sim) => arc_sim.generate_measurements(cosm)?, + Either::Right(arc_sim) => arc_sim.generate_measurements(cosm)?, + }; // Save the tracking arc let maybe = arc.to_parquet(path, export_cfg); From 617747fb969b363dd4e8739e895e1719d5a9ecdb Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Tue, 29 Aug 2023 22:25:04 -0600 Subject: [PATCH 2/4] Clippy happiness --- src/cosmic/cosm.rs | 4 ++-- src/od/process/mod.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cosmic/cosm.rs b/src/cosmic/cosm.rs index 80131220..9024f4cd 100644 --- a/src/cosmic/cosm.rs +++ b/src/cosmic/cosm.rs @@ -542,7 +542,7 @@ impl Cosm { let splt: Vec<_> = name.split(' ').collect(); if splt[0] == "iau" { // This is an IAU frame, so the orientation is specified first, and we don't capitalize the ephemeris name - vec![splt[0].to_string(), splt[1..splt.len()].join(" ")].join(" ") + [splt[0].to_string(), splt[1..splt.len()].join(" ")].join(" ") } else { // Likely a default center and frame, so let's do some clever guessing and capitalize the words let frame_name = capitalize(splt[splt.len() - 1]); @@ -552,7 +552,7 @@ impl Cosm { .collect::>() .join(" "); - vec![ephem_name, frame_name].join(" ") + [ephem_name, frame_name].join(" ") } } } diff --git a/src/od/process/mod.rs b/src/od/process/mod.rs index 83b61211..5bf34f2d 100644 --- a/src/od/process/mod.rs +++ b/src/od/process/mod.rs @@ -26,7 +26,6 @@ pub use crate::od::*; use crate::propagators::error_ctrl::ErrorCtrl; use crate::propagators::PropInstance; pub use crate::time::{Duration, Unit}; -use crate::State; mod conf; pub use conf::{IterationConf, SmoothingArc}; mod trigger; From 1b1ece045df2728a6e2feb48ae653bc2520e5693 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Thu, 31 Aug 2023 18:58:35 -0600 Subject: [PATCH 3/4] Switch from Brotli to zstd for parquet compression Significantly reduces the execution time of OD tests --- Cargo.toml | 11 +++++-- src/io/watermark.rs | 4 +-- src/md/trajectory/traj.rs | 19 +++++++++++ src/od/simulator/arc.rs | 5 +++ src/python/mission_design/orbit_trajectory.rs | 7 ++++ src/python/mission_design/sc_trajectory.rs | 7 ++++ src/python/orbit_determination/arc.rs | 32 ++++++++++++++----- 7 files changed, 72 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7cd23cde..e3a65d58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,11 +62,11 @@ numpy = { version = "0.19", optional = true } indicatif = { version = "0.17", features = ["rayon"] } rstats = "1.2.50" thiserror = "1.0" -parquet = { version = "45.0.0", default-features = false, features = [ +parquet = { version = "46.0.0", default-features = false, features = [ "arrow", - "brotli", + "zstd", ] } -arrow = "45.0.0" +arrow = "46.0.0" shadow-rs = { version = "0.23.0", default-features = false } serde_yaml = "0.9.21" whoami = "1.3.0" @@ -92,3 +92,8 @@ python = ["pyo3", "pyo3-log", "hifitime/python", "numpy", "pythonize"] [lib] crate-type = ["cdylib", "rlib"] name = "nyx_space" + +[target.x86_64-unknown-linux-gnu] +# For flamegraph -- https://github.com/flamegraph-rs/flamegraph +linker = "/usr/bin/clang" +rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"] \ No newline at end of file diff --git a/src/io/watermark.rs b/src/io/watermark.rs index 4692e0f9..dac7e38a 100644 --- a/src/io/watermark.rs +++ b/src/io/watermark.rs @@ -20,7 +20,7 @@ use std::collections::HashMap; use hifitime::Epoch; use parquet::{ - basic::{BrotliLevel, Compression}, + basic::{Compression, ZstdLevel}, file::properties::WriterProperties, format::KeyValue, }; @@ -32,7 +32,7 @@ shadow!(build); /// The parquet writer properties pub(crate) fn pq_writer(metadata: Option>) -> Option { let bldr = WriterProperties::builder() - .set_compression(Compression::BROTLI(BrotliLevel::try_new(10).unwrap())); + .set_compression(Compression::ZSTD(ZstdLevel::try_new(10).unwrap())); let mut file_metadata = vec![ KeyValue::new("Generated by".to_string(), prj_name_ver()), diff --git a/src/md/trajectory/traj.rs b/src/md/trajectory/traj.rs index ac20bd0c..7de2ba00 100644 --- a/src/md/trajectory/traj.rs +++ b/src/md/trajectory/traj.rs @@ -596,6 +596,25 @@ where Ok(traj) } + /// Rebuilds this trajectory with the provided epochs. + /// This may lead to aliasing due to the Nyquist–Shannon sampling theorem. + pub fn rebuild(&self, epochs: &[Epoch]) -> Result { + if self.states.is_empty() { + return Err(NyxError::Trajectory(TrajError::CreationError( + "No trajectory to convert".to_string(), + ))); + } + + let mut traj = Self::new(); + for epoch in epochs { + traj.states.push(self.at(*epoch)?); + } + + traj.finalize(); + + Ok(traj) + } + /// Export the difference in RIC from of this trajectory compare to the "other" trajectory in parquet format. /// /// # Notes diff --git a/src/od/simulator/arc.rs b/src/od/simulator/arc.rs index 6c9a55d3..82d69db1 100644 --- a/src/od/simulator/arc.rs +++ b/src/od/simulator/arc.rs @@ -156,6 +156,11 @@ where self.allow_overlap = false; } + /// Allows overlapping measurements + pub fn allow_overlap(&mut self) { + self.allow_overlap = true; + } + /// Generates measurements from the simulated tracking arc. /// /// Notes: diff --git a/src/python/mission_design/orbit_trajectory.rs b/src/python/mission_design/orbit_trajectory.rs index 04ff188a..acf636de 100644 --- a/src/python/mission_design/orbit_trajectory.rs +++ b/src/python/mission_design/orbit_trajectory.rs @@ -69,6 +69,13 @@ impl OrbitTraj { Ok(Self { inner }) } + /// Copies this object and rebuilds it with the provided epochs + fn rebuild(&self, epochs: &[Epoch]) -> Result { + let inner = self.inner.rebuild(epochs)?; + + Ok(Self { inner }) + } + /// Finds a specific event in a trajectory. /// /// If a start or end epoch is provided (or both are provided), this function will return a list of a single event. diff --git a/src/python/mission_design/sc_trajectory.rs b/src/python/mission_design/sc_trajectory.rs index f82e95ea..1d8c11e8 100644 --- a/src/python/mission_design/sc_trajectory.rs +++ b/src/python/mission_design/sc_trajectory.rs @@ -70,6 +70,13 @@ impl SpacecraftTraj { Ok(Self { inner }) } + /// Copies this object and rebuilds it with the provided epochs + fn rebuild(&self, epochs: &[Epoch]) -> Result { + let inner = self.inner.rebuild(epochs)?; + + Ok(Self { inner }) + } + /// Finds a specific event in a trajectory. /// /// If a start or end epoch is provided (or both are provided), this function will return a list of a single event. diff --git a/src/python/orbit_determination/arc.rs b/src/python/orbit_determination/arc.rs index 9e82814d..fb292648 100644 --- a/src/python/orbit_determination/arc.rs +++ b/src/python/orbit_determination/arc.rs @@ -47,18 +47,34 @@ impl GroundTrackingArcSim { trajectory: TrajectoryLoader, configs: HashMap, seed: u64, + allow_overlap: Option, ) -> Result { // Try to convert the dynamic trajectory into a trajectory let inner = if let Ok(sc_traj) = trajectory.to_traj::() { - Either::Left( - TrackingArcSim::with_seed(devices, sc_traj, configs, seed) - .map_err(NyxError::ConfigError)?, - ) + let mut inner = TrackingArcSim::with_seed(devices, sc_traj, configs, seed) + .map_err(NyxError::ConfigError)?; + + if let Some(allow_overlap) = allow_overlap { + if allow_overlap { + inner.allow_overlap(); + } else { + inner.disallow_overlap(); + } + } + Either::Left(inner) } else if let Ok(traj) = trajectory.to_traj::() { - Either::Right( - TrackingArcSim::with_seed(devices, traj, configs, seed) - .map_err(NyxError::ConfigError)?, - ) + let inner = TrackingArcSim::with_seed(devices, traj, configs, seed) + .map_err(NyxError::ConfigError)?; + + if let Some(allow_overlap) = allow_overlap { + if allow_overlap { + inner.allow_overlap(); + } else { + inner.disallow_overlap(); + } + } + + Either::Right(inner) } else { return Err(NyxError::CustomError("Provided trajectory could neither be parsed as an orbit trajectory or a spacecraft trajectory".to_string())); }; From b247a60fd09e4f1c5c6906058906d43f7abc89d5 Mon Sep 17 00:00:00 2001 From: Christopher Rabotin Date: Thu, 31 Aug 2023 21:15:33 -0600 Subject: [PATCH 4/4] Remove pyo3 warnings --- src/cosmic/spacecraft.rs | 8 -------- src/io/mod.rs | 9 +++------ src/io/trajectory_data.rs | 7 +++---- src/md/events/mod.rs | 6 ------ src/od/noise/gauss_markov.rs | 2 +- src/od/process/rejectcrit.rs | 5 +---- src/od/simulator/trkconfig.rs | 6 ------ src/python/mission_design/events.rs | 3 +++ src/python/mission_design/orbit_trajectory.rs | 4 ++-- src/python/mission_design/sc_trajectory.rs | 4 ++-- src/python/mission_design/spacecraft.rs | 5 +++++ src/python/orbit_determination/arc.rs | 3 +-- src/python/orbit_determination/estimate.rs | 2 +- src/python/orbit_determination/trkconfig.rs | 3 +++ tests/python/test_mission_design.py | 15 ++++++++++++++- 15 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/cosmic/spacecraft.rs b/src/cosmic/spacecraft.rs index 5cd856b6..fc55c66d 100644 --- a/src/cosmic/spacecraft.rs +++ b/src/cosmic/spacecraft.rs @@ -83,12 +83,6 @@ impl From for f64 { #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[cfg_attr(feature = "python", pyclass)] #[cfg_attr(feature = "python", pyo3(module = "nyx_space.cosmic"))] -#[cfg_attr( - feature = "python", - pyo3( - text_signature = "(orbit, dry_mass_kg, fuel_mass_kg, srp_area_m2, drag_area_m2, cr, cd, thruster, mode)" - ) -)] pub struct Spacecraft { /// Initial orbit the vehicle is in #[serde(deserialize_with = "orbit_from_str")] @@ -127,7 +121,6 @@ impl Default for Spacecraft { } #[cfg_attr(feature = "python", pyclass)] -#[cfg_attr(feature = "python", pyo3(text_signature = "(area_m2, cr=1.8)"))] #[cfg_attr(feature = "python", pyo3(module = "nyx_space.cosmic"))] #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] /// The Solar Radiation Pressure configuration for a spacecraft @@ -158,7 +151,6 @@ impl Default for SrpConfig { } #[cfg_attr(feature = "python", pyclass)] -#[cfg_attr(feature = "python", pyo3(text_signature = "(area_m2, cd=2.2)"))] #[cfg_attr(feature = "python", pyo3(module = "nyx_space.cosmic"))] #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] /// The drag configuration for a spacecraft diff --git a/src/io/mod.rs b/src/io/mod.rs index a748b18f..5a123203 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -64,12 +64,6 @@ use pyo3::prelude::*; /// Configuration for exporting a trajectory to parquet. #[derive(Clone, Default, Serialize, Deserialize, TypedBuilder)] #[cfg_attr(feature = "python", pyclass)] -#[cfg_attr( - feature = "python", - pyo3( - text_signature = "(timestamp=None, fields=None, start_epoch=None, step=None, end_epoch=None, metadata=None)" - ) -)] pub struct ExportCfg { /// Fields to export, if unset, defaults to all possible fields. #[builder(default, setter(strip_option))] @@ -147,6 +141,9 @@ impl ExportCfg { #[pymethods] impl ExportCfg { #[new] + #[pyo3( + text_signature = "(timestamp=None, fields=None, start_epoch=None, step=None, end_epoch=None, metadata=None)" + )] fn py_new( timestamp: Option, fields: Option>, diff --git a/src/io/trajectory_data.rs b/src/io/trajectory_data.rs index a643e68b..35ba55c6 100644 --- a/src/io/trajectory_data.rs +++ b/src/io/trajectory_data.rs @@ -44,10 +44,6 @@ use crate::cosmic::Cosm; /// A dynamic trajectory allows loading a trajectory Parquet file and converting it /// to the concrete trajectory state type when desired. #[cfg_attr(feature = "python", pyclass)] -#[cfg_attr( - feature = "python", - pyo3(text_signature = "(path, format='parquet', parquet_path=None, spacecraft_template=None)") -)] #[cfg_attr(feature = "python", pyo3(module = "nyx_space.mission_design"))] #[derive(Clone, PartialEq)] pub struct TrajectoryLoader { @@ -276,6 +272,9 @@ impl Display for TrajectoryLoader { impl TrajectoryLoader { /// Initializes a new dynamic trajectory from the provided file, and the format kind #[new] + #[pyo3( + text_signature = "(path, format='parquet', parquet_path=None, spacecraft_template=None)" + )] fn new( path: String, format: Option, diff --git a/src/md/events/mod.rs b/src/md/events/mod.rs index d9b93fee..f860f1ec 100644 --- a/src/md/events/mod.rs +++ b/src/md/events/mod.rs @@ -51,12 +51,6 @@ where /// Defines a state parameter event finder #[derive(Clone, Debug)] #[cfg_attr(feature = "python", pyclass)] -#[cfg_attr( - feature = "python", - pyo3( - text_signature = "(parameter, desired_value, epoch_precision=None, value_precision=None)" - ) -)] pub struct Event { /// The state parameter pub parameter: StateParameter, diff --git a/src/od/noise/gauss_markov.rs b/src/od/noise/gauss_markov.rs index 4aa0f50e..424623fe 100644 --- a/src/od/noise/gauss_markov.rs +++ b/src/od/noise/gauss_markov.rs @@ -61,7 +61,6 @@ use std::sync::Arc; /// This allows the users to model a white noise process without having to change the process type. #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] #[cfg_attr(feature = "python", pyclass)] -#[cfg_attr(feature = "python", pyo3(text_signature = "(tau, sigma, state_state)"))] #[cfg_attr(feature = "python", pyo3(module = "nyx_space.orbit_determination"))] pub struct GaussMarkov { /// The time constant, tau gives the correlation time, or the time over which the intensity of the time correlation will fade to 1/e of its prior value. (This is sometimes incorrectly referred to as the "half-life" of the process.) @@ -359,6 +358,7 @@ impl GaussMarkov { #[cfg(feature = "python")] #[new] + #[pyo3(text_signature = "(tau, sigma, state_state)")] fn py_new( tau: Option, sigma: Option, diff --git a/src/od/process/rejectcrit.rs b/src/od/process/rejectcrit.rs index 2c0ed491..0ee07449 100644 --- a/src/od/process/rejectcrit.rs +++ b/src/od/process/rejectcrit.rs @@ -34,10 +34,6 @@ use std::sync::Arc; #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] #[cfg_attr(feature = "python", pyclass)] #[cfg_attr(feature = "python", pyo3(module = "nyx_space.orbit_determination"))] -#[cfg_attr( - feature = "python", - pyo3(text_signature = "(min_accepted=None, num_sigmas=None)") -)] pub struct FltResid { /// Minimum number of accepted measurements before applying the rejection criteria. pub min_accepted: usize, @@ -49,6 +45,7 @@ pub struct FltResid { #[pymethods] impl FltResid { #[new] + #[pyo3(text_signature = "(min_accepted=None, num_sigmas=None)")] fn py_new(min_accepted: Option, num_sigmas: Option) -> Self { let mut me = Self::default(); if let Some(min_accepted) = min_accepted { diff --git a/src/od/simulator/trkconfig.rs b/src/od/simulator/trkconfig.rs index e4302929..d6e33ed9 100644 --- a/src/od/simulator/trkconfig.rs +++ b/src/od/simulator/trkconfig.rs @@ -38,12 +38,6 @@ use super::Availability; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[cfg_attr(feature = "python", pyclass)] #[cfg_attr(feature = "python", pyo3(module = "nyx_space.orbit_determination"))] -#[cfg_attr( - feature = "python", - pyo3( - text_signature = "(start=None, end=None, schedule_on=None, schedule_off=None, sampling=None)" - ) -)] pub struct TrkConfig { /// Availability configuration to start the tracking arc #[serde(default)] diff --git a/src/python/mission_design/events.rs b/src/python/mission_design/events.rs index 2fdb8d20..2a7b887c 100644 --- a/src/python/mission_design/events.rs +++ b/src/python/mission_design/events.rs @@ -25,6 +25,9 @@ use hifitime::Unit; impl Event { /// Initializes a new event. Arguments are "parameter: StateParameter" and "desired_value: float". #[new] + #[pyo3( + text_signature = "(parameter, desired_value, epoch_precision=None, value_precision=None)" + )] fn py_new( parameter: StateParameter, desired_value: f64, diff --git a/src/python/mission_design/orbit_trajectory.rs b/src/python/mission_design/orbit_trajectory.rs index acf636de..a6255384 100644 --- a/src/python/mission_design/orbit_trajectory.rs +++ b/src/python/mission_design/orbit_trajectory.rs @@ -70,8 +70,8 @@ impl OrbitTraj { } /// Copies this object and rebuilds it with the provided epochs - fn rebuild(&self, epochs: &[Epoch]) -> Result { - let inner = self.inner.rebuild(epochs)?; + fn rebuild(&self, epochs: Vec) -> Result { + let inner = self.inner.rebuild(&epochs)?; Ok(Self { inner }) } diff --git a/src/python/mission_design/sc_trajectory.rs b/src/python/mission_design/sc_trajectory.rs index 1d8c11e8..87ac3f18 100644 --- a/src/python/mission_design/sc_trajectory.rs +++ b/src/python/mission_design/sc_trajectory.rs @@ -71,8 +71,8 @@ impl SpacecraftTraj { } /// Copies this object and rebuilds it with the provided epochs - fn rebuild(&self, epochs: &[Epoch]) -> Result { - let inner = self.inner.rebuild(epochs)?; + fn rebuild(&self, epochs: Vec) -> Result { + let inner = self.inner.rebuild(&epochs)?; Ok(Self { inner }) } diff --git a/src/python/mission_design/spacecraft.rs b/src/python/mission_design/spacecraft.rs index da70b205..12eb8885 100644 --- a/src/python/mission_design/spacecraft.rs +++ b/src/python/mission_design/spacecraft.rs @@ -35,6 +35,9 @@ use pythonize::{depythonize, pythonize}; impl Spacecraft { /// Initialize a new Spacecraft with optional thruster, mode, SRP, and Drag parameters. #[new] + #[pyo3( + text_signature = "(orbit, dry_mass_kg, fuel_mass_kg, srp_area_m2, drag_area_m2, cr, cd, thruster, mode)" + )] pub fn py_new( orbit: Option, dry_mass_kg: Option, @@ -149,6 +152,7 @@ impl Spacecraft { #[pymethods] impl SrpConfig { #[new] + #[pyo3(text_signature = "(area_m2, cr=1.8)")] pub fn py_new(area_m2: Option, cr: Option) -> Self { Self { area_m2: area_m2.unwrap_or(0.0), @@ -207,6 +211,7 @@ impl SrpConfig { #[pymethods] impl DragConfig { #[new] + #[pyo3(text_signature = "(area_m2, cd=2.2)")] pub fn py_new(area_m2: Option, cd: Option) -> Self { Self { area_m2: area_m2.unwrap_or(0.0), diff --git a/src/python/orbit_determination/arc.rs b/src/python/orbit_determination/arc.rs index fb292648..b8ba12e3 100644 --- a/src/python/orbit_determination/arc.rs +++ b/src/python/orbit_determination/arc.rs @@ -19,7 +19,6 @@ use crate::cosmic::Cosm; use crate::io::trajectory_data::TrajectoryLoader; use crate::io::ExportCfg; -use crate::md::prelude::Traj; use crate::od::msr::RangeDoppler; use crate::od::simulator::TrackingArcSim; pub use crate::od::simulator::TrkConfig; @@ -63,7 +62,7 @@ impl GroundTrackingArcSim { } Either::Left(inner) } else if let Ok(traj) = trajectory.to_traj::() { - let inner = TrackingArcSim::with_seed(devices, traj, configs, seed) + let mut inner = TrackingArcSim::with_seed(devices, traj, configs, seed) .map_err(NyxError::ConfigError)?; if let Some(allow_overlap) = allow_overlap { diff --git a/src/python/orbit_determination/estimate.rs b/src/python/orbit_determination/estimate.rs index 66d75d74..b0f3b367 100644 --- a/src/python/orbit_determination/estimate.rs +++ b/src/python/orbit_determination/estimate.rs @@ -36,7 +36,6 @@ use super::ConfigError; /// An estimate of an orbit with its covariance, the latter should be a numpy array of size 36. #[derive(Debug, Clone, PartialEq)] #[pyclass] -#[pyo3(text_signature = "(nominal_orbit, covariance)")] pub(crate) struct OrbitEstimate(pub(crate) KfEstimate); impl Configurable for OrbitEstimate { @@ -60,6 +59,7 @@ impl Configurable for OrbitEstimate { #[pymethods] impl OrbitEstimate { #[new] + #[pyo3(text_signature = "(nominal_orbit, covariance)")] fn new(nominal: Orbit, covar: PyReadonlyArrayDyn) -> Result { // Check the shape of the input let mat6 = match covar.shape() { diff --git a/src/python/orbit_determination/trkconfig.rs b/src/python/orbit_determination/trkconfig.rs index 592c6704..57dc4d1a 100644 --- a/src/python/orbit_determination/trkconfig.rs +++ b/src/python/orbit_determination/trkconfig.rs @@ -43,6 +43,9 @@ impl TrkConfig { } #[new] + #[pyo3( + text_signature = "(start=None, end=None, schedule_on=None, schedule_off=None, sampling=None)" + )] fn py_new( start: Option, end: Option, diff --git a/tests/python/test_mission_design.py b/tests/python/test_mission_design.py index d9f6b31b..a6fe3efb 100644 --- a/tests/python/test_mission_design.py +++ b/tests/python/test_mission_design.py @@ -97,6 +97,19 @@ def test_propagate(): # Resample the trajectory at fixed step size of 25 seconds traj = traj.resample(Unit.Second * 25.0) + # Rebuild the trajectory with specific epochs + ts = TimeSeries( + traj.first().epoch, + traj.last().epoch, + step=Duration("0.3 min 56 s 15 ns"), + inclusive=True, + ) + epochs = [epoch for epoch in ts] + rebuilt_traj = traj.rebuild(epochs[1:-1]) + assert rebuilt_traj.first().epoch == epochs[1] + assert rebuilt_traj.last().epoch == epochs[-2] + + # Export this trajectory with additional metadata and the events # Base path root = Path(__file__).joinpath("../../../").resolve() @@ -311,5 +324,5 @@ def test_merge_traj(): if __name__ == "__main__": - # test_propagate() + test_propagate() test_merge_traj()