Skip to content
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
8 changes: 5 additions & 3 deletions python/src/pysatstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,11 @@ impl PySatState {
.as_ref()
.map(|s| &s.0 as &dyn satkit::orbitprop::SatProperties);

self.0
.propagate(&time, propsettings.as_ref(), satprops_ref)
.map(Self)
Ok(Self(self.0.propagate(
&time,
propsettings.as_ref(),
satprops_ref,
)?))
}

fn __getnewargs_ex__<'a>(&self, py: Python<'a>) -> (Bound<'a, PyTuple>, Bound<'a, PyDict>) {
Expand Down
20 changes: 16 additions & 4 deletions src/frames.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
use anyhow::bail;
use thiserror::Error;

/// Errors produced by the `frames` module.
#[derive(Debug, Error)]
pub enum Error {
/// Returned by [`Frame::from_str`](std::str::FromStr::from_str) when the
/// input string does not match any known frame name.
#[error("Invalid Frame")]
InvalidFrame,
}

/// Convenient type alias used throughout the `frames` module.
pub type Result<T> = std::result::Result<T, Error>;

/// Reference frame identifier.
///
Expand Down Expand Up @@ -116,9 +128,9 @@ impl std::fmt::Display for Frame {
}

impl std::str::FromStr for Frame {
type Err = anyhow::Error;
type Err = Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"ITRF" => Ok(Self::ITRF),
"TIRS" => Ok(Self::TIRS),
Expand All @@ -132,7 +144,7 @@ impl std::str::FromStr for Frame {
// canonical is RTN.
"RTN" | "RSW" | "RIC" => Ok(Self::RTN),
"NTW" => Ok(Self::NTW),
_ => bail!("Invalid Frame"),
_ => Err(Error::InvalidFrame),
}
}
}
Expand Down
20 changes: 16 additions & 4 deletions src/itrfcoord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@ use crate::consts::WGS84_F;

use crate::mathtypes::*;

use anyhow::Result;
use thiserror::Error;

/// Errors produced by the `itrfcoord` module.
#[derive(Debug, Error)]
pub enum Error {
/// Returned by [`ITRFCoord::from_slice`] and the [`TryFrom`] impls
/// when the input slice does not contain exactly three elements.
#[error("Input slice must have 3 elements, got {got}")]
InvalidSliceLength { got: usize },
}

/// Convenient type alias used throughout the `itrfcoord` module.
pub type Result<T> = std::result::Result<T, Error>;

/// Geodetic coordinates with named fields
///
Expand Down Expand Up @@ -109,10 +121,10 @@ impl std::convert::From<[f64; 3]> for ITRFCoord {
}

