Introduction
This is the developer manual for RMS-NAV, the spacecraft image-navigation system
distributed as the rms-nav PyPI package. The user manual lives under
User Guide (one chapter per CLI driver); this manual is for contributors and
maintainers — people who modify, extend, build, test, or release the package.
Package overview
RMS-NAV ingests images from Cassini ISS, Voyager 1/2 ISS, Galileo SSI, and New Horizons LORRI; predicts the per-pixel scene geometry from SPICE kernels; and reports the offset between the predicted and observed scene that brings the two into alignment. Downstream the same package generates per-pixel backplanes, PDS4 bundles, body / ring mosaics, and reprojections.
The runtime is pure Python (3.11+), with NumPy / SciPy for numerics, oops for
geometry and SPICE access, starcat / psfmodel for star navigation, filecache
for transparent local-or-remote file access, and PyQt6 for the optional manual-nav and
mosaic-viewer GUIs. No GPU is required.
Repository layout
The repo is a standard src-layout Python package:
rms-nav/
pyproject.toml # PEP 621 metadata + tool config (ruff/mypy/pytest)
README.md # PyPI-facing summary
CONTRIBUTING.md # contributor checklist
scripts/
run-all-checks.sh # parallel wrapper: ruff + mypy + pytest + sphinx + pymarkdown
src/
nav/ # importable top-level package (`import nav`)
config/ # YAML loader + DEFAULT_CONFIG + logger + path helpers
config_files/ # bundled YAML defaults (numeric-prefix load order)
dataset/ # DataSet + per-mission subclasses (file enumeration)
obs/ # Obs / ObsSnapshot / ObsInst / per-instrument
feature/ # NavFeature dataclass + geometry / flags / reliability
nav_model/ # NavModel + per-family subpackages (stars, rings, ...)
nav_technique/ # NavTechnique + per-technique subclasses + dt_fitting
nav_orchestrator/ # NavOrchestrator + ensemble + curator + classifier
annotation/ # Annotation / Annotations (summary-PNG overlay)
reproj/ # BodyMosaic, RingMosaic, cartographic_model
sim/ # simulated-image renderer
support/ # cross-cutting helpers (filters, types, file, time, ...)
ui/ # PyQt6 widgets (manual nav dialog, mosaic viewer)
navigate_image_files.py # top-level driver called by nav_offset
backplanes/ # per-pixel geometry products (driven by nav_backplanes)
pds4/ # PDS4 bundle generation (driven by nav_create_bundle)
reproj_cli/ # CLI-only helpers shared by mosaic drivers
main/ # CLI entry-point dispatch modules
tests/
nav/ # unit tests, mirror src/nav layout
integration/ # operator-curated image library + regression tests
docs/
user_guide/ # user manual chapters
dev_guide/ # this manual
api_reference/ # autodoc landing pages
conf.py # Sphinx config
The src/nav/ tree is the importable public package; everything else under
src/ is supporting code (backplanes, pds4, reproj_cli, main).
Install
git clone https://github.com/SETI/rms-nav.git
cd rms-nav
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
Editable install of runtime + dev tools (ruff, mypy, pytest, coverage):
pip install -e ".[dev]"
The [dev] group transitively includes the docs group (Sphinx, myst-parser,
sphinx-rtd-theme, sphinxcontrib-mermaid). The runtime-only install
(pip install rms-nav) is what end users get from PyPI.
Editable installs + mypy: export
SETUPTOOLS_ENABLE_FEATURES=legacy-editable if mypy cannot find the nav
package.
Environment variables
Most runs need at least one of these (CLI flags override env vars override config
defaults). Each accepts a local path or a URL handled transparently by filecache:
SPICE_PATH— required by every real navigation run; points at the SPICE kernels directory.PDS3_HOLDINGS_DIR— root for PDS3 holdings (defaulthttps://pds-rings.seti.org/holdings).PDS4_HOLDINGS_DIR— root for PDS4 holdings.OOPS_RESOURCES— root for theoopsresources bundle.UCAC4_PATH/YBSC_PATH— star-catalog roots used byNavModelStars.NAV_RESULTS_ROOT— output directory for_metadata.jsonand_summary.pngfiles written bynav_offset.
Running the CLI tools
Every CLI listed in [project.scripts] is installed onto $PATH by
pip install. The full set:
Script |
Purpose |
|---|---|
|
Run autonomous navigation on one image (or a batch); writes JSON metadata + summary PNG. Documented at Image Navigation. |
|
Queue-driven navigation variant; reads task JSON, processes one batch. |
|
Generate per-pixel backplanes from a navigated image. See Backplane Generation and Backplanes. |
|
PyQt6 viewer for a backplane file. |
|
Build a PDS4 bundle from navigated images + backplanes. See PDS4 Bundle Generation and PDS4 Bundle Generation. |
|
Reprojection drivers (rings, body, dispatcher). See Reprojection Mosaicing and Reprojection Internals. |
|
PyQt6 mosaic viewer. |
|
Queue-driven mosaic builder. |
|
Render a synthetic test image (operator-supplied bodies / rings / stars). See The Image Simulator. |
Quick smoke test:
nav_offset coiss N1234567890 --dry-run
Running the test suite
Pytest defaults exclude integration tests; pass -m "" (empty marker filter) to
include them. pyproject.toml sets addopts = ["-m", "not integration"].
pytest # unit suite (fast)
pytest -m "" # full suite (incl. integration)
pytest -m integration # only integration
pytest -n auto --dist=loadfile # parallel (matches CI)
pytest tests/nav/reproj/test_bodies.py # one file
pytest tests/nav/reproj/test_bodies.py::test_foo # one test
pytest --cov # with coverage
pytest-xdist must run with --dist=loadfile — the default scheduling
crashes PyQt6 workers when tests from one file split across processes.
The integration suite pulls real images from the holdings URLs and uses the
operator-curated regression library at tests/integration/image_library/.
It needs the env vars listed above (in particular PDS3_HOLDINGS_DIR,
UCAC4_PATH, YBSC_PATH, OOPS_RESOURCES) plus a SPICE_PATH.
scripts/run-all-checks.sh -i (or --integration) flips the marker for the
all-checks runner.
Test markers and layout
Unit tests live under
tests/nav/and mirrorsrc/nav/directory by directory (tests/nav/reproj/test_bodies.py↔src/nav/reproj/bodies.py).Integration tests live under
tests/integration/and carry theintegrationmarker. Two sub-layers: the structural-invariants test (fast, no holdings needed) and the per-image regression test (slow, needs holdings).The image library (operator-curated regression cohort) lives at
tests/integration/image_library/. See Image Library for the calibration workflow, sidecar schema, and baseline tooling.
Lint, types, formatter
ruff check src tests # lint
ruff format --check src tests # format check (CI uses --check; local: drop --check)
mypy src tests # type-check (strict mode is on)
pymarkdown scan docs/ .cursor/ README.md CONTRIBUTING.md # markdown lint
Project conventions: line length 100, single-quoted strings, three-group import
order (stdlib / third-party / local), Google-style docstrings with
Parameters: (not Args:) wrapped at 90 chars, at most three positional
parameters per function (rest keyword-only after *), modules under 1000 lines,
no module-level # type: ignore without a specific error code. See
Best Practices for the full list and the rationale.
The all-in-one wrapper:
./scripts/run-all-checks.sh # ruff + mypy + pytest + sphinx + pymarkdown, parallel
./scripts/run-all-checks.sh -i # same, but include integration tests
Building the documentation
Documentation source is reStructuredText under docs/ plus Markdown via
myst-parser. Sphinx config lives at docs/conf.py. Build with:
pip install -e ".[docs]" # or [dev]
sphinx-build -W -b html docs docs/_build
The -W flag promotes warnings to errors; CI runs the same. The built HTML
opens at docs/_build/index.html. Per-format alternates are available from
the docs/ directory’s Makefile (make latexpdf, make singlehtml,
make epub).
Mermaid diagrams render via sphinxcontrib-mermaid. Validate complex
diagrams in the Mermaid Live Editor (https://mermaid.live/) before committing.
The autodoc API pages under docs/api_reference/ are populated from
docstrings in the source tree; new modules require a corresponding entry under
docs/api_reference/ so they appear in the rendered API surface.
CI / CD pipeline
GitHub Actions defines three workflows under .github/workflows/:
run-tests.yml— runs on every PR and on pushes tomain, plus a weekly cron. Three jobs:Lint — Python 3.13.
ruff check,ruff format --check,mypy.Test — matrix across Python 3.11 / 3.12 / 3.13 / 3.14 on Ubuntu. Runs
pytest -m "not integration" -n auto --dist=loadfilewith the holdings / catalog env vars exported. Uploads coverage to Codecov from the 3.13 job on pushes (PRs from forks are excluded for secrets reasons).Docs —
sphinx-build -Wagainst the[docs]extra.
Integration tests are skipped in CI (slow, holdings-dependent). Maintainers run them locally before merging anything that could plausibly regress navigation accuracy.
publish_to_test_pypi.yml— runs on pushes tomainonce tests pass. Builds a wheel + sdist and uploads to TestPyPI under the dev version stamped bysetuptools_scm.publish_to_pypi.yml— triggered by publishing a GitHub Release onmain. Builds viapython -m buildand uploads to production PyPI using thePYPI_API_TOKENrepo secret.
Release / deployment
Versions are derived automatically from git tags by setuptools_scm; there
is no version string committed to the repo.
Cutting a release:
Land all release content on
main(squash-merged).From a clean checkout of
main, draft a GitHub Release and tag itvX.Y.Z(semver). Publishing the release triggerspublish_to_pypi.yml.Verify the new version appears on https://pypi.org/project/rms-nav/ and that
pip install rms-nav==X.Y.Zworks in a clean venv.ReadTheDocs builds the new tag automatically from the same Git ref; verify the tagged version appears in the version selector at https://rms-nav.readthedocs.io/.
Pre-release iteration uses TestPyPI (publish_to_test_pypi.yml); install
from there with
pip install --index-url https://test.pypi.org/simple/ rms-nav.
Contribution workflow
The contributor checklist is at Contributing. Highlights:
Conventional Commits subjects (
feat:,fix:,docs:,refactor:,test:,perf:,ci:,chore:,style:). Subject imperative, ≤ 50 chars, no trailing period. One logical change per commit.PRs squash-merge to
main; the squash message becomes the release notes entry.Every PR must pass
./scripts/run-all-checks.shlocally and the same checks in CI.Static-data values (per-body shape, per-instrument calibration) require a citation traceable to a fetched document; see Config and Static Data.
Pointers to the rest of this manual
Navigation — how the navigation pipeline is structured and where each subsystem lives.
Reprojection Internals — body / ring mosaicing.
Backplanes — per-pixel backplane generation.
PDS4 Bundle Generation — PDS4 bundle generation.
Support Modules — config / static data / logging.
Extending the System — adding a new instrument, model, or technique.
Best Practices — coding conventions.
API Reference — autodoc API surface.
Contributing — contributor checklist.