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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ process_path = "0.1.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
quick-xml = { version = "0.38", features = ["serialize"], optional = true }
anyhow = "1"
chrono = { version = "0.4", optional = true }

[dev-dependencies]
anyhow = "1"
rand = "0.9.2"
approx = "0.5"
rand_distr = "0.5.1"
Expand Down
3 changes: 2 additions & 1 deletion python/src/mod_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ fn update_datafiles(kwds: Option<&Bound<'_, PyDict>>) -> Result<()> {
},
};

satkit::utils::update_datafiles(datadir, overwrite_files)
satkit::utils::update_datafiles(datadir, overwrite_files)?;
Ok(())
}

/// Get directory where astronomy data is stored
Expand Down
6 changes: 3 additions & 3 deletions python/src/pysgp4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,12 +389,12 @@ pub fn sgp4(
let tmarray = time.to_time_vec()?;
let results: Vec<psgp4::SGP4State> = plist
.iter()
.map(|item| {
.map(|item| -> Result<psgp4::SGP4State> {
if item.is_instance_of::<PyTLE>() {
let mut stle: PyRefMut<PyTLE> = item.extract().map_err(|e| {
pyo3::exceptions::PyValueError::new_err(format!("Invalid TLE: {}", e))
})?;
psgp4::sgp4(&mut stle.0, tmarray.as_slice())
Ok(psgp4::sgp4(&mut stle.0, tmarray.as_slice())?)
} else if item.is_instance_of::<PyDict>() {
let dict: &Bound<'_, PyDict> = item.cast().map_err(|e| {
pyo3::exceptions::PyValueError::new_err(format!(
Expand All @@ -403,7 +403,7 @@ pub fn sgp4(
))
})?;
let mut omm = omm_from_pydict(dict)?;
psgp4::sgp4(&mut omm, tmarray.as_slice())
Ok(psgp4::sgp4(&mut omm, tmarray.as_slice())?)
} else {
bail!("Invalid TLE in list");
}
Expand Down
12 changes: 2 additions & 10 deletions src/earth_orientation_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,8 @@ pub enum Error {
#[error(transparent)]
Datadir(#[from] crate::utils::datadir::Error),

/// Wraps an [`anyhow::Error`] surfaced by the (still-anyhow) download
/// helpers in [`crate::utils::download`].
#[error("Download failed: {0}")]
Download(anyhow::Error),
}

impl From<anyhow::Error> for Error {
fn from(e: anyhow::Error) -> Self {
Self::Download(e)
}
#[error(transparent)]
Download(#[from] crate::utils::download::Error),
}

/// Convenient type alias used throughout the
Expand Down
12 changes: 2 additions & 10 deletions src/earthgravity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,8 @@ pub enum Error {
#[error(transparent)]
Datadir(#[from] crate::utils::datadir::Error),

/// Wraps an [`anyhow::Error`] surfaced by the (still-anyhow) download
/// helpers in [`crate::utils::download`].
#[error("Download failed: {0}")]
Download(anyhow::Error),
}

impl From<anyhow::Error> for Error {
fn from(e: anyhow::Error) -> Self {
Self::Download(e)
}
#[error(transparent)]
Download(#[from] crate::utils::download::Error),
}

/// Convenient type alias used throughout the `earthgravity` module.
Expand Down
80 changes: 80 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//! Top-level error type for the satkit crate.
//!
//! Most functions return module-scoped error types
//! (e.g. [`tle::Error`](crate::tle::Error),
//! [`orbitprop::Error`](crate::orbitprop::Error)). For downstream apps
//! that consume multiple modules and don't want to define their own
//! outer error, this façade has `From` impls for every public module
//! error and can be used as a single result type.
//!
//! ```rust,ignore
//! fn do_thing() -> Result<(), satkit::Error> {
//! let tle = satkit::TLE::from_url(url)?; // tle::Error
//! let states = satkit::orbitprop::propagate(...)?; // orbitprop::Error
//! Ok(())
//! }
//! ```

use thiserror::Error;

/// Top-level satkit error covering every public module-scoped error.
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Tle(#[from] crate::tle::Error),

#[error(transparent)]
Omm(#[from] crate::omm::Error),

#[error(transparent)]
Frames(#[from] crate::frames::Error),

#[error(transparent)]
Itrfcoord(#[from] crate::itrfcoord::Error),

#[error(transparent)]
Orbitprop(#[from] crate::orbitprop::Error),

#[error(transparent)]
Time(#[from] crate::time::InstantError),

#[error(transparent)]
Kepler(#[from] crate::kepler::Error),

#[error(transparent)]
Sgp4(#[from] crate::sgp4::Error),

#[error(transparent)]
Frametransform(#[from] crate::frametransform::Error),

#[error(transparent)]
SpaceWeather(#[from] crate::spaceweather::Error),

#[error(transparent)]
SolarCycleForecast(#[from] crate::solar_cycle_forecast::Error),

#[error(transparent)]
EarthOrientationParams(#[from] crate::earth_orientation_params::Error),

#[error(transparent)]
JplEphem(#[from] crate::jplephem::Error),

#[error(transparent)]
EarthGravity(#[from] crate::earthgravity::Error),

#[error(transparent)]
LpEphem(#[from] crate::lpephem::Error),

#[error(transparent)]
Datadir(#[from] crate::utils::datadir::Error),

#[error(transparent)]
Download(#[from] crate::utils::download::Error),

#[cfg(feature = "download")]
#[error(transparent)]
UpdateData(#[from] crate::utils::update_data::Error),
}

/// Convenient type alias used throughout satkit.
pub type Result<T> = std::result::Result<T, Error>;
52 changes: 52 additions & 0 deletions src/frametransform/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! Errors produced by the `frametransform` module.

use std::num::{ParseFloatError, ParseIntError};

use thiserror::Error;

use crate::Frame;

/// Errors produced by the
/// [`frametransform`](crate::frametransform) module.
#[derive(Debug, Error)]
pub enum Error {
/// [`to_gcrf`](super::to_gcrf) and [`from_gcrf`](super::from_gcrf)
/// only build rotation matrices for satellite-local orbital frames
/// (`GCRF`, `LVLH`, `RTN`, `NTW`). Time-dependent inertial /
/// Earth-fixed frames must use the dedicated quaternion helpers
/// ([`qitrf2gcrf`](super::qitrf2gcrf),
/// [`qteme2gcrf`](super::qteme2gcrf), …).
#[error(
"to_gcrf: frame {frame} is not a satellite-local orbital frame; use the \
time-based quaternion helpers (qitrf2gcrf, qteme2gcrf, etc.) instead"
)]
UnsupportedFrame { frame: Frame },

/// A `j = N` table-definition line in an IERS table file is
/// malformed.
#[error("Error parsing file {fname}, invalid table definition line")]
InvalidIersTableDef { fname: String },

/// Encountered a coefficient row in an IERS table file before any
/// table dimension was declared.
#[error("Error parsing file {fname}, table not initialized")]
IersTableNotInitialized { fname: String },

#[error(transparent)]
Io(#[from] std::io::Error),

#[error(transparent)]
ParseInt(#[from] ParseIntError),

#[error(transparent)]
ParseFloat(#[from] ParseFloatError),

#[error(transparent)]
Datadir(#[from] crate::utils::datadir::Error),

#[error(transparent)]
Download(#[from] crate::utils::download::Error),
}

/// Convenient type alias used throughout the `frametransform` module.
pub type Result<T> = std::result::Result<T, Error>;
13 changes: 7 additions & 6 deletions src/frametransform/ierstable.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::utils::{self, download_if_not_exist};

use anyhow::Result;
use super::{Error, Result};

use crate::mathtypes::*;

Expand Down Expand Up @@ -54,17 +54,18 @@ impl IERSTable {
let s: Vec<&str> = tline.split_whitespace().collect();
let tsize: usize = s[s.len() - 1].parse().unwrap_or(0);
if !(0..=5).contains(&tnum) || tsize == 0 {
anyhow::bail!(
"Error parsing file {}, invalid table definition line",
fname
);
return Err(Error::InvalidIersTableDef {
fname: fname.to_string(),
});
}
table.data[tnum as usize] = DMatrix::<f64>::zeros(tsize, 17);
rowcnt = 0;
continue;
} else if tnum >= 0 {
if table.data[tnum as usize].ncols() < 17 {
anyhow::bail!("Error parsing file {}, table not initialized", fname);
return Err(Error::IersTableNotInitialized {
fname: fname.to_string(),
});
}
let vals: Vec<f64> = tline.split_whitespace().map(|x| x.parse().unwrap()).collect();
for (c, &val) in vals.iter().enumerate() {
Expand Down
13 changes: 6 additions & 7 deletions src/frametransform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
//! - **CIRS** (Celestial Intermediate Reference System): IERS 2010 intermediate frame
//! - **MOD** (Mean of Date): Precession-only frame

mod error;
mod ierstable;
mod qcirs2gcrs;

pub use error::{Error, Result};

use crate::{TimeLike, TimeScale};
use std::f64::consts::PI;

Expand Down Expand Up @@ -818,7 +821,7 @@ pub fn to_gcrf(
frame: crate::Frame,
pos_gcrf: &Vector3,
vel_gcrf: &Vector3,
) -> anyhow::Result<Matrix3> {
) -> Result<Matrix3> {
use crate::Frame;
match frame {
Frame::GCRF => Ok(Matrix3::eye()),
Expand All @@ -830,11 +833,7 @@ pub fn to_gcrf(
| Frame::CIRS
| Frame::TEME
| Frame::EME2000
| Frame::ICRF => anyhow::bail!(
"to_gcrf: frame {} is not a satellite-local orbital frame; use the \
time-based quaternion helpers (qitrf2gcrf, qteme2gcrf, etc.) instead",
frame
),
| Frame::ICRF => Err(Error::UnsupportedFrame { frame }),
}
}

Expand All @@ -847,7 +846,7 @@ pub fn from_gcrf(
frame: crate::Frame,
pos_gcrf: &Vector3,
vel_gcrf: &Vector3,
) -> anyhow::Result<Matrix3> {
) -> Result<Matrix3> {
Ok(to_gcrf(frame, pos_gcrf, vel_gcrf)?.transpose())
}

Expand Down
12 changes: 2 additions & 10 deletions src/jplephem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,8 @@ pub enum Error {
#[error(transparent)]
Datadir(#[from] crate::utils::datadir::Error),

/// Wraps an [`anyhow::Error`] surfaced by the (still-anyhow) download
/// helpers in [`crate::utils::download`].
#[error("Download failed: {0}")]
Download(anyhow::Error),
}

impl From<anyhow::Error> for Error {
fn from(e: anyhow::Error) -> Self {
Self::Download(e)
}
#[error(transparent)]
Download(#[from] crate::utils::download::Error),
}

/// Convenient type alias used throughout the `jplephem` module.
Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,11 @@ pub mod omm;

// Time and duration
mod time;
pub use time::{Duration, Instant, TimeLike, TimeScale, Weekday};
pub use time::{Duration, Instant, InstantError, TimeLike, TimeScale, Weekday};

// Top-level façade error type
mod error;
pub use error::{Error, Result};

// Core types available at crate level
pub use frames::Frame;
Expand Down
16 changes: 10 additions & 6 deletions src/omm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ impl OMM {
/// let omms = OMM::from_json_string(json)?;
/// assert_eq!(omms.len(), 1);
/// assert_eq!(omms[0].object_id, "1998-067A");
/// # Ok::<(), anyhow::Error>(())
/// # Ok::<(), satkit::omm::Error>(())
/// ```
///
/// # Errors
Expand All @@ -357,7 +357,7 @@ impl OMM {
///
/// let omms = OMM::from_json_file("/path/to/omm.json")?;
/// println!("Loaded {} OMM records", omms.len());
/// # Ok::<(), anyhow::Error>(())
/// # Ok::<(), satkit::omm::Error>(())
/// ```
///
/// # Errors
Expand Down Expand Up @@ -423,24 +423,28 @@ impl SGP4Source for OMM {
&mut self.satrec
}

fn sgp4_init_args(&self) -> anyhow::Result<SGP4InitArgs> {
fn sgp4_init_args(&self) -> crate::sgp4::Result<SGP4InitArgs> {
use std::f64::consts::PI;

const TWOPI: f64 = PI * 2.0;

if let Some(theory) = &self.mean_element_theory {
if !theory.trim().eq_ignore_ascii_case("SGP4") {
return Err(Error::UnsupportedMeanElementTheory(theory.clone()).into());
return Err(crate::sgp4::Error::source(
Error::UnsupportedMeanElementTheory(theory.clone()),
));
}
}

if let Some(ts) = &self.time_system {
if !ts.trim().eq_ignore_ascii_case("UTC") {
return Err(Error::UnsupportedTimeSystem(ts.clone()).into());
return Err(crate::sgp4::Error::source(Error::UnsupportedTimeSystem(
ts.clone(),
)));
}
}

let epoch = self.epoch_instant()?;
let epoch = self.epoch_instant().map_err(crate::sgp4::Error::source)?;

Ok(SGP4InitArgs {
jdsatepoch: epoch.as_jd_with_scale(TimeScale::UTC),
Expand Down
2 changes: 1 addition & 1 deletion src/omm/xml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ impl OMM {
/// assert_eq!(omms.len(), 1);
/// assert_eq!(omms[0].object_id, "1998-067A");
/// # }
/// # Ok::<(), anyhow::Error>(())
/// # Ok::<(), satkit::omm::Error>(())
/// ```
///
/// # Errors
Expand Down
Loading
Loading