Skip to content

Commit

Permalink
Exposing iteration of OD to Python
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristopherRabotin committed Sep 2, 2023
1 parent 65c021f commit 074df45
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 8 deletions.
88 changes: 88 additions & 0 deletions src/od/process/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ use std::convert::TryFrom;
use std::default::Default;
use std::fmt;

#[cfg(feature = "python")]
use pyo3::prelude::*;

/// Defines the stopping condition for the smoother
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "python", pyclass)]
pub enum SmoothingArc {
/// Stop smoothing when the gap between estimate is the provided floating point number in seconds
TimeGap(Duration),
Expand All @@ -35,6 +39,43 @@ pub enum SmoothingArc {
All,
}

#[cfg(feature = "python")]
#[pymethods]
impl SmoothingArc {
#[new]
fn py_new(
strategy: Option<Epoch>,
duration: Option<Duration>,
epoch: Option<Epoch>,
) -> Result<Self, NyxError> {
if let Some(strategy) = strategy {
match strategy.to_lowercase().trim() {
"all" => Ok(Self::All),
"prediction" => Ok(Self::Prediction),
_ => Err(NyxError::CustomError(format!(
"strategy should be `all` or `prediction`"
))),
}
} else if Some(duration) = duration {
Ok(Self::TimeGap(duration))
} else if Some(epoch) = epoch {
Ok(Self::Epoch(epoch))
} else {
Err(NyxError::CustomError(
"Smoothing arc strategy not specified",
))
}
}

fn __repr__(&self) -> String {
format!("{self}")
}

fn __str__(&self) -> String {
format!("Smoothing {self}")
}
}

impl fmt::Display for SmoothingArc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Expand All @@ -49,6 +90,7 @@ impl fmt::Display for SmoothingArc {
/// Defines a filter iteration configuration. Allows iterating on an OD solution until convergence criteria is met.
/// The root mean squared of the prefit residuals ratios is used to assess convergence between iterations.
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "python", pyclass)]
pub struct IterationConf {
/// The number of measurements to account for in the iteration
pub smoother: SmoothingArc,
Expand All @@ -74,6 +116,52 @@ impl IterationConf {
}
}

#[cfg(feature = "python")]
#[pymethods]
impl IterationConf {
#[new]
fn py_new(
smoother: SmoothingArc,
absolute_tol: Option<f64>,
relative_tol: Option<f64>,
max_iterations: Option<usize>,
max_divergences: Option<usize>,
force_failure: Option<bool>,
) -> Self {
let mut me = Self::default();
me.smoother = smoother;
if let Some(abs_tol) = absolute_tol {
me.absolute_tol = abs_tol;
}

if let Some(rel_tol) = relative_tol {
me.relative_tol = rel_toll
}

if let Some(max_it) = max_iterations {
me.max_iterations = max_it;
}

if let Some(max_div) = max_divergences {
me.max_divergences = max_div;
}

if let Some(force_failure) = force_failure {
me.force_failure = force_failure;
}

me
}

fn __repr__(&self) -> String {
format!("{self}")
}

fn __str__(&self) -> String {
format!("Smoothing {self}")
}
}

impl Default for IterationConf {
/// The default absolute tolerance is 1e-2 (calibrated on an EKF with error).
fn default() -> Self {
Expand Down
17 changes: 17 additions & 0 deletions src/python/mission_design/orbit_trajectory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,23 @@ impl OrbitTraj {
Ok(Self { inner: conv_traj })
}

/// Compute the RIC difference between this trajectory and another, writing the output to a parquet file.
fn ric_diff_to_parquet(
&self,
other: &Self,
path: String,
cfg: Option<ExportCfg>,
) -> Result<String, NyxError> {
match self.inner.ric_diff_to_parquet(
&other.inner,
path,
cfg.unwrap_or_else(|| ExportCfg::default()),
) {
Ok(path) => Ok(format!("{}", path.to_str().unwrap())),
Err(e) => Err(NyxError::CustomError(e.to_string())),
}
}

fn __add__(&self, rhs: &Self) -> Result<Self, NyxError> {
let inner = (self.inner.clone() + rhs.inner.clone())?;

Expand Down
4 changes: 3 additions & 1 deletion src/python/orbit_determination/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use crate::io::tracking_data::DynamicTrackingArc;
use crate::io::ExportCfg;
use crate::od::noise::GaussMarkov;
use crate::od::process::FltResid;
use crate::od::process::{FltResid, IterationConf, SmoothingArc};
pub use crate::od::simulator::TrkConfig;
pub use crate::{io::ConfigError, od::prelude::GroundStation};
use pyo3::{prelude::*, py_run};
Expand All @@ -42,6 +42,8 @@ pub(crate) fn register_od(py: Python<'_>, parent_module: &PyModule) -> PyResult<
sm.add_class::<OrbitEstimate>()?;
sm.add_class::<GaussMarkov>()?;
sm.add_class::<FltResid>()?;
sm.add_class::<IterationConf>()?;
sm.add_class::<SmoothingArc>()?;
sm.add_class::<ExportCfg>()?;
sm.add_function(wrap_pyfunction!(process_tracking_arc, sm)?)?;
sm.add_function(wrap_pyfunction!(predictor, sm)?)?;
Expand Down
16 changes: 9 additions & 7 deletions src/python/orbit_determination/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
md::prelude::{Cosm, Propagator, SpacecraftDynamics},
od::{
filter::kalman::KF,
process::{EkfTrigger, FltResid, ODProcess},
process::{EkfTrigger, FltResid, IterationConf, ODProcess},
},
NyxError, Spacecraft,
};
Expand Down Expand Up @@ -54,8 +54,8 @@ pub(crate) fn process_tracking_arc(
predict_for: Option<Duration>,
predict_step: Option<Duration>,
fixed_step: Option<bool>,
iter_conf: Option<IterationConf>,
) -> Result<String, NyxError> {
// TODO: Return a navigation trajectory or use a class that mimics the better ODProcess -- https://github.com/nyx-space/nyx/issues/134
let msr_noise = Matrix2::from_iterator(measurement_noise);

let init_sc = spacecraft.with_orbit(initial_estimate.0.nominal_state.with_stm());
Expand Down Expand Up @@ -83,18 +83,20 @@ pub(crate) fn process_tracking_arc(

let concrete_arc = arc.to_tracking_arc()?;

odp.process_arc::<GroundStation>(&concrete_arc).unwrap();
odp.process_arc::<GroundStation>(&concrete_arc)?;

if let Some(iter_conf) = iter_conf {
odp.iterate_arc::<GroundStation>(&concrete_arc, iter_conf)?;
}

if let Some(epoch) = predict_until {
let max_step =
predict_step.ok_or_else(|| NyxError::CustomError("predict_step unset".to_string()))?;
odp.predict_until(max_step, fixed_step.unwrap_or_else(|| false), epoch)
.unwrap();
odp.predict_until(max_step, fixed_step.unwrap_or_else(|| false), epoch)?;
} else if let Some(duration) = predict_for {
let max_step =
predict_step.ok_or_else(|| NyxError::CustomError("predict_step unset".to_string()))?;
odp.predict_for(max_step, fixed_step.unwrap_or_else(|| false), duration)
.unwrap();
odp.predict_for(max_step, fixed_step.unwrap_or_else(|| false), duration)?;
}

let maybe = odp.to_parquet(
Expand Down

0 comments on commit 074df45

Please sign in to comment.