Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relax tracking sim from Python #221

Merged
merged 4 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"]
4 changes: 2 additions & 2 deletions src/cosmic/cosm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand All @@ -552,7 +552,7 @@ impl Cosm {
.collect::<Vec<_>>()
.join(" ");

vec![ephem_name, frame_name].join(" ")
[ephem_name, frame_name].join(" ")
}
}
}
Expand Down
8 changes: 0 additions & 8 deletions src/cosmic/spacecraft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,6 @@ impl From<GuidanceMode> 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")]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
9 changes: 3 additions & 6 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down Expand Up @@ -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<bool>,
fields: Option<Vec<StateParameter>>,
Expand Down
7 changes: 3 additions & 4 deletions src/io/trajectory_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<String>,
Expand Down
4 changes: 2 additions & 2 deletions src/io/watermark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::collections::HashMap;

use hifitime::Epoch;
use parquet::{
basic::{BrotliLevel, Compression},
basic::{Compression, ZstdLevel},
file::properties::WriterProperties,
format::KeyValue,
};
Expand All @@ -32,7 +32,7 @@ shadow!(build);
/// The parquet writer properties
pub(crate) fn pq_writer(metadata: Option<HashMap<String, String>>) -> Option<WriterProperties> {
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()),
Expand Down
6 changes: 0 additions & 6 deletions src/md/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
19 changes: 19 additions & 0 deletions src/md/trajectory/traj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, NyxError> {
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
Expand Down
2 changes: 1 addition & 1 deletion src/od/noise/gauss_markov.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down Expand Up @@ -359,6 +358,7 @@ impl GaussMarkov {

#[cfg(feature = "python")]
#[new]
#[pyo3(text_signature = "(tau, sigma, state_state)")]
fn py_new(
tau: Option<Duration>,
sigma: Option<f64>,
Expand Down
1 change: 0 additions & 1 deletion src/od/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 1 addition & 4 deletions src/od/process/rejectcrit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<usize>, num_sigmas: Option<f64>) -> Self {
let mut me = Self::default();
if let Some(min_accepted) = min_accepted {
Expand Down
5 changes: 5 additions & 0 deletions src/od/simulator/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 0 additions & 6 deletions src/od/simulator/trkconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
3 changes: 3 additions & 0 deletions src/python/mission_design/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions src/python/mission_design/orbit_trajectory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ impl OrbitTraj {
Ok(Self { inner })
}

/// Copies this object and rebuilds it with the provided epochs
fn rebuild(&self, epochs: Vec<Epoch>) -> Result<Self, NyxError> {
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.
Expand Down
7 changes: 7 additions & 0 deletions src/python/mission_design/sc_trajectory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ impl SpacecraftTraj {
Ok(Self { inner })
}

/// Copies this object and rebuilds it with the provided epochs
fn rebuild(&self, epochs: Vec<Epoch>) -> Result<Self, NyxError> {
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.
Expand Down
5 changes: 5 additions & 0 deletions src/python/mission_design/spacecraft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Orbit>,
dry_mass_kg: Option<f64>,
Expand Down Expand Up @@ -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<f64>, cr: Option<f64>) -> Self {
Self {
area_m2: area_m2.unwrap_or(0.0),
Expand Down Expand Up @@ -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<f64>, cd: Option<f64>) -> Self {
Self {
area_m2: area_m2.unwrap_or(0.0),
Expand Down
51 changes: 40 additions & 11 deletions src/python/orbit_determination/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,25 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

use std::collections::HashMap;

use crate::cosmic::Cosm;
use crate::io::trajectory_data::TrajectoryLoader;
use crate::io::ExportCfg;
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<Spacecraft, RangeDoppler, GroundStation>,
inner: Either<
TrackingArcSim<Spacecraft, RangeDoppler, GroundStation>,
TrackingArcSim<Orbit, RangeDoppler, GroundStation>,
>,
}

#[pymethods]
Expand All @@ -43,14 +46,37 @@ impl GroundTrackingArcSim {
trajectory: TrajectoryLoader,
configs: HashMap<String, TrkConfig>,
seed: u64,
allow_overlap: Option<bool>,
) -> Result<Self, NyxError> {
// Try to convert the dynamic trajectory into an Orbit trajectory
let traj = trajectory
.to_traj()
.map_err(|e| NyxError::CustomError(e.to_string()))?;
// Try to convert the dynamic trajectory into a trajectory
let inner = if let Ok(sc_traj) = trajectory.to_traj::<Spacecraft>() {
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::<Orbit>() {
let mut 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();
}
}

let inner = TrackingArcSim::with_seed(devices, traj, configs, seed)
.map_err(NyxError::ConfigError)?;
Either::Right(inner)
} else {
return Err(NyxError::CustomError("Provided trajectory could neither be parsed as an orbit trajectory or a spacecraft trajectory".to_string()));
};

Ok(Self { inner })
}
Expand All @@ -64,7 +90,10 @@ impl GroundTrackingArcSim {
export_cfg: ExportCfg,
) -> Result<String, NyxError> {
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);
Expand Down
Loading