Getting Started
This guide walks through installing ACONS, running the bundled lunar scenario, and adapting the configuration to other celestial bodies.
Prerequisites
- Python 3.11 (matching the
pyproject.tomlrequirement) - Poetry for dependency management
Install Poetry via pipx (recommended):
pip install --user pipx
pipx ensurepath
pipx install poetry
or through the official installer:
curl -sSL https://install.python-poetry.org | python3 -
Verify the installation:
poetry --version
Installation
git clone https://github.com/<your-org>/acons.git
cd acons
poetry install # create the virtual environment and install dependencies
(Optional) spawn a Poetry-managed shell:
poetry shell
Running the lunar example
The repository ships with a fully configured lunar navigation scenario. Execute the full pipeline from ingest to estimation with:
poetry run acons all --config configs/scenarios/lunar_case.yaml --run-dir outputs/lunar_static
This performs the three stages sequentially:
- Ingest – resample the satellite ephemerides onto the scenario grid and render quick-look plots.
- Simulate – synthesise one-way range and Doppler measurements, including Friis-based C/N₀, and
write the complete measurement catalogue to
measurements.json. - Estimate – run the eight-state EKF (position, velocity, clock bias/drift), produce state and covariance histories, and generate residual/error plots. Reporting automatically drops robust outliers before computing statistics.
You can drive individual stages if you only need a subset:
poetry run acons ingest --config configs/scenarios/lunar_case.yaml --run-dir outputs/lunar_static
poetry run acons simulate --config configs/scenarios/lunar_case.yaml --run-dir outputs/lunar_static
poetry run acons estimate --config configs/scenarios/lunar_case.yaml --run-dir outputs/lunar_static
Re-running a stage overwrites only that stage’s outputs, letting you quickly iterate on scenarios.
Running the Mars example
configs/scenarios/mars_case.yaml reproduces a surface user that ingests the MARCONI
constellation ephemerides from ephemeris/MARS/. These tracks are provided in the JCRF inertial
frame and the scenario’s frames block requests a JCRF → MARS-IAU conversion, so make sure the
referenced SPICE kernels include the NAIF leapseconds file plus Mars frame/orientation kernels (e.g.,
data/SPICE/mars_iau2000.tf and data/SPICE/mars_pa_de440_2000-2050.bpc).
Run the full pipeline with:
poetry run acons all --config configs/scenarios/mars_case.yaml --run-dir outputs/mars_static
Swap in ingest, simulate, or estimate if you only need a subset of the flow just like the lunar
example.
The Mars scenario ships with delayed two-way measurements disabled by default
(measurement.two_way_delay_simulate: false and estimation.delayed_twm_enabled: false). Flip
both to true (and set a delay in seconds or samples) if you want to exercise the delayed-TWM
estimator path.
If you need delayed two-way data but want to keep the standard EKF, set
measurement.two_way_delay_simulate: true and keep estimation.delayed_twm_enabled: false.
Seconds are rounded to a sample delay, and the estimator will ingest the delayed columns automatically.
Mars regression check
The regression harness uses configs/scenarios/regression_test/mars_regression.yaml, which inlines
every Mars setting (no !include fragments) so the check remains self-contained. The scenario now
exposes measurement.two_way_delay_seconds, measurement.two_way_delay_simulate, and
estimation.delayed_twm_enabled so you can toggle delayed two-way measurements without touching the
code—leave the defaults (0.0 seconds, disabled flags) for the baseline comparison. The CLI
automatically compares against ref/regression/mars_case/state_error_stats.csv and writes results to
outputs/regression/mars_case. Execute the baseline comparison with:
poetry run acons regression
The command re-runs ingest/simulate/estimate and fails if either
estimate/state_error_stats.csv or simulate/measurements.json differs from the reference. When the
comparison succeeds the temporary run directory is deleted automatically. Pass --reference <yaml>
if you ever need to point at an alternative scenario/baseline bundle.
Adapting to another body
Scenario configuration is consolidated into a single YAML/JSON file. The key sections are:
time– start/end timestamps and sampling cadence.celestial_body– human-readable label for the target body and default constants lookup.planet_shape–sphereuses the body mean radius;ellipsoiduses semimajor/semiminor axes fromconfigs/environment/constants.yamlto derive the local surface radius (overridesuser.body_radius_mwhen set).frames– source and body-fixed frames (e.g.,J2000→MOON_PA).spice.kernels– list of SPICE kernels furnished before ingest.user– geodetic location, elevation mask, and user type (surface,orbiter, oredl).measurement– carrier frequency, DLL/FLL parameters, transmitter/receiver gain patterns, RNG seed, observable list (currentlyrangeandrange_rate), anoscillatorentry with at least two Allan deviation samples (any averaging times are accepted), and asiseblock describing ODTS one-sigma errors for satellite position, velocity, clock bias, and clock drift.constellation– satellite identifiers with ephemeris file paths (CSV/SP3/OEM).estimation– initial position/velocity/clock bias/drift, process-noise settings (process_noisefor orbiters/EDL,process_noise_diagfor surface users), and 1‑σ initial covariance.
Bundled scenario YAMLs now include short comment banners ahead of each section so you can glance at
configs/scenarios/*.yaml to understand what knobs a block controls before editing it.
Scenario files may also compose smaller YAML/JSON snippets. Use !include to pull in a block
relative to the master scenario:
measurement:
transmitter: !include measurements/components/transmitter.yaml
receiver_rf: !include measurements/components/receiver.yaml
frames: !include environment/lunar/frames.yaml#frames
spice: !include environment/lunar/frames.yaml#spice
The bundled lunar_case.yaml and mars_case.yaml now split time, frames, spice, user,
constellation, measurement, and estimation across directories named environment/,
measurements/, and estimation/. Multiple scenarios can include the same fragments while keeping
each master file readable. The constellation block is now the sole place to list ephemeris files,
replacing the older orbits alias used by legacy tooling.
When initial_state.use_user_as_truth is true you can specify random_position_error_m
(e.g., 1000 m) to inject a random offset with that magnitude, optionally fixing the direction via
position_error_seed for reproducibility.
Update those sections to reflect the new body or constellation, then rerun the same acons commands.
No code changes are required as long as ephemerides and SPICE kernels are supplied.
Project layout
acon/
├── configs/
│ └── scenarios/ # <body>_case entry points + environment/estimation/measurement fragments
├── data/SPICE/ # Furnished kernels (LSK, frame, SPK…)
├── docs/ # MkDocs documentation set
├── ephemeris/ # Tabulated orbit samples (CSV)
├── outputs/ # Generated artefacts (gitignored)
├── src/
│ ├── constants.py # Shared physical/logging constants
│ ├── cli.py # CLI entry point (`poetry run acons …`)
│ ├── measurement_simulator.py # Measurement synthesis & JSON exporter
│ ├── filtering.py # Eight-state EKF
│ └── reporting.py # Plots and CSV summaries with outlier removal
└── README.md
Refer to the Outputs & Reporting page for a breakdown of stage artefacts.