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
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ cargo clippy --workspace -- -D warnings # lint (must pass)
cargo doc --workspace --no-deps # generate docs
```

**Ignored tests.** 23 tests in `rpo-nyx/src/validation/` are `#[ignore]`d because they
require `MetaAlmanac` ephemeris data. Run them with `cargo test -p rpo-nyx -- --ignored`;
the first run downloads SPICE kernels (outbound network required) and caches them under
the `anise` default path. Subsequent runs hit the cache.

## Coding Standards

### General
Expand Down Expand Up @@ -68,7 +73,7 @@ Astrodynamics code must cite source equations (Koenig, D'Amico). Cross-check aga

The dependency graph is `rpo-core` <- `rpo-cli` and `rpo-core` <- `rpo-api`. The two binaries never depend on each other.

Shared orchestration lives in `rpo-core/src/pipeline/`. Both the CLI and API call `execute_mission()`, `compute_transfer()`, and `replan_mission()` from this module.
Shared orchestration primitives (`execute_mission_from_transfer`, `replan_from_transfer`) live in `rpo-core/src/pipeline/`. The server-side Lambert-inclusive wrappers (`execute_mission`, `compute_transfer`) live in `rpo-nyx/src/pipeline/`; the CLI and API call them.

## How to Add a New CLI Command

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ rpo-nyx (AGPL-3.0) <-- rpo-cli (AGPL-3.0)

```bash
cargo build # build workspace
cargo test # 621 tests across 5 crates (see Testing below)
cargo test # 625 tests across 5 crates (see Testing below)
```

Run an example mission (CLI):
Expand Down Expand Up @@ -152,7 +152,7 @@ cargo run -p rpo-cli -- validate --input examples/validate.json --auto-drag # J

Velocity error stays under 40 mm/s (no-drag) and 55 mm/s (auto-drag) across the same run. Per-leg growth under drag: position RMS grows roughly 3× from leg 1 to leg 3 (41 m → 92 m → 138 m) as DMF linearization accumulates — a known linear-regime characteristic, not a bug.

**Test-suite gates (conservative pass/fail with ~10× margin over observed).** The regression tests enforce `FULL_PHYSICS_SINGLE_LEG_POS_TOL_KM = 500 m`, `DRAG_STM_VS_NYX_POS_TOL_KM = 1 km`, and `FULL_PHYSICS_MULTI_LEG_POS_TOL_KM = 3 km` (all in `rpo-nyx/src/validation/trajectory.rs`); eclipse gates are `SUN_DIRECTION_VALIDATION_TOL_RAD = 3.5e-4 rad` (≈ 0.02°) and `ECLIPSE_TIMING_VALIDATION_TOL_S = 120 s` (`rpo-core/src/constants.rs`).
**Test-suite gates (conservative pass/fail with ~10× margin over observed).** The regression tests enforce `FULL_PHYSICS_SINGLE_LEG_POS_TOL_KM = 500 m`, `DRAG_STM_VS_NYX_POS_TOL_KM = 1 km`, and `FULL_PHYSICS_MULTI_LEG_POS_TOL_KM = 3 km` (all in `rpo-nyx/src/validation/trajectory/pipeline.rs`); eclipse gates are `SUN_DIRECTION_VALIDATION_TOL_RAD = 3.5e-4 rad` (≈ 0.02°) and `ECLIPSE_TIMING_VALIDATION_TOL_S = 120 s` (`rpo-core/src/constants.rs`).

**Per-component ROE validation against Koenig Table 4 Case 1.** The J2 STM is independently validated against the per-component error bounds in Koenig et al. (2017), Table 4 Case 1. Each quasi-nonsingular ROE component (`δa`, `δλ`, `δex`, `δey`, `δix`, `δiy`) is bounded within ~10× of the published errors — e.g. `KOENIG_T4C1_DA_BOUND_M = 385 m`, `KOENIG_T4C1_DIX_BOUND_M = 9 m`. See the `koenig_table4_j2_stm_accuracy_case1` test in `rpo-nyx/tests/regression_tests.rs`.

Expand Down Expand Up @@ -260,7 +260,7 @@ The CLI provides batch execution and shell-composable plumbing for scripting. Th

## Testing

621 tests across 5 crates (363 rpo-core, 132 rpo-nyx, 77 rpo-cli, 36 rpo-wasm, 13 rpo-api). 32 full-physics tests are `#[ignore]` by default (require ANISE kernels, ~50 MB cached download).
625 tests across 5 crates (363 rpo-core, 136 rpo-nyx, 77 rpo-cli, 36 rpo-wasm, 13 rpo-api). 32 full-physics tests are `#[ignore]` by default (require ANISE kernels, ~50 MB cached download).

```bash
cargo test # full suite (5 crates)
Expand Down
4 changes: 2 additions & 2 deletions rpo-core/src/pipeline/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! These compose the core planning primitives (classify, waypoint
//! targeting, covariance, eclipse) into a complete mission pipeline.
//! Server-only wrappers (`compute_transfer`, `execute_mission`, `replan_mission`)
//! Server-only wrappers (`compute_transfer`, `execute_mission`)
//! that require nyx-space live in `rpo-nyx`.

use crate::constants::DEFAULT_COVARIANCE_SAMPLES_PER_LEG;
Expand Down Expand Up @@ -423,7 +423,7 @@ pub fn apply_perch_enrichment(
/// replans from that waypoint onward.
///
/// This is the waypoint-level analog of [`apply_perch_enrichment`] +
/// `replan_mission`: the user sees the enriched suggestion, accepts it,
/// `replan_from_transfer`: the user sees the enriched suggestion, accepts it,
/// and the system replans with the enriched target.
///
/// # Arguments
Expand Down
2 changes: 1 addition & 1 deletion rpo-core/src/pipeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Owns the canonical input/output types and the mission planning entry
//! points. Entry points ([`execute_mission_from_transfer`],
//! [`replan_from_transfer`]) accept a pre-computed [`TransferResult`].
//! Server-only wrappers (`compute_transfer`, `execute_mission`, `replan_mission`)
//! Server-only wrappers (`compute_transfer`, `execute_mission`)
//! that require nyx-space live in `rpo-nyx`.
//!
//! ## DAG position
Expand Down
2 changes: 1 addition & 1 deletion rpo-core/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//!
//! Entry points (`execute_mission_from_transfer`, `replan_from_transfer`)
//! accept a pre-computed `TransferResult`. Server-only wrappers
//! (`execute_mission`, `replan_mission`, `plan_mission`) that require
//! (`execute_mission`, `plan_mission`) that require
//! nyx-space live in `rpo-nyx`.

// Core domain types
Expand Down
12 changes: 8 additions & 4 deletions rpo-nyx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@
#![allow(clippy::module_name_repetitions)]
#![warn(missing_docs)]

pub mod lambert;
pub mod monte_carlo;
// Primitives -- direct nyx-space wrappers, no internal dependencies.
pub mod nyx_bridge;
pub mod pipeline;
pub(crate) mod planning;
pub mod lambert;

// Utilities -- internal support, used by orchestration layers.
pub(crate) mod statistics;

// Orchestration -- composes primitives + utilities into higher-level flows.
pub mod monte_carlo;
pub mod validation;
pub mod pipeline;
65 changes: 65 additions & 0 deletions rpo-nyx/src/monte_carlo/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! Monte Carlo error type and `From` adapters.

use std::fmt;

use rpo_core::mission::monte_carlo::MonteCarloError as CoreMonteCarloError;

/// Errors from Monte Carlo ensemble analysis (nyx-backed).
///
/// Wraps the core analytical error and adds nyx-specific failure modes.
#[derive(Debug)]
pub enum MonteCarloError {
/// Error from the analytical engine.
Core(CoreMonteCarloError),
/// Nyx bridge failure. Boxed because `NyxBridgeError` is significantly
/// larger than `CoreMonteCarloError`; the box keeps the enum compact.
NyxBridge(Box<crate::nyx_bridge::NyxBridgeError>),
}

impl fmt::Display for MonteCarloError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Core(e) => write!(f, "{e}"),
Self::NyxBridge(e) => write!(f, "nyx bridge failure: {e}"),
}
}
}

impl std::error::Error for MonteCarloError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Core(e) => Some(e),
Self::NyxBridge(e) => Some(e.as_ref()),
}
}
}

impl From<CoreMonteCarloError> for MonteCarloError {
fn from(e: CoreMonteCarloError) -> Self {
Self::Core(e)
}
}

impl From<crate::nyx_bridge::NyxBridgeError> for MonteCarloError {
fn from(e: crate::nyx_bridge::NyxBridgeError) -> Self {
Self::NyxBridge(Box::new(e))
}
}

impl From<rpo_core::elements::eci_ric_dcm::DcmError> for MonteCarloError {
fn from(e: rpo_core::elements::eci_ric_dcm::DcmError) -> Self {
Self::Core(CoreMonteCarloError::from(e))
}
}

impl From<rpo_core::mission::errors::MissionError> for MonteCarloError {
fn from(e: rpo_core::mission::errors::MissionError) -> Self {
Self::Core(CoreMonteCarloError::from(e))
}
}

impl From<rpo_core::propagation::propagator::PropagationError> for MonteCarloError {
fn from(e: rpo_core::propagation::propagator::PropagationError) -> Self {
Self::Core(CoreMonteCarloError::from(e))
}
}
Loading