v0.16.0: Gauss-Jackson 8, frame overhaul, EGM96 default, tutorial reorg#79
Merged
ssmichael1 merged 7 commits intomainfrom Apr 5, 2026
Merged
v0.16.0: Gauss-Jackson 8, frame overhaul, EGM96 default, tutorial reorg#79ssmichael1 merged 7 commits intomainfrom
ssmichael1 merged 7 commits intomainfrom
Conversation
- TLE.from_url(url): fetches plain-text TLE data and parses it - OMM.from_url(url): fetches OMM data with auto-detection of JSON vs XML - Python omm_from_url(url): returns list of dicts compatible with sgp4() - Full Python bindings, type stubs, and docstrings - Update user guide with URL loading examples, remove requests dependency from OMM example - Update API reference to include omm_from_url Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major additions on this branch: * Gauss-Jackson 8 fixed-step multistep integrator for high-precision orbit propagation, with per-step dense output and quintic Hermite interpolation. Specialised for 2nd-order ODEs via a combined GJ + Summed-Adams formulation that handles velocity-dependent forces naturally. Typically 3-10x fewer force evaluations than RKV98 for smooth long-duration propagation. Selected via Integrator::GaussJackson8 with a user-supplied gj_step_seconds. No STM support. Lives in the new satkit::orbitprop::ode submodule (integrator is astrodynamics-specific enough that it doesn't belong in numeris). * Frame::NTW (velocity-aligned: T=v_hat, W=h_hat, N=T x W). Natural for prograde/retrograde burns on eccentric orbits: a pure +T delta-v of magnitude dv adds exactly dv to |v|, while a RIC/LVLH in-track burn of the same magnitude loses 10(1-cos gamma). Compile-time Frame::RSW and Frame::RTN aliases point at Frame::RIC since the axes are identical (just different community names). * LVLH added as a supported maneuver/thrust coordinate frame (previously only valid for uncertainty). * Unified uncertainty API: SatState::set_pos_uncertainty(sigma, frame) and set_vel_uncertainty(sigma, frame) replace the four per-frame methods. Supports GCRF, LVLH, RIC, NTW. Each call preserves the 3x3 block it is not updating, so pos-then-vel builds a full 6x6 covariance (the old methods silently overwrote the whole matrix). Removed the old methods entirely. * Python/Rust API parity on frame handling: add_maneuver, set_pos_uncertainty, set_vel_uncertainty, and thrust.constant now all require an explicit frame argument from Python, matching the Rust API (no silent defaults). Added ergonomic add_prograde / add_retrograde / add_radial / add_normal helpers on satstate alongside the generic add_maneuver. * Ergonomic ImpulsiveManeuver constructors in Rust: prograde, retrograde, radial_out, normal, plus gcrf / ric / ntw for arbitrary-vector burns. * Precompute bounds fix: Precomputed::new_padded takes an explicit padding, and PropSettings::required_precompute_padding automatically extends interp-table bounds to cover the GJ8 backward startup stencil (4 * gj_step_seconds on each end). Previously failed silently for gj_step_seconds > 60. * New "Theory: Maneuver Coordinate Frames" guide (docs/guide/maneuver_frames.md) comparing GCRF/RIC/NTW/LVLH with the flight-path-angle derivation, a worked numeric example showing the 0.245 m/s discrepancy on an e=0.3 orbit, a cheat sheet, and a summary table. Wired into mkdocs nav. Other changes: * Frame derives Copy + PartialEq + Eq (strictly permissive). * PyFrame::NTW, PyIntegrator::gauss_jackson8, PyPropSettings::gj_step_seconds, and PyPropResult::gj_dense exposed via the Python bindings. * .pyi stubs updated throughout: new frame variant, new integrator variant, gj_step_seconds, unified uncertainty API, ergonomic maneuver helpers, frame docstrings corrected re: covariance conventions. * Covariance Propagation tutorial notebook simplified to use the unified API (dropped hand-rolled LVLH->GCRF rotation in favor of set_vel_uncertainty). * satprop guide integrator table and covariance / maneuver examples updated. Tests: 153 Rust + 29 Python propagation tests pass (up from 139 / 24 at branch start). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Follow-on work on the gauss_jackson branch covering canonical-name cleanup, a unified frame-transform API, tutorial overhauls, and the default gravity model switch. * Rename Frame::RIC to Frame::RTN as the canonical name (CCSDS OEM convention). Frame::RIC and Frame::RSW are kept as compile-time aliases, so Frame::RIC == Frame::RTN and existing code using either name still compiles. Python exposes frame.RIC and frame.RSW as class-level aliases of frame.RTN. * Unified frame-transform API: frametransform::to_gcrf(frame, pos, vel) and from_gcrf(frame, pos, vel) replace the combinatorial per-frame helpers. state_to_gcrf / gcrf_to_state handle the position+velocity pair with the correct TIRS-frame Earth-rotation term (omega x r_tirs) for ITRF<->GCRF. Validated against Vallado Example 3-14 in a new single-call test. * Default gravity model is now EGM96 (was JGM3). Applies to PropSettings::default() and the standalone gravity() / gravity_and_partials() Python helpers. * Coordinate Frame Transforms tutorial rewrite: explicit GCRS/ICRF and ITRS definitions, geodetic-vs-geocentric explanation, cartopy ground track overlay, time-series qgcrf2itrf_approx-vs-full error plot over 30 years. Dropped the low-value 24-hour Earth rotation section. * New API reference page: docs/api/frame.md documenting the Frame enum with all variants and aliases. Wired into mkdocs nav and docs/api/index.md. * MathJax now accepts both \(...\) / \[...\] and dollar delimiters so equations render correctly in Jupyter-notebook tutorials (previously broken in Quaternions.ipynb and others). * Python pypropsettings/pygravity default changed to egm96; .pyi stubs and docstrings updated. * Bump version to 0.16.0 (Cargo.toml, pyproject.toml) and add 0.16.0 CHANGELOG entry covering the full branch (GJ8, NTW/LVLH maneuvers, unified uncertainty API, RTN rename, EGM96 default, docs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tutorial renames (nav + filenames): * "Coordinate Frame Transforms" -> "Coordinate Frames". The tutorial is primarily a description of the frames themselves, not just the rotations between them. * "ITRF Coordinates" -> "Geodetic Coordinates". This tutorial is about the itrfcoord data type (geodetic / Cartesian / ENU / NED / geodesic distance), not the ITRF reference frame. The old name made it sound like two views of the same topic as Coordinate Frames. * mkdocs.yml nav reordered so Coordinate Frames (frame theory) comes before Geodetic Coordinates (data type built on top). * docs/tutorials/index.md updated with new titles and descriptions. * Added reciprocal cross-reference notes at the top of both tutorials so readers arriving at either one know where the other topic lives. Content expansions: * Coordinate Frames: expanded TEME section with the origin of the "True Equator, Mean Equinox" name (intentional half-and-half construction), the three practical awkwardness points (not uniquely defined, time-dependent orientation, positions cannot be compared directly), the API table, and the Vallado 2006 reference. * Coordinate Frames: added an explicit list of the intermediate and satellite-local frames (CIRS/TIRS, EME2000/ICRF, RTN/NTW/LVLH) with pointers to the maneuver frames guide. * Time Systems: added a "Why yet another time type?" section at the top covering time-scale-as-first-class, high-precision internal representation, correct leap-second handling, UT1/TDB with no extra dependencies, and the single-type-across-Rust-and-Python story. MathJax fix: * docs/javascripts/mathjax.js: remove the `ignoreHtmlClass: ".*|"` + `processHtmlClass: "arithmatex"` restriction that caused MathJax to skip notebook HTML entirely (mkdocs-jupyter does not wrap notebook-cell math in an .arithmatex span). Equations in the Quaternions notebook and all other tutorials now render. Plain markdown pages still work because pymdownx.arithmatex (generic mode) emits raw delimiters that MathJax picks up under default scanning. Test fix: * python/test/test_frames.py::TestGravity::test_gravity now explicitly passes `model=sk.gravmodel.jgm3`. The ICGEM reference values in that test are for JGM3 specifically; previously they relied on the default, which switched to EGM96 in 0.16.0. Reference-value tests should always name the model they compare against. All 20 notebooks re-executed, verified to run cleanly, and stripped of outputs (mkdocs-jupyter re-executes at build time). 81 Python tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* PropSettings.max_steps (Rust) / propsettings(max_steps=...) (Python): user-configurable maximum integrator step count before the propagator aborts with a max-steps error. Applies uniformly to adaptive RK / Rosenbrock (via numeris::ode::AdaptiveSettings::max_steps) and to Gauss-Jackson 8 (via its own settings). Default 1_000_000, which preserves the previous hard-coded GJ8 ceiling and is a loosening of the previously inherited numeris RK default of 100_000. Getter/setter and .pyi stubs (constructor kwarg, @Property pair, docstrings, default list). * release.yml check_version: verify the git tag against Cargo.toml, python/Cargo.toml, AND pyproject.toml — not just Cargo.toml as before. Previously python/Cargo.toml could drift silently if a version bump was applied by hand instead of through cargo release; that drift (0.15.1 -> 0.16.0) was caught and fixed in this branch. Any future drift hard-blocks the release workflow. * python/Cargo.toml version synced to 0.16.0 to match Cargo.toml / pyproject.toml. verified via cargo metadata cross-check. * README.md quick-start: gravity_model=sk.gravmodel.egm96 with a comment annotating the default. Previously showed jgm3 which stopped being the default in 0.16.0. * docs/guide/satprop.md: - new "Integrator step budget: max_steps" admonition under the integrator selection section - gravity model table: egm96 is now labelled (default), jgm3 demoted * docs/tutorials/High Precision Propagation.ipynb: fix stale "jgm3 (default)" comment in the settings cell, flip to egm96. * CHANGELOG: new sub-sections for the max_steps feature and the release tooling hardening, added to the existing 0.16.0 entry. Explicitly note the default = 1_000_000 rationale (matches old GJ8, loosens RK). * Display for PropSettings includes Max Steps line. 157 Rust + 41 doc-tests + 81 Python tests pass. All three version strings (Cargo.toml, python/Cargo.toml, pyproject.toml) verified at 0.16.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR delivers the 0.16.0 release: a large feature and cleanup branch built on top of the Gauss-Jackson 8 work. Major items:
Propagation
satkit::orbitprop::odesubmodule. Typically 3–10× fewer force evaluations than RKV98 for smooth long-duration propagation. Selected viaIntegrator::GaussJackson8+ a user-suppliedgj_step_seconds.max_steps—PropSettings::max_steps(Rust) /propsettings(max_steps=...)(Python). Applies uniformly to adaptive RK / Rosenbrock and to GJ8. Default1_000_000, preserving the previous hard-coded GJ8 ceiling.PropSettings::required_precompute_paddingautomatically extends interp-table bounds to cover the GJ8 backward startup stencil. Previously failed silently forgj_step_seconds > 60.Coordinate Frames
Frame::RIC→Frame::RTNcanonical rename (CCSDS OEM convention).Frame::RICandFrame::RSWkept as compile-time aliases, so existing code using either name still compiles andFrame::RIC == Frame::RTN.Frame::NTW(velocity-aligned) — natural for prograde/retrograde burns on eccentric orbits. Accepted by maneuvers, thrust, uncertainty, and frame transforms.frametransform::to_gcrf(frame, pos, vel)andfrom_gcrf(frame, pos, vel)replace the combinatorial per-frame helpers.state_to_gcrf/gcrf_to_statehandle the position+velocity pair with the correct TIRS-frame Earth-rotation term for ITRF↔GCRF. Validated against Vallado Example 3-14.Breaking changes
SatState::set_pos_uncertainty(sigma, frame)andset_vel_uncertainty(sigma, frame)replace the four per-frame methods (set_lvlh_pos_uncertainty/set_lvlh_vel_uncertainty/ etc.), which are removed. Each call preserves the 3×3 block it isn't updating, so pos-then-vel correctly builds a full 6×6 covariance — the old methods silently overwrote the whole matrix.satstate.add_maneuver,set_pos_uncertainty,set_vel_uncertainty, andthrust.constantnow all require an explicitframeargument from Python. Added ergonomic helpers:add_prograde,add_retrograde,add_radial,add_normal.PropSettings::default()and the standalonegravity()/gravity_and_partials()helpers. Sub-meter numerical difference for typical LEO propagation over a day, but the default selection changes.Documentation
itrfcoorddata type, not the frame). Nav reordered so Coordinate Frames comes before Geodetic Coordinates. Reciprocal cross-reference notes at the top of both.docs/guide/maneuver_frames.md) comparing GCRF / RTN / NTW / LVLH with the flight-path-angle derivation and a worked e=0.3 numeric example.docs/api/frame.mddocumenting the Frame enum with all aliases.ignoreHtmlClass/processHtmlClass: "arithmatex"restriction that was causing MathJax to skipmkdocs-jupyternotebook HTML entirely. Equations in the Quaternions tutorial (and others) now render.Tooling
release.yml check_versionnow verifies the tag against all three version strings (rootCargo.toml,python/Cargo.toml,pyproject.toml). Previously only the rootCargo.tomlwas checked, which allowedpython/Cargo.tomlto drift silently. That drift (0.15.1 → 0.16.0) was caught and fixed in this branch.Test plan
cargo test --release— 157 lib + 41 doc-tests passpytest python/test/— 81 Python tests pass (up from 71 at 0.15.1)mkdocs build --strict— builds cleanly; renamed tutorials render; newapi/frame.mdrendersCargo.toml,python/Cargo.toml,pyproject.tomlall at0.16.0test_gravityexplicitly pinned togravmodel.jgm3(ICGEM reference values are JGM3-specific)🤖 Generated with Claude Code