Skip to content

Release v1.7.0

Latest

Choose a tag to compare

@github-actions github-actions released this 21 Jul 23:07

Important

This release adds support for arbitrary-central-body orbit propagation, extending Brahe support beyond Earth or other planets and interplanetary trajectories. As such it contains multiple breaking changes to the NumericalOrbitPropagator class.

There are also a number of other fun improvements added with this release:

  • Native SPICE kernel support for planetary body positions and frames
  • Support for cislunar and mars reference frames and trajectories
  • Added Permenant Tide, Solid Earth Tide, and Ocean Tide force models
  • Support for conversions to/from synodic reference frames
  • API Clients for JPL Small Bodies Database and Horizons systems for SPICE kernel download
  • Datasets to download and access FK5, Hipparcos, and Tycho2 star catalogs
  • Overhaul MeanToOsculating orbital element conversions, adding support for numerical-averaging based conversions
  • Added builder-pattern constructors for major, complex classes making them easier toconstruct andcustomize
  • Review and improvement of error system in the package. Standardizing use of Result in Rust API, and improving Python errors

Added

  • Native SPICE kernel support: SPK and BPCK readers (DAF container, SPK Type 2/3, binary PCK Type 2), usable standalone or via the global registry. @duncaneddy (#376)
  • Global multi-kernel registry: load_kernel, unload_kernel, clear_kernels, loaded_kernels; kernels stay resident simultaneously with most-recently-loaded precedence and epoch-aware chain fallback. @duncaneddy (#376)
  • NAIFId enum (planets, planetary-system barycenters, major moons, Id(i32) catch-all) and FrameId enum (MoonPaDe440 = 31008 + Id(i32)); all query functions take impl Into<NAIFId> / impl Into<FrameId>, so raw integer IDs keep working. Python mirrors as NAIFId/FrameId IntEnums. @duncaneddy (#376)
  • SPICEKernel enum covering every downloadable kernel — DE (de430–de442s), JPL satellite ephemeris kernels (mar099, mar099s, jup365, sat441, ura184, nep097, plu060), and binary PCK (moon_pa_de440) — plus KernelSource for bring-your-own kernel paths. @duncaneddy (#376)
  • load_common_kernels() (de440s + moon_pa_de440, ~46 MB) and load_all_kernels() (~2.5 GB) pre-initialization helpers. @duncaneddy (#376)
  • Generic NAIF-ID ephemeris queries: spk_position, spk_velocity, spk_state (pooled, cross-kernel chaining) and kernel-scoped spk_position_from_kernel / spk_velocity_from_kernel / spk_state_from_kernel. @duncaneddy (#376)
  • Per-body kernel-backed accessors {body}_{position,velocity,state}_spice for Sun, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, and the SSB. The five outer planets return true body centers via a two-leg DE + satellite-ephemeris-kernel sum (kernel auto-downloaded on first use: mar099s ~68 MB, jup365 ~1.1 GB, sat441 ~662 MB, ura184 ~387 MB, nep097 ~105 MB); {planet}_barycenter_{position,velocity,state}_spice variants provide the planetary-system barycenter from the DE kernel alone. @duncaneddy (#376)
  • PCK orientation queries with typed attitude returns: pck_euler_angle (EulerAngle, ZXZ), pck_euler_angle_and_rates, pck_euler_rates, pck_quaternion (Quaternion), pck_rotation_matrix (RotationMatrix), plus the raw pck_euler_angles. @duncaneddy (#376)
  • Epoch::spice_et() — SPICE ephemeris time (TDB seconds past J2000) convenience accessor; Epoch::seconds_past_j2000_as_time_system for other time systems. @duncaneddy (#376)
  • datasets::naif::download_spice_kernel for any known NAIF kernel (DE, satellite ephemeris, binary PCK), exposed in Python as datasets.naif.download_spice_kernel. @duncaneddy (#376)
  • ANISE validation suite (mm-level matched-ET comparisons, network-gated lunar DCM validation) and native-vs-ANISE criterion benchmarks. @duncaneddy (#376)
  • Learn/API documentation for the spice module and four standalone examples, enabled in CI with cached kernels. @duncaneddy (#376)
  • Lunar reference frames LCI/LFPA/LFME and Mars frames MCI/MCMF with {rotation|position|state}_{from}_to_{to} transformations, cross-center state_eci_to_lci/state_eci_to_mci, and automatic moon_pa_de440 PCK loading. @duncaneddy (#377)
  • Native IAU/WGCCRE 2015 body-rotation engine (rotation_icrf_to_body_fixed_iau, iau_rotation_model_ids) with an embedded 17-body coefficient table transcribed from pck00011.tpc and validated against ANISE. @duncaneddy (#377)
  • Centralized ReferenceFrame enum and frame router (rotation_frame_to_frame, position_frame_to_frame, state_frame_to_frame) with generic NAIF-ID variants (BodyCenteredICRF, BodyFixedIAU, BodyFixedPCK) and "ECI"/"ECEF" string aliases. @duncaneddy (#377)
  • CentralBody abstraction (Earth, Moon, Mars, EMB, SSB, Custom) with from_naif_id(), and ForceModelConfig::for_body(), lunar_default() (GRGM660PRIM 50×50, Earth+Sun third bodies, Moon+Earth occultation), mars_default() (GMM-2B 50×50, exponential drag, Mars occultation), and cislunar_default() (EMB-centered) constructors. @duncaneddy (#377)
  • Barycentric (EMB/SSB) propagation with correct direct/differential third-body handling; ThirdBody gains Earth, Phobos, Deimos, and Custom { name, naif_id, gm } variants. @duncaneddy (#377)
  • Multi-body eclipse modeling via occulting_bodies on the SRP configuration and new OccultingBody type. @duncaneddy (#377)
  • Body-generic dynamics functions: accel_third_body_for_body, eclipse_conical_for_body, eclipse_cylindrical_for_body, accel_relativity_for_body, accel_drag_for_body, state_eci_to_koe_for_body. @duncaneddy (#377)
  • state_bci, state_bcbf, and state_in_frame(frame, epoch) on the orbit state-provider traits (DOrbitStateProvider/SOrbitStateProvider), implemented on the numerical, Keplerian, and SGP propagators and both trajectory types. @duncaneddy (#377)
  • OrbitFrame::BodyCenteredInertial(center): non-Earth propagator trajectories carry their center's NAIF ID, and trajectory Earth-frame conversions (point queries, batch to_*, covariance, CCSDS OEM export) re-center through the frame router; state_koe_osc returns elements about the trajectory's own center using that body's GM. @duncaneddy (#377)
  • User-defined body-fixed frames: register_custom_frame(key, rotation, omega=None) + ReferenceFrame::BodyFixedCustom{center, key} (epoch->DCM callback with optional angular-velocity callback; numeric transport-term fallback), with a runnable example. @duncaneddy (#377)
  • state_koe_to_eci_for_body (inverse of state_eci_to_koe_for_body), ReferenceFrame::ECI/ECEF aliases, kernel_is_loaded(), and the SECONDS_PER_JULIAN_CENTURY constant. @duncaneddy (#377)
  • just setup recipe for local development (uv venv, pre-commit hooks, and pre-downloading the de440s/moon_pa_de440/mar099s kernels tests expect cached). @duncaneddy (#377)
  • NAIF mar099s Mars satellite kernel support (download_satellite_kernel, load_kernel("mar099s")) for Phobos/Deimos ephemerides. @duncaneddy (#377)
  • Physical constants R_MARS, OMEGA_MARS, OMEGA_MOON, GM_PHOBOS, GM_DEIMOS. @duncaneddy (#377)
  • ForceModelConfig::validate() with clear errors for invalid central-body/force-model combinations, called automatically at propagator construction. @duncaneddy (#377)
  • Python bindings for all of the above (ReferenceFrame, CentralBody, OccultingBody, extended ThirdBody, config constructors, frame functions, state_in_frame). @duncaneddy (#377)
  • CI caching of kernel and gravity-model downloads across jobs and workflow runs, with a weekly keep-warm workflow. @duncaneddy (#377)
  • Learn/API documentation pages and runnable lunar/Mars propagation examples (Python + Rust). @duncaneddy (#377)
  • Clenshaw-summation spherical-harmonic gravity kernel (GravityModel::compute_spherical_harmonics_clenshaw, accel_gravity_spherical_harmonics_clenshaw), with serial and parallel (order-parallel, bitwise-identical) execution — Rust + Python. @duncaneddy (#378)
  • GravityModelCoefficients load configuration (Clenshaw default / Cunningham / Both) with from_model_type_with_coefficients / from_file_with_coefficients constructors (Rust + Python) and load_uncached_with_coefficients (Rust only, matching load_uncached), plus precompute_* / drop_* / has_* coefficient-set lifecycle methods — Rust + Python. @duncaneddy (#378)
  • GravityModel::get_c(n, m) / get_s(n, m) single-coefficient accessors — Rust + Python. @duncaneddy (#378)
  • accel_gravity_spherical_harmonics_cunningham convenience function — Rust + Python. @duncaneddy (#378)
  • High-precision reference tests: Clenshaw kernel pinned against independent 40-digit mpmath evaluations of EGM2008_360 to 1e-13 relative at degrees 120–200, with the generator committed as scripts/generate_clenshaw_gravity_reference.py. @duncaneddy (#378)
  • The Cunningham kernel returns a descriptive error (instead of silent NaN) on denormalized-recursion overflow. @duncaneddy (#378)
  • set_global_gravity_model Python binding (mirrors the existing Rust function). @duncaneddy (#378)
  • Clenshaw-vs-Cunningham benchmark suite in benchmarks/gravity_benchmarks.rs. @duncaneddy (#378)
  • is_empty() method on StaticEOPProvider, FileEOPProvider, and CachingEOPProvider (Python bindings), satisfying clippy's len_without_is_empty for the newly workspace-covered brahe-py crate. @duncaneddy (#380)
  • Solid Earth tide accelerations for numerical propagation per IERS Conventions (2010) TN36 §6.2.1: frequency-independent lunisolar corrections to degree-2/3 geopotential coefficients with degree-4 coupling (Eqs. 6.6–6.7, anelastic Love numbers from Table 6.3), and optional frequency-dependent corrections from Tables 6.5a/b/c via SolidTideConfig(frequency_dependent=True). @duncaneddy (#385)
  • TidesConfiguration and PermanentTideConfig (Auto/ConvertTo(system)/Off) on ForceModelConfig.tides to enable tidal corrections and control permanent-tide normalization of the static field's C̄20 (IERS TN36 §6.2.2); tides remain disabled by default in all force-model presets. @duncaneddy (#385)
  • GravityModel.convert_tide_system (Rust and Python) to convert a gravity model's C̄20 between mean-tide, zero-tide, and conventional tide-free systems. @duncaneddy (#385)
  • Rust API accel_solid_earth_tides / solid_earth_tide_coefficients in orbit_dynamics::tides for direct evaluation of tide accelerations and coefficient corrections. @duncaneddy (#385)
  • Warnings for tide configurations that double-count the permanent tide (zero-tide/mean-tide conversion combined with solid tides, or shared global gravity models): Rust warning at propagator construction, suppressible UserWarning in Python. @duncaneddy (#385)
  • Learn documentation page on tidal corrections (tide systems, permanent tide handling, solid Earth tides) and runnable Python/Rust examples: force_model_tides, tides_permanent_only, tides_static, tides_static_time_varying. @duncaneddy (#385)
  • ReferenceFrame.EMR, ReferenceFrame.SER, and ReferenceFrame.GSE synodic frames (NASA TP-20220014814) supported across the frame router (rotation/position/state_frame_to_frame, state_in_frame), with exact rotation-matrix time derivatives (GTDS/STK convention) for the velocity transform. @duncaneddy (#393)
  • Pairwise synodic transforms rotation/position/state_gcrf_to_{emr,ser,gse} and inverses in Rust and Python. @duncaneddy (#393)
  • Native SPK acceleration evaluation via analytic Chebyshev differentiation: spk_acceleration and spk_acceleration_from_kernel (Rust and Python). @duncaneddy (#393)
  • SUN_EARTH_BARYCENTER_ID synthetic frame-center ID with GM-weighted Sun-Earth barycenter resolution in the router's translation seam. @duncaneddy (#393)
  • Batch state-provider methods states_bci, states_bcbf, and states_in_frame on SOrbitStateProvider/DOrbitStateProvider and on the SGPPropagator, KeplerianPropagator, and NumericalOrbitPropagator Python classes. @duncaneddy (#393)
  • Documentation: Synodic Reference Frames Learn page and Library API page, frame/kernel-requirement table entries, and SPK acceleration API docs. @duncaneddy (#393)
  • Per-body third-body configuration: ThirdBodyConfiguration pairs each perturber with its own ephemeris_source and gravity model; SphericalHarmonic and EarthZonal fields are evaluated at the object's position relative to the perturbing body in that body's fixed frame, with a point-mass indirect term. @duncaneddy (#394)
  • Public accel_third_body_field_for_body (Rust + Python) evaluating a configured gravity model (point-mass, spherical-harmonic, or Earth-zonal) for a perturbing body about any central body. @duncaneddy (#394)
  • ThirdBody planetary-system barycenter variants MarsBarycenterNeptuneBarycenter (NAIF 4–8, system GMs), used by the default Earth force models; accel_third_body accepts both barycenter and planet-center variants (the latter auto-load their satellite-system kernels). @duncaneddy (#394)
  • DragConfiguration.body to attribute drag to a non-central body: density and relative wind are evaluated at the object's state relative to that body (enables Earth drag on EMB-centered propagations). @duncaneddy (#394)
  • GravityConfiguration::Zero — explicit no-central-gravity term for barycentric propagation centers (used by cislunar_default). @duncaneddy (#394)
  • ECI↔EMBI frame translation helpers position_eci_to_emb/position_emb_to_eci and state_eci_to_emb/state_emb_to_eci (Rust + Python). @duncaneddy (#394)
  • Constants GM_MARS_SYSTEMGM_NEPTUNE_SYSTEM and GM_PLUTO_SYSTEM (planetary-system barycentric GMs, NAIF gm_de440.tpc). @duncaneddy (#394)
  • ThirdBody::as_central_body() and ThirdBody::body_fixed_frame() helpers (Rust + Python). @duncaneddy (#394)
  • Standalone example numerical_propagation/cislunar_earth_forces (EMB-centered propagation with Earth-attributed forces). @duncaneddy (#394)
  • Epoch.to_time_system() (Rust and Python) returns a new Epoch for the same instant expressed in a different time system. The instant is unchanged, so the returned epoch compares equal to the original; only the scale it reports in differs. The method had been referenced by the Epoch::time_system documentation but was never implemented. @duncaneddy (#395)
  • TimeSystem API reference page under library_api/time/, documenting all ten time systems (GPS, TAI, TT, UTC, UT1, TDB, TCG, TCB, BDT, GST). It previously lived under constants/units.md and listed only five, so TDB, TCG, TCB, BDT, and GST did not render at all. @duncaneddy (#395)
  • "Time System" section in the Learn Epoch guide, covering how an epoch's time system is set and read, and the distinction between converting an epoch and projecting one, with runnable Rust and Python examples. @duncaneddy (#395)
  • Example fixtures covering FLAGS declared past the tenth line of a file. @duncaneddy (#395)
  • FES2004 ocean tide model (OceanTideModel, OceanTideConfig) with admittance-wave expansion per IERS TN36 §6.3, configurable degree/order up to 100, and one-time cached download of the IERS coefficient file. @duncaneddy (#397)
  • Solid Earth pole tide (SolidTideConfig.pole_tide) and ocean pole tide (OceanTideConfig.pole_tide) per IERS TN36 §6.4/§6.5 with the IERS 2018 linear secular pole model (secular_pole, wobble_parameters, solid_earth_pole_tide_deltas, ocean_pole_tide_deltas). @duncaneddy (#397)
  • TidesConfiguration.ocean field enabling ocean tides in the numerical propagator force model; ForceModelConfig::high_fidelity() now enables solid tides with frequency-dependent corrections, both pole tides, and 30×30 ocean tides. @duncaneddy (#397)
  • Python bindings for OceanTideConfig and the new pole_tide / ocean configuration fields, with regenerated type stubs. @duncaneddy (#397)
  • ReferenceFrame.Synodic(origin, primary, secondary) and SynodicOrigin for arbitrary two-body rotating frames, with synodic_origin/synodic_primary/synodic_secondary accessors (also populated for EMR/SER/GSE). @duncaneddy (#399)
  • synodic_barycenter_id and generalized synthetic-barycenter resolution in the frame router (Sun-Earth barycenter is now a special case of the generic encoding). @duncaneddy (#399)
  • Solar System Scope planet texture registry with download_planet_texture and generalized load_body_texture (CC BY 4.0, credited in the license page). @duncaneddy (#399)
  • central_body and additional_bodies parameters on plot_trajectory_3d for Moon/Mars/custom-body scenes, backed by a body-visuals registry. @duncaneddy (#399)
  • plot_synodic_3d and plot_earth_moon_rotating_3d for rotating-frame trajectory visualization. @duncaneddy (#399)
  • Worked examples: LRO lunar orbit, Earth-Moon free return, MRO Mars orbit, Dawn at Ceres (user-defined central body), with learn/API documentation for the new frames and plotting. @duncaneddy (#399)
  • Standalone Rx/Ry/Rz matrix functions. @duncaneddy (#399)
  • Planetary radius constants (R_MERCURY .. R_NEPTUNE). @duncaneddy (#399)
  • Tested learn examples for generic ReferenceFrame.Synodic frames and plot_earth_moon_rotating_3d. @duncaneddy (#399)
  • Keplerian ↔ equinoctial element conversions (state_koe_to_equinoctial, state_equinoctial_to_koe) following the Vallado (eq. 2-99) formulation with Montenbruck a, h, k, p, q, l naming and an explicit retrograde factor (Rust + Python). @duncaneddy (#403)
  • MeanElementMethod selector (BROUWER_LYDDANE analytical, numerical(config) windowed averaging) plus WindowAlignment, WindowEdgeHandling, MeanElementNumericalMethodConfig, and MeanElementInverseConfig configuration types (Rust + Python). @duncaneddy (#403)
  • Numerical osculating → mean conversion via windowed averaging in equinoctial space, with trapezoidal time-weighting of the slow elements and configurable window length (seconds), alignment (centered / trailing / leading), and edge handling (truncate / preserve-window). @duncaneddy (#403)
  • Numerical mean → osculating conversion via an iterative differential-correction inverse driven by a supplied force model and numerical propagation configuration. @duncaneddy (#403)
  • Batch conversion functions batch_state_koe_osc_to_mean and batch_state_koe_mean_to_osc returning (epoch, state) pairs, with Python bindings returning (list[Epoch], ndarray). @duncaneddy (#403)
  • FK5, Hipparcos, and Tycho-2 star catalog datasets with cached downloads, typed records, StarRecord trait, filtering, and Polars DataFrame conversion (Rust + Python). @duncaneddy (#404)
  • RA/Dec coordinate transformations: position_radec_to_inertial/position_inertial_to_radec, state_radec_to_inertial/state_inertial_to_radec, position_radec_to_azel/position_azel_to_radec. @duncaneddy (#404)
  • Rigorous proper-motion epoch propagation apply_proper_motion (delegating to IAU SOFA iauPmsafe via rsofa; theory per ESA SP-1200 §1.5.5) and StarRecord::radec_at_epoch. @duncaneddy (#404)
  • Star-field sensor simulation example with animated 3D and sensor-frame Plotly visualizations. @duncaneddy (#404)
  • datasets::ssn_sensors::load_ssn_sensors / bh.datasets.ssn_sensors.load(): embedded dataset of 21 representative SSN sensor sites from Vallado 4th Ed. Tables 4-2/4-3/4-4 with locations, az/el/range field-of-view limits, and bias/noise calibration values. @duncaneddy (#406)
  • AzElRangeMeasurementModel: topocentric azimuth/elevation/range measurement model with constant-bias support, azimuth residual wrapping, and a wrap-aware finite-difference Jacobian. @duncaneddy (#406)
  • SimpleSSNSensor: ground-sensor measurement simulation with wrap-aware field-of-view checks, seeded Gaussian noise, step-wise (measure) and batched (simulate_observations) generation, and a matching filter-side measurement_model(). @duncaneddy (#406)
  • MeasurementModel::residual(): overridable, dimension-validated residual hook used by all EKF/UKF/BLS residual computations. @duncaneddy (#406)
  • ExtendedKalmanFilter::propagate_to / UnscentedKalmanFilter::propagate_to: measurement-free prediction step that records covariance growth across tracking gaps. @duncaneddy (#406)
  • SSN tracking examples: standalone Rust/Python pair (examples/estimation/ssn_tracking) and a six-hour BLS/EKF/UKF walkthrough with sensor-network, measurement, and filter-comparison figures (docs/examples/ssn_tracking.md). @duncaneddy (#406)
  • brahe transform position and brahe transform rotation CLI subcommands for position-vector and rotation-matrix transforms between reference frames. @duncaneddy (#407)
  • brahe transform frame now accepts all named reference frames (GCRF, ITRF, EME2000, lunar LCI/LFPA/LFME, Mars MCI/MCMF, EMBI, SSBI, synodic EMR/SER/GSE) in addition to the ECI/ECEF aliases. @duncaneddy (#407)
  • Runnable fk5_frame_correction example (Python and Rust) demonstrating the star-catalog to EME2000 to GCRF to ITRF transformation chain. @duncaneddy (#413)
  • builder() constructors for DNumericalOrbitPropagator, DNumericalPropagator, SGPPropagator (OMM elements), WalkerConstellationGenerator, ExtendedKalmanFilter, UnscentedKalmanFilter, BatchLeastSquares, AccessProperties, and CDMObjectMetadata, with Python mirrors for all but CDMObjectMetadata. @duncaneddy (#414)
  • WalkerConstellationGeneratorBuilder::build() returns an error on invalid t/p/f configurations instead of panicking. @duncaneddy (#414)
  • CDMObjectMetadataBuilder exposes the optional CCSDS CDM metadata fields that CDMObjectMetadata::new() could not set, and enforces the conditional-mandatory rules (ALT_COV_TYPE requires ALT_COV_REF_FRAME; EPHEMERIS_NAME=ODM requires ODM_MSG_LINK). @duncaneddy (#414)
  • Builder-focused documentation: Learn-page sections, library API pages, and runnable Rust/Python examples for each builder. @duncaneddy (#414)
  • JPL Small-Body Database (SBDB) Lookup client (brahe.datasets.sbdb.SBDBClient returning SBDBObject) that resolves a name or designation to its NAIF/SPK ID and SI physical parameters (GM in m³/s², radius in m), with on-disk caching. @duncaneddy (#416)
  • JPL Horizons SPK client (brahe.datasets.horizons: HorizonsClient, HorizonsSPKRequest, HorizonsSPKResponse) that generates, caches, and loads targeted small-body SPK kernels, with a response handle exposing the cached .bsp path, raw bytes, and a direct load into the SPICE registry. @duncaneddy (#416)
  • SPK segment type 21 (Extended Modified Difference Arrays) reader, enabling Horizons-generated small-body SPKs (e.g. Ceres) to load and answer position, velocity, and state queries; the interpolation is a port of CSPICE spke21 validated bit-exact against CSPICE spkgeo on a real Ceres kernel. @duncaneddy (#416)
  • Learn and Library-API documentation pages for the SBDB and Horizons clients. @duncaneddy (#416)

Changed

  • Breaking: third-body ephemeris accessors renamed from *_de to *_spice (e.g. sun_position_desun_position_spice, accel_third_body_sun_deaccel_third_body_sun_spice) — the ephemeris source is the SPICE kernel system (DE + satellite ephemeris kernels), not DE alone. @duncaneddy (#376)
  • Breaking: the five outer-planet accessors return the true planet body center (previously the planetary-system barycenter); the barycenter is available via the new *_barycenter_*_spice functions, and third-body force models use those (numerically identical to before, no satellite-kernel downloads in propagation). @duncaneddy (#376)
  • Breaking: per-body position outputs no longer apply a J2000→ICRF frame-bias rotation. NAIF documents that SPICE "J2000" output from DE kernels is already ICRF-aligned, so the previous rotation introduced a ~23 mas systematic offset (~1.7e4 m at 1 AU). @duncaneddy (#376)
  • Breaking: EphemerisSource::SPK payload type is SPICEKernel (was SPKKernel); serde variant names for the DE kernels are unchanged, so serialized force-model configs remain compatible. @duncaneddy (#376)
  • Switching between DE kernels no longer reloads global state; both kernels stay resident in the registry. @duncaneddy (#376)
  • ForceModelConfig gains a central_body field (serde-default Earth; existing configs and constructors unchanged) and its validity is now checked at propagator construction instead of failing mid-propagation. @duncaneddy (#377)
  • SolarRadiationPressureConfiguration gains occulting_bodies (serde-default [Earth], preserving existing behavior). @duncaneddy (#377)
  • state_koe_osc on the numerical propagator now computes osculating elements about the propagator's central body (Earth behavior unchanged; barycenter central bodies return an error). @duncaneddy (#377)
  • CentralBody::Mars and the MCI/MCMF frames are centered on the Mars body center (NAIF 499), with the body-center leg auto-loaded from the mar099s satellite ephemeris (the default DE kernel loads first so a satellite kernel cannot suppress the auto-initialization); ThirdBody::Mars keeps the system barycenter (NAIF 4) per the standard third-body formulation. @duncaneddy (#377)
  • The lunar PCK / Mars SPK auto-load guards latch on first use (OnceLock): the kernel registry is consulted once, and unloading kernels mid-run is no longer re-detected (failed loads retry). @duncaneddy (#377)
  • Kernel-only tests and doctests are no longer integration-gated: SPICE kernels are expected cached locally (just setup) and in CI; the pytest integration marker is reserved for remote-host access and long runtimes. @duncaneddy (#377)
  • Rust ThirdBody no longer derives Eq/Hash (the new Custom variant carries a float GM); the Python ThirdBody/CentralBody/OccultingBody/ReferenceFrame types are plain wrapper classes — equality is preserved but they are not hashable and class attributes are not singletons (use ==, not is). @duncaneddy (#377)
  • compute_spherical_harmonics and accel_gravity_spherical_harmonics now evaluate via the Clenshaw kernel when its coefficients are present (the default), falling back to Cunningham coefficients when those are the only set loaded. @duncaneddy (#378)
  • Gravity model loading now precomputes only Clenshaw coefficients by default (use GravityModelCoefficients::Both/Cunningham for the V/W kernel); set_max_degree_order rebuilds whichever coefficient sets exist. @duncaneddy (#378)
  • The numerical orbit propagator evaluates spherical-harmonic gravity through the Clenshaw kernel (the per-propagator V/W workspace matrices were removed); its per-propagator rotation cache is now an lru-crate LRU keyed on stage time. @duncaneddy (#378)
  • CLENSHAW_PARALLEL_THRESHOLD_NMAX tuned to 180 from benchmarks (with a TODO to derive it from the execution environment); Cunningham's documented numerical limits added to its rustdoc. @duncaneddy (#378)
  • GravityModel::compute_spherical_harmonics_with_workspace and accel_gravity_spherical_harmonics_with_workspace — renamed to compute_spherical_harmonics_cunningham_with_workspace / accel_gravity_spherical_harmonics_cunningham_with_workspace (the Clenshaw kernel needs no workspace). The old allocating Cunningham entry point is now GravityModel::compute_spherical_harmonics_cunningham (compute_spherical_harmonics keeps its name and signature but dispatches). No deprecation aliases retained. @duncaneddy (#378)
  • Marked additional space-weather tests and integration to issues running locally if celestrak is unavailable. @duncaneddy (#378)
  • Numerical propagator construction now applies the configured permanent-tide conversion to propagator-owned gravity models when ForceModelConfig.tides is set (models load with C̄20 exactly as published when tides are not configured). @duncaneddy (#385)
  • Breaking: ForceModelConfig.third_body becomes Option<Vec<ThirdBodyConfiguration>> with per-body entries. The field deserializes from bare bodies, a single entry, a list, or the legacy pre-flattening object shape, which migrates on load with its original semantics (pre-split planet names map to the *Barycenter variants they then denoted). @duncaneddy (#394)
  • Breaking: ThirdBody::MarsNeptune now denote planet centers (NAIF 499–899, planet-only GMs, resolved through satellite-system kernels) instead of system barycenters; use the new *Barycenter variants for the previous behavior. @duncaneddy (#394)
  • Breaking: GM_MARSGM_NEPTUNE and GM_PLUTO now hold planet-only GMs from NAIF gm_de440.tpc; the previous DE430-era system values moved to GM_*_SYSTEM and were updated to DE440 (full-precision tpc digits). @duncaneddy (#394)
  • Force-model validation keys the Earth-atmosphere-model and radius/spin drag requirements on the attributed body rather than the central body, and validates per-third-body gravity models (EarthZonal requires ThirdBody::Earth; SphericalHarmonic requires a body with a known body-fixed frame). An attributed drag body's ephemeris is verified at propagator construction. @duncaneddy (#394)
  • Renamed the IGNORE example flag to NETWORK, completing the split begun in #366. IGNORE named no reason to skip, while every file carrying it depends on a live third-party service. --ignore becomes --network on just test-examples and just make-plots. @duncaneddy (#395)
  • Reclassified plots/fig_access_benchmark.py and plots/fig_comparative_benchmarks.py from IGNORE to MANUAL. Neither is network-driven: both regenerate committed artifacts and require a custom environment, and neither is driven by just make-plots. @duncaneddy (#395)
  • Aligned the User Guide and Python API Reference navigation to a single section ordering and vocabulary. @duncaneddy (#395)
  • The Learn time-scale table and the TimeSystem overview rustdoc now document all ten time systems rather than five. @duncaneddy (#395)
  • All tidal corrections (solid Earth Step 1/2, pole tides, ocean tides) and the static gravity field are now evaluated in a single fold-in Clenshaw pass per dynamics call, replacing the separate solid-tide evaluation path. @duncaneddy (#397)
  • Bundled EGM2008 gravity model truncated from degree/order 360 to 120 and renamed GravityModelType::EGM2008_360EGM2008_120 to keep the crates.io package within size limits (bit-identical coefficients through degree 120; higher degrees available via ICGEM). @duncaneddy (#397)
  • Ocean tide force-model documentation (docs/learn/orbital_dynamics/tides.md) expanded to cover ocean tides, admittance waves, pole tides, and the download/cache behavior. @duncaneddy (#397)
  • Documentation is now served from https://docs.brahe.space/, with brahe.space and www.brahe.space redirecting to it. Existing duncaneddy.github.io/brahe/ links continue to resolve through GitHub's permanent redirect. @duncaneddy (#398)
  • Nightly development wheels now install from https://docs.brahe.space/simple/. @duncaneddy (#398)
  • The git clone commands in the installation guide now pass -c core.symlinks=true and clone only the main branch. @duncaneddy (#398)
  • plot_trajectory_3d: earth_texture renamed to texture, show_earth renamed to show_body, and all parameters after trajectories are now keyword-only (breaking; all in-repo usages updated). @duncaneddy (#399)
  • load_earth_texture removed in favor of load_body_texture. @duncaneddy (#399)
  • just download-resources now also warms the moon/mars/ceres textures. @duncaneddy (#399)
  • state_koe_to_eci_for_body/state_eci_to_koe_for_body renamed to state_koe_to_inertial_for_body/state_inertial_to_koe_for_body, taking a CentralBody instead of a bare gm and referencing elements to the body's mean equator at J2000 instead of the ICRF axes. @duncaneddy (#399)
  • Live-API examples now NETWORK-flagged. @duncaneddy (#399)
  • Breaking: state_koe_osc_to_mean and state_koe_mean_to_osc now take a MeanElementMethod argument (before angle_format) and return a Result (Rust) / raise on error (Python). Existing analytical behavior is preserved by passing MeanElementMethod::BrouwerLyddane / bh.MeanElementMethod.BROUWER_LYDDANE. @duncaneddy (#403)
  • Upgraded Rust dependency version requirements to latest compatible releases (cargo upgrade). @duncaneddy (#404)
  • UKF measurement statistics use a reference-point mean and residual-based deviations, making the measurement update robust for angular measurements near a wrap (identical results for non-angular models). @duncaneddy (#406)
  • EKF/UKF/BLS pre-fit and post-fit residuals now route through MeasurementModel::residual() (default behavior unchanged). @duncaneddy (#406)
  • rand/rand_distr promoted from dev-dependencies to runtime dependencies for measurement noise sampling. @duncaneddy (#406)
  • brahe transform frame dispatches through the core frame router (state_frame_to_frame) instead of a hardcoded ECI↔ECEF path. @duncaneddy (#407)
  • Rewrote docs/examples note admonitions as per-example API deep dives covering GPRecord.to_sgp_propagator, location_accesses, datasets.groundstations.load, AccessPropertyComputer, TimeEvent callbacks, additional_dynamics/control_input, ConstraintAll, and AOIExitEvent. @duncaneddy (#409)
  • Docs deploys now restore NETWORK-example figures saved by the weekly --network integration run, so example pages no longer reference figures that were never generated. @duncaneddy (#409)
  • Star-catalog documentation now describes each catalog's native reference frame (Hipparcos and Tycho-2 on ICRS axes, FK5 realizing EME2000) and when the ~23 mas frame bias must be applied before use. @duncaneddy (#413)
  • DNumericalOrbitPropagatorBuilder is now a runtime builder with required fields as builder() arguments, replacing the typestate (Set/Unset) builder; the entry point and setter names are otherwise unchanged (breaking, Rust only). @duncaneddy (#414)
  • DNumericalOrbitPropagator::new and DNumericalPropagator::new validate initial_covariance dimensions against the state dimension and return an error on mismatch. @duncaneddy (#414)
  • Reworked the Dawn-at-Ceres example to resolve Ceres via SBDB, load a Ceres SPK from Horizons, and model Sun and Jupiter third-body plus solar radiation pressure perturbations around the custom-defined body. @duncaneddy (#416)
  • Refactored the SPK reader behind a segment abstraction so a resolved chain can mix segment types, combining a type-21 small-body segment with the existing Chebyshev (types 2 and 3) segments from the DE kernels. @duncaneddy (#416)
  • Crates.io and PyPI package-validation CI jobs now run in parallel with the Rust and Python test suites instead of waiting for them to finish. @duncaneddy (#417)
  • Getting-started documentation now links function and class references to their corresponding library API and Learn pages. @duncaneddy (#417)

Removed

  • Breaking: SPKKernel — use SPICEKernel. @duncaneddy (#376)
  • Breaking: datasets::download_de_kernel — use datasets::naif::download_spice_kernel, which accepts any known kernel name (DE, satellite ephemeris, binary PCK). @duncaneddy (#376)
  • Breaking: set_global_almanac, get_loaded_kernel_type, and brahe_epoch_to_anise (ANISE types no longer appear in the public API). @duncaneddy (#376)
  • Breaking: ANISE as a runtime dependency (now dev-only). Downstream users relying on brahe's transitive anise dependency must add it directly. @duncaneddy (#376)
  • GravityModelType::EGM2008_360 (renamed to EGM2008_120; not deprecated — the variant was removed outright since the underlying packaged file changed). @duncaneddy (#397)
  • Internal fixed-size (degree ≤ 4) tidal coefficient evaluator (TideCoefficients, accel_low_degree_harmonics), superseded by the dynamically sized TideDeltas accumulator evaluated through the shared Clenshaw kernel. @duncaneddy (#397)
  • Removed the Starlink visualization example (docs/examples/visualizing_starlink.md, examples/examples/visualizing_starlink.py) and its committed figures; the GPS visualization example covers the same workflow. @duncaneddy (#409)

Fixed

  • ICGEM GFC parser now handles the gravity_constant header key and Fortran D-exponent notation, unblocking all non-Earth gravity model downloads (e.g. lunar GRGM660PRIM, Mars GMM-2B). @duncaneddy (#377)
  • Earth-centered propagation configured with the new third-body variants (Phobos, Deimos, Custom) routes through the SPK-backed path instead of panicking; validate() rejects LowPrecision ephemerides for bodies other than Sun/Moon and third bodies that coincide with the central body. @duncaneddy (#377)
  • Repo-wide cargo fmt and ruff lint/format debt that had accumulated since pre-commit hooks were never installed locally. @duncaneddy (#380)
  • crates/brahe-py clippy errors that were silently exempted from the pre-commit gate because the clippy hook lacked --workspace; the hook now covers the whole workspace. This included missing_safety_doc on 5 numpy-conversion methods in attitude.rs — on inspection none of them perform an actual unsafe operation (into_pyarray/reshape are safe in the pinned numpy version), so the vestigial unsafe keyword was removed instead of documenting a non-existent invariant — plus new_without_default/len_without_is_empty on the EOP provider bindings. @duncaneddy (#380)
  • Fix issue with some tests that rely on celestrak integration not being properly marked pytest.mark.integration. @duncaneddy (#385)
  • FLAGS declared past the tenth line of an example were silently ignored, because the runner parsed only the first ten lines. lunar_orbit.rs and mars_orbit.rs declare theirs on line 13, so both ran on every default just test-examples — downloading the GRGM660PRIM gravity model and the moon_pa_de440 kernel — despite being flagged, while their .py twins were correctly skipped. The runner now parses the whole leading comment block. @duncaneddy (#395)
  • Corrected the TCG secular drift from ~0.7 s/year to ~22 ms/year (IAU 2000 Resolution B1.9, L_G = 6.969290134e-10). @duncaneddy (#395)
  • Corrected the Epoch::time_system documentation, which listed five of the ten time systems and directed readers to a to_time_system() method that did not exist. @duncaneddy (#395)
  • Corrected typos in the TimeSystem rustdoc ("supposed" to "supported", "expeccted" to "expected"). @duncaneddy (#395)
  • Repaired six broken documentation links. The contributing guide, code of conduct, development guide, and license links omitted the /latest/ version segment and resolved to nothing. The development guide was additionally linked as developer_guidelines.html, a page that has never existed. Two versioning policy links used a directory-style URL where the site emits .html. @duncaneddy (#398)
  • brahe transform frame no longer emits duplicate output when the source and target frames are identical (missing early return). @duncaneddy (#407)
  • brahe transform coordinates now rejects non-ECI/ECEF values for --from-frame/--to-frame at validation instead of silently mis-routing them. @duncaneddy (#407)
  • Fixed 404s for all NETWORK-flagged example figures on the live documentation site (Doppler compensation, maximum communications gap, LRO, MRO, Dawn at Ceres, ground contacts, imaging opportunities, imaging data latency, tessellation). @duncaneddy (#409)
  • Fixed the NAIF kernel and 3D-texture example caches being stranded on PR merge refs, which forced every CI run into live downloads from naif.jpl.nasa.gov and Solar System Scope; caches now save from main only. @duncaneddy (#409)
  • Corrected the NASA NEN station count claim in the ground contacts example note. @duncaneddy (#409)
  • apply_proper_motion documentation now describes the IAU SOFA iauPmsafe space-motion transformation the function actually calls, rather than a hand-rolled tangent-plane method. @duncaneddy (#413)
  • Documentation figures for the lunar, Mars, Ceres, Earth-Moon free-return, and star-field examples now regenerate on every docs deploy instead of only via the weekly network run. @duncaneddy (#413)
  • Mismatched initial_covariance dimensions previously constructed successfully and panicked inside the first propagation step; construction now fails with a descriptive error, including from Python with extended (6+N) states. @duncaneddy (#414)
  • Captured output of the NETWORK-flagged getting-started examples (first script, Celestrak client) now reaches the deployed docs through a network-example-outputs cache, and the offline external-data examples emit output, so the affected pages no longer render empty output blocks. @duncaneddy (#417)
  • Star-field sensor view no longer flips direction at orbital plane crossings by using the orbit normal as a continuous roll reference, scrolls the star field along the elevation axis, and preserves per-star identity so markers fade in and out instead of jumping when the visible-star count changes. @duncaneddy (#417)
  • ci-success aggregation now includes the check-skip and delay-python gating jobs so a failed gate cannot be reported as a passing run. @duncaneddy (#417)