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
Resultin Rust API, and improving Python errors
Added
- Native SPICE kernel support:
SPKandBPCKreaders (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) NAIFIdenum (planets, planetary-system barycenters, major moons,Id(i32)catch-all) andFrameIdenum (MoonPaDe440= 31008 +Id(i32)); all query functions takeimpl Into<NAIFId>/impl Into<FrameId>, so raw integer IDs keep working. Python mirrors asNAIFId/FrameIdIntEnums. @duncaneddy (#376)SPICEKernelenum covering every downloadable kernel — DE (de430–de442s), JPL satellite ephemeris kernels (mar099,mar099s,jup365,sat441,ura184,nep097,plu060), and binary PCK (moon_pa_de440) — plusKernelSourcefor bring-your-own kernel paths. @duncaneddy (#376)load_common_kernels()(de440s + moon_pa_de440, ~46 MB) andload_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-scopedspk_position_from_kernel/spk_velocity_from_kernel/spk_state_from_kernel. @duncaneddy (#376) - Per-body kernel-backed accessors
{body}_{position,velocity,state}_spicefor 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}_spicevariants 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 rawpck_euler_angles. @duncaneddy (#376) Epoch::spice_et()— SPICE ephemeris time (TDB seconds past J2000) convenience accessor;Epoch::seconds_past_j2000_as_time_systemfor other time systems. @duncaneddy (#376)datasets::naif::download_spice_kernelfor any known NAIF kernel (DE, satellite ephemeris, binary PCK), exposed in Python asdatasets.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/LFMEand Mars framesMCI/MCMFwith{rotation|position|state}_{from}_to_{to}transformations, cross-centerstate_eci_to_lci/state_eci_to_mci, and automaticmoon_pa_de440PCK 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 frompck00011.tpcand validated against ANISE. @duncaneddy (#377) - Centralized
ReferenceFrameenum 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) CentralBodyabstraction (Earth, Moon, Mars, EMB, SSB, Custom) withfrom_naif_id(), andForceModelConfig::for_body(),lunar_default()(GRGM660PRIM 50×50, Earth+Sun third bodies, Moon+Earth occultation),mars_default()(GMM-2B 50×50, exponential drag, Mars occultation), andcislunar_default()(EMB-centered) constructors. @duncaneddy (#377)- Barycentric (EMB/SSB) propagation with correct direct/differential third-body handling;
ThirdBodygainsEarth,Phobos,Deimos, andCustom { name, naif_id, gm }variants. @duncaneddy (#377) - Multi-body eclipse modeling via
occulting_bodieson the SRP configuration and newOccultingBodytype. @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, andstate_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, batchto_*, covariance, CCSDS OEM export) re-center through the frame router;state_koe_oscreturns 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 ofstate_eci_to_koe_for_body),ReferenceFrame::ECI/ECEFaliases,kernel_is_loaded(), and theSECONDS_PER_JULIAN_CENTURYconstant. @duncaneddy (#377)just setuprecipe for local development (uv venv, pre-commit hooks, and pre-downloading the de440s/moon_pa_de440/mar099s kernels tests expect cached). @duncaneddy (#377)- NAIF
mar099sMars 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, extendedThirdBody, 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) GravityModelCoefficientsload configuration (Clenshawdefault /Cunningham/Both) withfrom_model_type_with_coefficients/from_file_with_coefficientsconstructors (Rust + Python) andload_uncached_with_coefficients(Rust only, matchingload_uncached), plusprecompute_*/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_cunninghamconvenience 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_modelPython binding (mirrors the existing Rust function). @duncaneddy (#378)- Clenshaw-vs-Cunningham benchmark suite in
benchmarks/gravity_benchmarks.rs. @duncaneddy (#378) is_empty()method onStaticEOPProvider,FileEOPProvider, andCachingEOPProvider(Python bindings), satisfying clippy'slen_without_is_emptyfor the newly workspace-coveredbrahe-pycrate. @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) TidesConfigurationandPermanentTideConfig(Auto/ConvertTo(system)/Off) onForceModelConfig.tidesto 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_coefficientsinorbit_dynamics::tidesfor 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
UserWarningin 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, andReferenceFrame.GSEsynodic 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_accelerationandspk_acceleration_from_kernel(Rust and Python). @duncaneddy (#393) SUN_EARTH_BARYCENTER_IDsynthetic 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, andstates_in_frameonSOrbitStateProvider/DOrbitStateProviderand on theSGPPropagator,KeplerianPropagator, andNumericalOrbitPropagatorPython 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:
ThirdBodyConfigurationpairs each perturber with its ownephemeris_sourceandgravitymodel;SphericalHarmonicandEarthZonalfields 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) ThirdBodyplanetary-system barycenter variantsMarsBarycenter–NeptuneBarycenter(NAIF 4–8, system GMs), used by the default Earth force models;accel_third_bodyaccepts both barycenter and planet-center variants (the latter auto-load their satellite-system kernels). @duncaneddy (#394)DragConfiguration.bodyto 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 bycislunar_default). @duncaneddy (#394)- ECI↔EMBI frame translation helpers
position_eci_to_emb/position_emb_to_eciandstate_eci_to_emb/state_emb_to_eci(Rust + Python). @duncaneddy (#394) - Constants
GM_MARS_SYSTEM–GM_NEPTUNE_SYSTEMandGM_PLUTO_SYSTEM(planetary-system barycentric GMs, NAIFgm_de440.tpc). @duncaneddy (#394) ThirdBody::as_central_body()andThirdBody::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 newEpochfor 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 theEpoch::time_systemdocumentation but was never implemented. @duncaneddy (#395)TimeSystemAPI reference page underlibrary_api/time/, documenting all ten time systems (GPS,TAI,TT,UTC,UT1,TDB,TCG,TCB,BDT,GST). It previously lived underconstants/units.mdand listed only five, soTDB,TCG,TCB,BDT, andGSTdid 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
FLAGSdeclared 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.oceanfield 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
OceanTideConfigand the newpole_tide/oceanconfiguration fields, with regenerated type stubs. @duncaneddy (#397) ReferenceFrame.Synodic(origin, primary, secondary)andSynodicOriginfor arbitrary two-body rotating frames, withsynodic_origin/synodic_primary/synodic_secondaryaccessors (also populated for EMR/SER/GSE). @duncaneddy (#399)synodic_barycenter_idand 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_textureand generalizedload_body_texture(CC BY 4.0, credited in the license page). @duncaneddy (#399) central_bodyandadditional_bodiesparameters onplot_trajectory_3dfor Moon/Mars/custom-body scenes, backed by a body-visuals registry. @duncaneddy (#399)plot_synodic_3dandplot_earth_moon_rotating_3dfor 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/Rzmatrix functions. @duncaneddy (#399) - Planetary radius constants (
R_MERCURY..R_NEPTUNE). @duncaneddy (#399) - Tested learn examples for generic
ReferenceFrame.Synodicframes andplot_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 Montenbrucka, h, k, p, q, lnaming and an explicit retrograde factor (Rust + Python). @duncaneddy (#403) MeanElementMethodselector (BROUWER_LYDDANEanalytical,numerical(config)windowed averaging) plusWindowAlignment,WindowEdgeHandling,MeanElementNumericalMethodConfig, andMeanElementInverseConfigconfiguration 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_meanandbatch_state_koe_mean_to_oscreturning(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,
StarRecordtrait, 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 SOFAiauPmsafeviarsofa; theory per ESA SP-1200 §1.5.5) andStarRecord::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-sidemeasurement_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 positionandbrahe transform rotationCLI subcommands for position-vector and rotation-matrix transforms between reference frames. @duncaneddy (#407)brahe transform framenow 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_correctionexample (Python and Rust) demonstrating the star-catalog to EME2000 to GCRF to ITRF transformation chain. @duncaneddy (#413) builder()constructors forDNumericalOrbitPropagator,DNumericalPropagator,SGPPropagator(OMM elements),WalkerConstellationGenerator,ExtendedKalmanFilter,UnscentedKalmanFilter,BatchLeastSquares,AccessProperties, andCDMObjectMetadata, with Python mirrors for all butCDMObjectMetadata. @duncaneddy (#414)WalkerConstellationGeneratorBuilder::build()returns an error on invalid t/p/f configurations instead of panicking. @duncaneddy (#414)CDMObjectMetadataBuilderexposes the optional CCSDS CDM metadata fields thatCDMObjectMetadata::new()could not set, and enforces the conditional-mandatory rules (ALT_COV_TYPErequiresALT_COV_REF_FRAME;EPHEMERIS_NAME=ODMrequiresODM_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.SBDBClientreturningSBDBObject) 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.bsppath, 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
spke21validated bit-exact against CSPICEspkgeoon 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
*_deto*_spice(e.g.sun_position_de→sun_position_spice,accel_third_body_sun_de→accel_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_*_spicefunctions, 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::SPKpayload type isSPICEKernel(wasSPKKernel); 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)
ForceModelConfiggains acentral_bodyfield (serde-defaultEarth; existing configs and constructors unchanged) and its validity is now checked at propagator construction instead of failing mid-propagation. @duncaneddy (#377)SolarRadiationPressureConfigurationgainsocculting_bodies(serde-default[Earth], preserving existing behavior). @duncaneddy (#377)state_koe_oscon 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::Marsand theMCI/MCMFframes are centered on the Mars body center (NAIF 499), with the body-center leg auto-loaded from themar099ssatellite ephemeris (the default DE kernel loads first so a satellite kernel cannot suppress the auto-initialization);ThirdBody::Marskeeps 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 pytestintegrationmarker is reserved for remote-host access and long runtimes. @duncaneddy (#377) - Rust
ThirdBodyno longer derivesEq/Hash(the newCustomvariant carries a float GM); the PythonThirdBody/CentralBody/OccultingBody/ReferenceFrametypes are plain wrapper classes — equality is preserved but they are not hashable and class attributes are not singletons (use==, notis). @duncaneddy (#377) compute_spherical_harmonicsandaccel_gravity_spherical_harmonicsnow 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/Cunninghamfor the V/W kernel);set_max_degree_orderrebuilds 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_NMAXtuned 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_workspaceandaccel_gravity_spherical_harmonics_with_workspace— renamed tocompute_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 nowGravityModel::compute_spherical_harmonics_cunningham(compute_spherical_harmonicskeeps 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.tidesis set (models load with C̄20 exactly as published when tides are not configured). @duncaneddy (#385) - Breaking:
ForceModelConfig.third_bodybecomesOption<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*Barycentervariants they then denoted). @duncaneddy (#394) - Breaking:
ThirdBody::Mars–Neptunenow denote planet centers (NAIF 499–899, planet-only GMs, resolved through satellite-system kernels) instead of system barycenters; use the new*Barycentervariants for the previous behavior. @duncaneddy (#394) - Breaking:
GM_MARS–GM_NEPTUNEandGM_PLUTOnow hold planet-only GMs from NAIFgm_de440.tpc; the previous DE430-era system values moved toGM_*_SYSTEMand 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 (
EarthZonalrequiresThirdBody::Earth;SphericalHarmonicrequires a body with a known body-fixed frame). An attributed drag body's ephemeris is verified at propagator construction. @duncaneddy (#394) - Renamed the
IGNOREexample flag toNETWORK, completing the split begun in #366.IGNOREnamed no reason to skip, while every file carrying it depends on a live third-party service.--ignorebecomes--networkonjust test-examplesandjust make-plots. @duncaneddy (#395) - Reclassified
plots/fig_access_benchmark.pyandplots/fig_comparative_benchmarks.pyfromIGNOREtoMANUAL. Neither is network-driven: both regenerate committed artifacts and require a custom environment, and neither is driven byjust 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
TimeSystemoverview 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_360→EGM2008_120to 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/, withbrahe.spaceandwww.brahe.spaceredirecting to it. Existingduncaneddy.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 clonecommands in the installation guide now pass-c core.symlinks=trueand clone only themainbranch. @duncaneddy (#398) plot_trajectory_3d:earth_texturerenamed totexture,show_earthrenamed toshow_body, and all parameters aftertrajectoriesare now keyword-only (breaking; all in-repo usages updated). @duncaneddy (#399)load_earth_textureremoved in favor ofload_body_texture. @duncaneddy (#399)just download-resourcesnow also warms the moon/mars/ceres textures. @duncaneddy (#399)state_koe_to_eci_for_body/state_eci_to_koe_for_bodyrenamed tostate_koe_to_inertial_for_body/state_inertial_to_koe_for_body, taking aCentralBodyinstead of a baregmand 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_meanandstate_koe_mean_to_oscnow take aMeanElementMethodargument (beforeangle_format) and return aResult(Rust) / raise on error (Python). Existing analytical behavior is preserved by passingMeanElementMethod::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_distrpromoted from dev-dependencies to runtime dependencies for measurement noise sampling. @duncaneddy (#406)brahe transform framedispatches through the core frame router (state_frame_to_frame) instead of a hardcoded ECI↔ECEF path. @duncaneddy (#407)- Rewrote
docs/examplesnote admonitions as per-example API deep dives coveringGPRecord.to_sgp_propagator,location_accesses,datasets.groundstations.load,AccessPropertyComputer,TimeEventcallbacks,additional_dynamics/control_input,ConstraintAll, andAOIExitEvent. @duncaneddy (#409) - Docs deploys now restore NETWORK-example figures saved by the weekly
--networkintegration 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)
DNumericalOrbitPropagatorBuilderis now a runtime builder with required fields asbuilder()arguments, replacing the typestate (Set/Unset) builder; the entry point and setter names are otherwise unchanged (breaking, Rust only). @duncaneddy (#414)DNumericalOrbitPropagator::newandDNumericalPropagator::newvalidateinitial_covariancedimensions 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— useSPICEKernel. @duncaneddy (#376) - Breaking:
datasets::download_de_kernel— usedatasets::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, andbrahe_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
anisedependency must add it directly. @duncaneddy (#376) GravityModelType::EGM2008_360(renamed toEGM2008_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 sizedTideDeltasaccumulator 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_constantheader key and FortranD-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()rejectsLowPrecisionephemerides for bodies other than Sun/Moon and third bodies that coincide with the central body. @duncaneddy (#377) - Repo-wide
cargo fmtandrufflint/format debt that had accumulated since pre-commit hooks were never installed locally. @duncaneddy (#380) crates/brahe-pyclippy errors that were silently exempted from the pre-commit gate because the clippy hook lacked--workspace; the hook now covers the whole workspace. This includedmissing_safety_docon 5 numpy-conversion methods inattitude.rs— on inspection none of them perform an actual unsafe operation (into_pyarray/reshapeare safe in the pinnednumpyversion), so the vestigialunsafekeyword was removed instead of documenting a non-existent invariant — plusnew_without_default/len_without_is_emptyon 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) FLAGSdeclared past the tenth line of an example were silently ignored, because the runner parsed only the first ten lines.lunar_orbit.rsandmars_orbit.rsdeclare theirs on line 13, so both ran on every defaultjust test-examples— downloading the GRGM660PRIM gravity model and themoon_pa_de440kernel — despite being flagged, while their.pytwins were correctly skipped. The runner now parses the whole leading comment block. @duncaneddy (#395)- Corrected the TCG secular drift from
~0.7 s/yearto~22 ms/year(IAU 2000 Resolution B1.9, L_G = 6.969290134e-10). @duncaneddy (#395) - Corrected the
Epoch::time_systemdocumentation, which listed five of the ten time systems and directed readers to ato_time_system()method that did not exist. @duncaneddy (#395) - Corrected typos in the
TimeSystemrustdoc ("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 asdeveloper_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 frameno longer emits duplicate output when the source and target frames are identical (missing early return). @duncaneddy (#407)brahe transform coordinatesnow rejects non-ECI/ECEF values for--from-frame/--to-frameat 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_motiondocumentation now describes the IAU SOFAiauPmsafespace-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_covariancedimensions 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)