impl std::convert::TryFrom<&[f64]> for ITRFCoord {
type Error = anyhow::Error;
type Error = Error;
fn try_from(v: &[f64]) -> Result<Self> {
if v.len() != 3 {
anyhow::bail!("Input slice must have 3 elements, got {}", v.len());
return Err(Error::InvalidSliceLength { got: v.len() });
}
Ok(Self {
itrf: numeris::vector![v[0], v[1], v[2]],
Expand Down Expand Up @@ -190,7 +202,7 @@ impl ITRFCoord {
///
pub fn from_slice(v: &[f64]) -> Result<Self> {
if v.len() != 3 {
anyhow::bail!("Input slice must have 3 elements");
return Err(Error::InvalidSliceLength { got: v.len() });
}
Ok(Self {
itrf: numeris::vector![v[0], v[1], v[2]],
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ pub mod tle;
pub mod utils;

/// Coordinate frames (re-exported as `Frame` at crate root)
mod frames;
pub mod frames;

// Orbital Mean-Element Messages
pub mod omm;
Expand Down
81 changes: 81 additions & 0 deletions src/orbitprop/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Errors produced by the `orbitprop` module.

use numeris::ode;
use thiserror::Error;

use crate::Frame;

/// Errors that can occur while configuring or executing orbit propagation.
#[derive(Debug, Error)]
pub enum Error {
// -- propagator-internal errors --------------------------------------
/// Returned when the integrated state matrix has an unexpected
/// number of columns.
#[error("Invalid number of columns: {c}")]
InvalidStateColumns { c: usize },

/// Returned by the dense-output interp helpers when the underlying
/// ODE solution does not carry interpolation data.
#[error("No Dense Output in Solution")]
NoDenseOutputInSolution,

/// Wraps an [`ode::OdeError`] surfaced by the chosen integrator.
/// `OdeError` does not implement `std::error::Error` (numeris keeps
/// it as a plain `Display`-only enum), so this variant is built
/// manually rather than via `#[from]`.
#[error("ODE Error: {0}")]
OdeError(ode::OdeError),

/// RODAS4 does not support state transition matrix propagation
/// (`C == 7`).
#[error("RODAS4 does not support state transition matrix propagation")]
RODAS4NoSTM,

/// Gauss-Jackson 8 does not support state transition matrix
/// propagation (`C == 7`).
#[error("Gauss-Jackson 8 does not support state transition matrix propagation")]
GaussJackson8NoSTM,

// -- precomputed.rs --------------------------------------------------
/// Returned by [`Precomputed::interp`](crate::orbitprop::Precomputed::interp)
/// when the requested time falls outside the precomputed range.
#[error(
"Precomputed::interp: time {time} is outside of precomputed range : {begin} to {end}"
)]
PrecomputedOutOfRange {
time: String,
begin: String,
end: String,
},

/// Wraps an error surfaced while building a
/// [`Precomputed`](crate::orbitprop::Precomputed) interp table.
/// Currently captures stringified errors from the still-anyhow
/// `jplephem` module; will become a typed variant when that module
/// is migrated in Phase 3.
#[error("Cannot compute precomputed interpolation data: {0}")]
Precompute(String),

// -- satstate.rs -----------------------------------------------------
/// Returned by [`SatState::set_pos_uncertainty`](crate::orbitprop::SatState::set_pos_uncertainty),
/// [`SatState::set_vel_uncertainty`](crate::orbitprop::SatState::set_vel_uncertainty),
/// and the internal `cov_frame_to_gcrf` helper when the supplied
/// frame is not one of the supported orbital or inertial frames.
#[error("Unsupported frame for uncertainty: {frame}. Must be GCRF, LVLH, RIC, or NTW")]
UnsupportedUncertaintyFrame { frame: Frame },

// -- settings.rs -----------------------------------------------------
/// Returned by [`PropSettings::set_gravity`](crate::orbitprop::PropSettings::set_gravity)
/// when `order > degree`.
#[error("Gravity order ({order}) must be ≤ degree ({degree})")]
InvalidGravityOrder { order: u16, degree: u16 },
}

impl From<ode::OdeError> for Error {
fn from(e: ode::OdeError) -> Self {
Self::OdeError(e)
}
}

/// Convenient type alias used throughout the `orbitprop` module.
pub type Result<T> = std::result::Result<T, Error>;
2 changes: 2 additions & 0 deletions src/orbitprop/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod error;
mod precomputed;
pub mod propagator;
mod satproperties;
Expand All @@ -13,6 +14,7 @@ pub mod thrust;
/// ODE integrators specific to orbit propagation
pub mod ode;

pub use error::{Error, Result};
pub use precomputed::*;
pub use propagator::*;
pub use satproperties::SatProperties;
Expand Down
19 changes: 10 additions & 9 deletions src/orbitprop/precomputed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::SolarSystem;

pub type InterpType = (Quaternion, Vector3, Vector3);

use anyhow::Result;
use super::error::{Error, Result};
#[derive(Debug, Clone)]
pub struct Precomputed {
pub begin: Instant,
Expand Down Expand Up @@ -83,8 +83,10 @@ impl Precomputed {
for idx in 0..nsteps {
let t = pbegin + Duration::from_seconds((idx as f64) * step);
let q = qgcrf2itrf_approx(&t);
let psun = jplephem::geocentric_pos(SolarSystem::Sun, &t)?;
let pmoon = jplephem::geocentric_pos(SolarSystem::Moon, &t)?;
let psun = jplephem::geocentric_pos(SolarSystem::Sun, &t)
.map_err(|e| Error::Precompute(e.to_string()))?;
let pmoon = jplephem::geocentric_pos(SolarSystem::Moon, &t)
.map_err(|e| Error::Precompute(e.to_string()))?;
data.push((q, psun, pmoon));
}
data
Expand All @@ -95,12 +97,11 @@ impl Precomputed {
pub fn interp<T: TimeLike>(&self, t: &T) -> Result<InterpType> {
let t = t.as_instant();
if t < self.begin || t > self.end {
anyhow::bail!(
"Precomputed::interp: time {} is outside of precomputed range : {} to {}",
t,
self.begin,
self.end
);
return Err(Error::PrecomputedOutOfRange {
time: t.to_string(),
begin: self.begin.to_string(),
end: self.end.to_string(),
});
}

let idx = (t - self.begin).as_seconds() / self.step;
Expand Down
Loading
Loading