sidereon 2.0.0__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sidereon-2.0.0 → sidereon-2.1.0}/CHANGELOG.md +16 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/Cargo.lock +5 -5
- {sidereon-2.0.0 → sidereon-2.1.0}/Cargo.toml +3 -3
- {sidereon-2.0.0 → sidereon-2.1.0}/PKG-INFO +7 -4
- {sidereon-2.0.0 → sidereon-2.1.0}/README.md +6 -3
- {sidereon-2.0.0 → sidereon-2.1.0}/pyproject.toml +1 -1
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/__init__.pyi +48 -4
- {sidereon-2.0.0 → sidereon-2.1.0}/src/ephemeris.rs +150 -34
- sidereon-2.1.0/tests/test_sp3_interpolation_policy.py +285 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_sp3_window_continuity.py +1 -1
- {sidereon-2.0.0 → sidereon-2.1.0}/uv.lock +1 -1
- {sidereon-2.0.0 → sidereon-2.1.0}/.cargo/config.toml.disabled +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/.gitattributes +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/.github/workflows/ci.yml +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/.github/workflows/release.yml +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/.gitignore +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/COVERAGE.md +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/LICENSE +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/THIRD-PARTY-NOTICES.md +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/bench/README.md +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/bench/bench_fleet.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/docs/public-gnss-distribution-sources.md +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/examples/clean_a_phone_track.ipynb +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/examples/static_occupation_vs_cors.ipynb +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/__init__.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/_compression.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/_exact_cache.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/_ingress.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/data.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/data.pyi +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/distribution.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/distribution.pyi +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/exact_cache.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/exact_cache.pyi +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/exact_sp3.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/exact_sp3.pyi +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/ntrip.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/ntrip.pyi +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/python/sidereon/py.typed +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/scripts/check-release.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/scripts/smoke-release.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/almanac.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/angles.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/anomaly.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/araim.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/atmosphere.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/bias.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/bodies.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/body_observe.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/broadcast_comparison.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/cdm.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/clock_stability.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/conjunction.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/constellation.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/covariance.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/coverage.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/data_catalog.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/defaults.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/dgnss.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/doppler.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/elements.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/emission.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/equinoctial.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/error_metrics.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/estimation.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/events.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/exact_cache.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/fallback.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/forces.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/frame_catalog.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/frames.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/fusion.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/geodesic.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/geodetic_time_series.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/geofence.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/geoid.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/geometry.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/geometry_quality.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/ils.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/iod.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/ionex.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/lambert.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/leap.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/least_squares.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/lib.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/lnav.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/marshal.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/nmea.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/normality.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/ntrip.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/observables.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/observation.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/oem.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/omm.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/opm.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/orbit_determination.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/ppp.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/ppp_corrections.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/products.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/propagation.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/qc.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/reduced_orbit.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/relative.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/reliability.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/rf.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/rinex.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/rtcm.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/rtk.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/sbas_pl.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/sbas_ssr.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/scenario.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/sidereal.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/signal_analysis.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/sky.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/source_localization.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/space_weather.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/spk.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/spp.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/staleness.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/static_positioning.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/tca.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/tdm.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/terrain.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/terrain_store.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/tides.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/tropo.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/src/unix_compress.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/_helpers.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/README.md +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/antex/igs20_pasa_scoa_gps.atx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/antex/igs20_wettzell_trim.atx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/batch_fleet.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/cdm.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/clk/synthetic_rinex_clock.clk +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/conjunction.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/constellation/gps_ops_sample.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/constellation/navcen_forecast_cases.html +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/constellation/navcen_gps_sample.html +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/events_bodies_dop.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/frames_time.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/geodesic/geodtest_row.dat +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/geoid/egm2008_25_norcal_crop.bin +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/gnss_data/igs22376.sp3.Z.b64 +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/gnss_data/ncompress-current-b9-counter.Z.b64 +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/listings/aiub-iono-p1p2-20260804.csv +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/listings/gfz-ultra-w2430-20260804.html +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/nav/BRDC00GOP_R_20210010000_01D_MN.rnx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/nav/ESBC00DNK_R_20201770000_01D_MN.rnx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/nav/KMS300DNK_R_20221591000_01H_MN.rnx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/numerical_propagation.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/obs/ESBC00DNK_R_20201770000_01D_30S_MO_trim.crx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/obs/ESBC00DNK_R_20201770000_01D_30S_MO_trim.rnx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/obs/algo0010_2015001_v1_trim.crx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/obs/algo0010_2015001_v1_trim.rnx +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/omm.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/pass_finder.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/ppp_esbc.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/rf_link_budget.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/rtk_wtzr.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/sgp4_topocentric.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/sp3/IGS0OPSFIN_20261200945_02H30M_15M_ORB.SP3 +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/sp3-merge-input-v1.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/sp3_bodies.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/spk/horizons_eros_type21.bsp +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/tdm/annex_e_01.kvn +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/fixtures/tle_roundtrip.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/golden/sp3-terminal-record-v1.json +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_012_bindings.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_013_bindings.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_014_bindings.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_015_bindings.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_016_wave2_bindings.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_017_domain_exposure.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_018_domain_exposure.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_019_track.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_020_error_metrics.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_antex.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_atmosphere.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_attested_open.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_batch.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_broadcast_comparison.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_broadcast_fallback.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_cdm.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_conjunction.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_constellation.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_constellation_fleet.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_core_defaults.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_core_public_api_parity.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_covariance.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_covariance_propagation.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_coverage.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_crinex.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_data.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_data_publication_resilience.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_dgnss.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_distribution.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_distribution_fallback.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_dop_at_epoch.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_dop_convention.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_doppler.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_egm96.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_elements.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_events_bodies_dop.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_exact_cache_atomicity.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_field_mode_bindings.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_forces.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_frames_time.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_ftp_transport.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_geoid.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_geometry.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_ils.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_import.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_ionex.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_ionosphere_nequick.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_klobuchar_native.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_lambert_iod.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_leap_seconds.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_least_squares.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_lnav.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_moving_baseline.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_new_core_api.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_next_issue_due.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_ntrip.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_numerical.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_observables.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_observables_broadcast.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_observation.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_observation_qc.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_observe_batch.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_oem.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_omm.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_opm.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_parity_gaps.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_parse_tle_file.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_passes.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_ppp.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_ppp_corrections.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_precise_samples.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_propagation.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_qc.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_reduced_orbit.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_reduced_orbit_piecewise.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_residual_stats.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_rf.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_rinex_clock.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_rinex_nav.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_rinex_obs.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_rtcm.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_rtk.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_sky.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_sp3_bodies.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_sp3_catalog_exact.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_sp3_clock_reference.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_sp3_merge.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_sp3_terminal_record.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_spk.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_spp.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_spp_glonass.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_spp_robustness.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_staleness.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_stub_accuracy.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_tca.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_tides.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_tle_fit.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_tle_roundtrip.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_tropo.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_unix_compress.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/tests/test_visible_ground_track.py +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/third_party_licenses/ERFA-BSD-3-Clause.txt +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/third_party_licenses/IERS-CONVENTIONS-SOFTWARE-LICENSE.txt +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/third_party_licenses/SciPy-BSD-3-Clause.txt +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/third_party_source/sidereon-core-0.35.0/tides/mod.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/third_party_source/sidereon-core-0.35.0/tides/ocean.rs +0 -0
- {sidereon-2.0.0 → sidereon-2.1.0}/third_party_source/sidereon-core-0.35.0/tides/pole.rs +0 -0
|
@@ -4,6 +4,22 @@ All notable changes to the Sidereon Python interface are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.1.0] - 2026-09-05
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Configurable SP3 coverage-gap interpolation policy (`gap_threshold_factor`, default 1.5):
|
|
12
|
+
- `load_sp3(..., gap_threshold_factor=None)` loads SP3 products with an explicit gap threshold factor.
|
|
13
|
+
- `Sp3.gap_threshold_factor` reads the active factor; `Sp3.with_interpolation_options(factor)` returns a copy with the updated policy.
|
|
14
|
+
- `Sp3.check_continuity(..., gap_threshold_factor=None)` and `Sp3.continuity_verdict(..., gap_threshold_factor=None)` accept an optional gap threshold factor override.
|
|
15
|
+
- `PreciseEphemerisSamples.from_samples(..., gap_threshold_factor=None)` preserves or overrides the factor; `PreciseEphemerisSamples.gap_threshold_factor` and `with_interpolation_options(factor)` inspect and update it.
|
|
16
|
+
- `PreciseEphemerisInterpolant.from_sp3(..., gap_threshold_factor=None)`, `from_samples(..., gap_threshold_factor=None)`, and `from_precise_ephemeris_samples(..., gap_threshold_factor=None)` accept an optional factor; `PreciseEphemerisInterpolant.gap_threshold_factor` and `with_interpolation_options(factor)` inspect and update it.
|
|
17
|
+
- `Sp3.precise_interpolant_artifact_bytes(gap_threshold_factor=None)`, `build_precise_interpolant_artifact_bytes(sp3, gap_threshold_factor=None)`, and `PreciseInterpolantArtifact.gap_threshold_factor` serialize, build, and inspect precomputed artifacts with the interpolation policy.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Engine update: sidereon 2.1.0 / sidereon-core 2.1.0. Additive upstream release: the SP3 coverage-gap threshold is now a validated, product-carried policy (`Sp3InterpolationOptions`, default 1.5 and bit-identical to before), the SP3 window-scoped continuity reach is derived from the interpolator's actual selectable node spans, and RINEX 4 CNAV week/TOW round trips are stable at the week boundary.
|
|
22
|
+
|
|
7
23
|
## [2.0.0] - 2026-09-03
|
|
8
24
|
|
|
9
25
|
### Changed
|
|
@@ -588,9 +588,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
|
|
588
588
|
|
|
589
589
|
[[package]]
|
|
590
590
|
name = "sidereon"
|
|
591
|
-
version = "2.
|
|
591
|
+
version = "2.1.0"
|
|
592
592
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
593
|
-
checksum = "
|
|
593
|
+
checksum = "34f5f297673a6c59dd5f22fa93124f8502a1ffa39b4c5c3c2f75d764a2ead2d0"
|
|
594
594
|
dependencies = [
|
|
595
595
|
"crc32fast",
|
|
596
596
|
"flate2",
|
|
@@ -599,9 +599,9 @@ dependencies = [
|
|
|
599
599
|
|
|
600
600
|
[[package]]
|
|
601
601
|
name = "sidereon-core"
|
|
602
|
-
version = "2.
|
|
602
|
+
version = "2.1.0"
|
|
603
603
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
604
|
-
checksum = "
|
|
604
|
+
checksum = "f6dea92a59883415b579bded7065f8f233c4d069a1677c629685eef7b6620d17"
|
|
605
605
|
dependencies = [
|
|
606
606
|
"approx",
|
|
607
607
|
"cc",
|
|
@@ -622,7 +622,7 @@ dependencies = [
|
|
|
622
622
|
|
|
623
623
|
[[package]]
|
|
624
624
|
name = "sidereon-py"
|
|
625
|
-
version = "2.
|
|
625
|
+
version = "2.1.0"
|
|
626
626
|
dependencies = [
|
|
627
627
|
"nalgebra",
|
|
628
628
|
"numpy",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "sidereon-py"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.1.0"
|
|
4
4
|
authors = []
|
|
5
5
|
edition = "2021"
|
|
6
6
|
description = "Python (PyO3) bindings over the sidereon ergonomic API: SP3 loading and SPP/RTK/PPP positioning solves"
|
|
@@ -21,8 +21,8 @@ exact-cache-test-failpoints = ["sidereon-core/exact-cache-test-failpoints"]
|
|
|
21
21
|
# The thin ergonomic engine surface this binding marshals to. No modeling logic
|
|
22
22
|
# lives here: every solve delegates to a `sidereon` entry point, which in turn
|
|
23
23
|
# delegates to `sidereon-core`.
|
|
24
|
-
sidereon = "2.
|
|
25
|
-
sidereon-core = { version = "2.
|
|
24
|
+
sidereon = "2.1.0"
|
|
25
|
+
sidereon-core = { version = "2.1.0", features = ["mmap"] }
|
|
26
26
|
# The standalone general-purpose trust-region least-squares solver: a data-driven
|
|
27
27
|
# `least_squares` whose residual and Jacobian are evaluated entirely in Rust, so
|
|
28
28
|
# no per-iteration Python callback is paid.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sidereon
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Topic :: Scientific/Engineering
|
|
@@ -157,9 +157,12 @@ The Python package mirrors the full breadth of the engine.
|
|
|
157
157
|
decode and SSR store helpers, and NTRIP client stream handling.
|
|
158
158
|
- **Ephemeris and time:** broadcast ephemeris and precise SP3 products, JPL SPK
|
|
159
159
|
(DAF/.bsp) kernels, uniform satellite-state sampling across broadcast and
|
|
160
|
-
precise sources with batched multi-satellite interpolation,
|
|
161
|
-
|
|
162
|
-
and
|
|
160
|
+
precise sources with batched multi-satellite interpolation, configurable
|
|
161
|
+
SP3 coverage-gap interpolation policy (`gap_threshold_factor`, default 1.5)
|
|
162
|
+
and window-scoped continuity verification options (`check_continuity` and
|
|
163
|
+
`continuity_verdict`), scale-aware time (UTC/TAI/TT/TDB/UT1 and the GNSS
|
|
164
|
+
system times) with leap-second handling, and Earth orientation parameters
|
|
165
|
+
(EOP).
|
|
163
166
|
- **Timing and clocks:** Allan-family stability analysis (ADEV/MDEV/HDEV/TDEV)
|
|
164
167
|
and power-law clock-noise identification with a five-coefficient fit
|
|
165
168
|
(IEEE 1139).
|
|
@@ -138,9 +138,12 @@ The Python package mirrors the full breadth of the engine.
|
|
|
138
138
|
decode and SSR store helpers, and NTRIP client stream handling.
|
|
139
139
|
- **Ephemeris and time:** broadcast ephemeris and precise SP3 products, JPL SPK
|
|
140
140
|
(DAF/.bsp) kernels, uniform satellite-state sampling across broadcast and
|
|
141
|
-
precise sources with batched multi-satellite interpolation,
|
|
142
|
-
|
|
143
|
-
and
|
|
141
|
+
precise sources with batched multi-satellite interpolation, configurable
|
|
142
|
+
SP3 coverage-gap interpolation policy (`gap_threshold_factor`, default 1.5)
|
|
143
|
+
and window-scoped continuity verification options (`check_continuity` and
|
|
144
|
+
`continuity_verdict`), scale-aware time (UTC/TAI/TT/TDB/UT1 and the GNSS
|
|
145
|
+
system times) with leap-second handling, and Earth orientation parameters
|
|
146
|
+
(EOP).
|
|
144
147
|
- **Timing and clocks:** Allan-family stability analysis (ADEV/MDEV/HDEV/TDEV)
|
|
145
148
|
and power-law clock-noise identification with a five-coefficient fit
|
|
146
149
|
(IEEE 1139).
|
|
@@ -110,6 +110,24 @@ class Sp3:
|
|
|
110
110
|
@property
|
|
111
111
|
def satellites(self) -> list[str]: ...
|
|
112
112
|
@property
|
|
113
|
+
def gap_threshold_factor(self) -> float: ...
|
|
114
|
+
def with_interpolation_options(self, gap_threshold_factor: float) -> Sp3: ...
|
|
115
|
+
def check_continuity(
|
|
116
|
+
self,
|
|
117
|
+
orbit_class: str | None = ...,
|
|
118
|
+
residual_tolerance_m: float | None = ...,
|
|
119
|
+
gap_threshold_factor: float | None = ...,
|
|
120
|
+
) -> dict[str, Any]: ...
|
|
121
|
+
def stencil_extent(self) -> tuple[float, float]: ...
|
|
122
|
+
def continuity_verdict(
|
|
123
|
+
self,
|
|
124
|
+
from_j2000_s: float,
|
|
125
|
+
through_j2000_s: float,
|
|
126
|
+
orbit_class: str | None = ...,
|
|
127
|
+
residual_tolerance_m: float | None = ...,
|
|
128
|
+
gap_threshold_factor: float | None = ...,
|
|
129
|
+
) -> dict[str, Any]: ...
|
|
130
|
+
@property
|
|
113
131
|
def epochs_j2000_seconds(self) -> npt.NDArray[np.float64]: ...
|
|
114
132
|
def interpolate(
|
|
115
133
|
self, satellite: str, j2000_seconds: npt.NDArray[np.float64]
|
|
@@ -118,7 +136,9 @@ class Sp3:
|
|
|
118
136
|
def prediction_summary(self) -> Sp3PredictionSummary: ...
|
|
119
137
|
def to_sp3_string(self) -> str: ...
|
|
120
138
|
def precise_ephemeris_samples(self) -> list[PreciseEphemerisSample]: ...
|
|
121
|
-
def precise_interpolant_artifact_bytes(
|
|
139
|
+
def precise_interpolant_artifact_bytes(
|
|
140
|
+
self, gap_threshold_factor: float | None = ...
|
|
141
|
+
) -> bytes: ...
|
|
122
142
|
def __repr__(self) -> str: ...
|
|
123
143
|
|
|
124
144
|
class ExactSp3ValidationError(ValueError): ...
|
|
@@ -248,6 +268,12 @@ class PreciseEphemerisSamples:
|
|
|
248
268
|
@staticmethod
|
|
249
269
|
def from_samples(
|
|
250
270
|
samples: Sequence[PreciseEphemerisSample],
|
|
271
|
+
gap_threshold_factor: float | None = ...,
|
|
272
|
+
) -> PreciseEphemerisSamples: ...
|
|
273
|
+
@property
|
|
274
|
+
def gap_threshold_factor(self) -> float: ...
|
|
275
|
+
def with_interpolation_options(
|
|
276
|
+
self, gap_threshold_factor: float
|
|
251
277
|
) -> PreciseEphemerisSamples: ...
|
|
252
278
|
@property
|
|
253
279
|
def time_scale(self) -> TimeScale: ...
|
|
@@ -523,14 +549,24 @@ class PreciseEphemerisInterpolant:
|
|
|
523
549
|
"""Reusable precise-ephemeris interpolant with cached per-satellite nodes."""
|
|
524
550
|
|
|
525
551
|
@staticmethod
|
|
526
|
-
def from_sp3(
|
|
552
|
+
def from_sp3(
|
|
553
|
+
source: Sp3,
|
|
554
|
+
gap_threshold_factor: float | None = ...,
|
|
555
|
+
) -> PreciseEphemerisInterpolant: ...
|
|
527
556
|
@staticmethod
|
|
528
557
|
def from_samples(
|
|
529
558
|
samples: Sequence[PreciseEphemerisSample],
|
|
559
|
+
gap_threshold_factor: float | None = ...,
|
|
530
560
|
) -> PreciseEphemerisInterpolant: ...
|
|
531
561
|
@staticmethod
|
|
532
562
|
def from_precise_ephemeris_samples(
|
|
533
563
|
source: PreciseEphemerisSamples,
|
|
564
|
+
gap_threshold_factor: float | None = ...,
|
|
565
|
+
) -> PreciseEphemerisInterpolant: ...
|
|
566
|
+
@property
|
|
567
|
+
def gap_threshold_factor(self) -> float: ...
|
|
568
|
+
def with_interpolation_options(
|
|
569
|
+
self, gap_threshold_factor: float
|
|
534
570
|
) -> PreciseEphemerisInterpolant: ...
|
|
535
571
|
@property
|
|
536
572
|
def time_scale(self) -> TimeScale: ...
|
|
@@ -568,6 +604,8 @@ class PreciseInterpolantArtifact:
|
|
|
568
604
|
def digest_provenance(self) -> Literal["verified", "attested"]: ...
|
|
569
605
|
def verify(self) -> None: ...
|
|
570
606
|
@property
|
|
607
|
+
def gap_threshold_factor(self) -> float: ...
|
|
608
|
+
@property
|
|
571
609
|
def time_scale(self) -> TimeScale: ...
|
|
572
610
|
@property
|
|
573
611
|
def satellites(self) -> list[str]: ...
|
|
@@ -4955,13 +4993,19 @@ def leap_second_table_info() -> LeapSecondTable: ...
|
|
|
4955
4993
|
def ut1_coverage_info() -> Ut1Coverage: ...
|
|
4956
4994
|
def timescale_offset(from_: TimeScale, to: TimeScale) -> float: ...
|
|
4957
4995
|
def timescale_offset_at(from_: TimeScale, to: TimeScale, utc_jd: float) -> float: ...
|
|
4958
|
-
def load_sp3(
|
|
4996
|
+
def load_sp3(
|
|
4997
|
+
source: bytes | bytearray | str | os.PathLike[str],
|
|
4998
|
+
gap_threshold_factor: float | None = ...,
|
|
4999
|
+
) -> Sp3: ...
|
|
4959
5000
|
def parse_exact_sp3(
|
|
4960
5001
|
source: bytes | bytearray | str | os.PathLike[str],
|
|
4961
5002
|
request: ExactSp3Request,
|
|
4962
5003
|
) -> tuple[Sp3, ExactSp3Coverage]: ...
|
|
4963
5004
|
def validate_exact_sp3(sp3: Sp3, request: ExactSp3Request) -> ExactSp3Coverage: ...
|
|
4964
|
-
def build_precise_interpolant_artifact_bytes(
|
|
5005
|
+
def build_precise_interpolant_artifact_bytes(
|
|
5006
|
+
sp3: Sp3,
|
|
5007
|
+
gap_threshold_factor: float | None = ...,
|
|
5008
|
+
) -> bytes: ...
|
|
4965
5009
|
def observable_states_at_j2000_s(
|
|
4966
5010
|
source: (
|
|
4967
5011
|
Sp3 | PreciseEphemerisSamples | PreciseEphemerisInterpolant | BroadcastEphemeris
|
|
@@ -34,7 +34,8 @@ use sidereon_core::ephemeris::{
|
|
|
34
34
|
};
|
|
35
35
|
use sidereon_core::ephemeris::{
|
|
36
36
|
check_continuity, ContinuityDefect, ContinuityOptions, EpochWindow, MergeContinuityViolation,
|
|
37
|
-
OrbitClass, SpeedBound, StencilExtent, WindowContinuityDecision,
|
|
37
|
+
OrbitClass, Sp3InterpolationOptions, SpeedBound, StencilExtent, WindowContinuityDecision,
|
|
38
|
+
WindowContinuityVerdict,
|
|
38
39
|
};
|
|
39
40
|
use sidereon_core::DigestProvenance;
|
|
40
41
|
use sidereon_core::Error as CoreError;
|
|
@@ -137,6 +138,7 @@ fn parse_sat(token: &str) -> PyResult<GnssSatelliteId> {
|
|
|
137
138
|
fn continuity_options(
|
|
138
139
|
orbit_class: Option<&str>,
|
|
139
140
|
residual_tolerance_m: Option<f64>,
|
|
141
|
+
gap_threshold_factor: Option<f64>,
|
|
140
142
|
) -> PyResult<ContinuityOptions> {
|
|
141
143
|
let speed_bound = match orbit_class {
|
|
142
144
|
None => None,
|
|
@@ -152,6 +154,11 @@ fn continuity_options(
|
|
|
152
154
|
let mut options = ContinuityOptions::new(speed_bound, residual_tolerance_m);
|
|
153
155
|
options.speed_bound = speed_bound;
|
|
154
156
|
options.residual_tolerance_m = residual_tolerance_m;
|
|
157
|
+
if let Some(factor) = gap_threshold_factor {
|
|
158
|
+
let interpolation = Sp3InterpolationOptions::new(factor)
|
|
159
|
+
.map_err(|error| PyValueError::new_err(error.to_string()))?;
|
|
160
|
+
options = options.with_interpolation_options(interpolation);
|
|
161
|
+
}
|
|
155
162
|
Ok(options)
|
|
156
163
|
}
|
|
157
164
|
|
|
@@ -307,6 +314,21 @@ impl PySp3 {
|
|
|
307
314
|
.collect()
|
|
308
315
|
}
|
|
309
316
|
|
|
317
|
+
/// SP3 interpolation gap threshold factor carried by this product.
|
|
318
|
+
#[getter]
|
|
319
|
+
fn gap_threshold_factor(&self) -> f64 {
|
|
320
|
+
self.inner.interpolation_options().gap_threshold_factor()
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/// Return a copy of this product with an explicit gap threshold factor.
|
|
324
|
+
fn with_interpolation_options(&self, gap_threshold_factor: f64) -> PyResult<Self> {
|
|
325
|
+
let options = Sp3InterpolationOptions::new(gap_threshold_factor)
|
|
326
|
+
.map_err(|error| PyValueError::new_err(error.to_string()))?;
|
|
327
|
+
Ok(Self {
|
|
328
|
+
inner: self.inner.clone().with_interpolation_options(options),
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
|
|
310
332
|
/// The product's parsed epochs as seconds since J2000, in the file's own time
|
|
311
333
|
/// scale, ascending, as a numpy `(n,)` `float64` array.
|
|
312
334
|
///
|
|
@@ -330,21 +352,28 @@ impl PySp3 {
|
|
|
330
352
|
/// `orbit_class` is one of `"meo_gnss"` (default), `"geosynchronous"`,
|
|
331
353
|
/// `"leo"`, or `None` to disable the speed gate.
|
|
332
354
|
/// `residual_tolerance_m` enables the residual check; `None` disables it.
|
|
355
|
+
/// `gap_threshold_factor` configures the hold-out interpolation policy;
|
|
356
|
+
/// `None` leaves the core default (1.5).
|
|
333
357
|
///
|
|
334
358
|
/// Returns a dict with `defects`, `attested`, and the counts of what was
|
|
335
359
|
/// examined, so "checked and clean" stays distinguishable from "not
|
|
336
360
|
/// checked". This reports rather than refuses: whether a product with
|
|
337
361
|
/// defects is acceptable is the caller's decision.
|
|
338
|
-
#[pyo3(signature = (
|
|
362
|
+
#[pyo3(signature = (
|
|
363
|
+
orbit_class = "meo_gnss",
|
|
364
|
+
residual_tolerance_m = Some(1.0),
|
|
365
|
+
gap_threshold_factor = None,
|
|
366
|
+
))]
|
|
339
367
|
fn check_continuity(
|
|
340
368
|
&self,
|
|
341
369
|
py: Python<'_>,
|
|
342
370
|
orbit_class: Option<&str>,
|
|
343
371
|
residual_tolerance_m: Option<f64>,
|
|
372
|
+
gap_threshold_factor: Option<f64>,
|
|
344
373
|
) -> PyResult<PyObject> {
|
|
345
374
|
let report = check_continuity(
|
|
346
375
|
&self.inner.precise_ephemeris_samples(),
|
|
347
|
-
&continuity_options(orbit_class, residual_tolerance_m)?,
|
|
376
|
+
&continuity_options(orbit_class, residual_tolerance_m, gap_threshold_factor)?,
|
|
348
377
|
);
|
|
349
378
|
|
|
350
379
|
let defects = continuity_defects_to_list(py, &report.defects)?;
|
|
@@ -377,6 +406,7 @@ impl PySp3 {
|
|
|
377
406
|
through_j2000_s,
|
|
378
407
|
orbit_class = "meo_gnss",
|
|
379
408
|
residual_tolerance_m = Some(1.0),
|
|
409
|
+
gap_threshold_factor = None,
|
|
380
410
|
))]
|
|
381
411
|
fn continuity_verdict(
|
|
382
412
|
&self,
|
|
@@ -385,6 +415,7 @@ impl PySp3 {
|
|
|
385
415
|
through_j2000_s: f64,
|
|
386
416
|
orbit_class: Option<&str>,
|
|
387
417
|
residual_tolerance_m: Option<f64>,
|
|
418
|
+
gap_threshold_factor: Option<f64>,
|
|
388
419
|
) -> PyResult<PyObject> {
|
|
389
420
|
let window = EpochWindow::new(from_j2000_s, through_j2000_s)
|
|
390
421
|
.map_err(|error| PyValueError::new_err(error.to_string()))?;
|
|
@@ -392,7 +423,7 @@ impl PySp3 {
|
|
|
392
423
|
.map_err(|error| PyValueError::new_err(error.to_string()))?;
|
|
393
424
|
let report = check_continuity(
|
|
394
425
|
&self.inner.precise_ephemeris_samples(),
|
|
395
|
-
&continuity_options(orbit_class, residual_tolerance_m)?,
|
|
426
|
+
&continuity_options(orbit_class, residual_tolerance_m, gap_threshold_factor)?,
|
|
396
427
|
);
|
|
397
428
|
continuity_verdict_to_py(py, report.verdict_for_window(window, stencil))
|
|
398
429
|
}
|
|
@@ -528,12 +559,19 @@ impl PySp3 {
|
|
|
528
559
|
}
|
|
529
560
|
|
|
530
561
|
/// Build deterministic memory-mappable precise-interpolant artifact bytes.
|
|
562
|
+
#[pyo3(signature = (gap_threshold_factor = None))]
|
|
531
563
|
fn precise_interpolant_artifact_bytes<'py>(
|
|
532
564
|
&self,
|
|
533
565
|
py: Python<'py>,
|
|
566
|
+
gap_threshold_factor: Option<f64>,
|
|
534
567
|
) -> PyResult<Bound<'py, PyBytes>> {
|
|
535
|
-
let
|
|
536
|
-
|
|
568
|
+
let mut product = self.inner.clone();
|
|
569
|
+
if let Some(factor) = gap_threshold_factor {
|
|
570
|
+
let options = Sp3InterpolationOptions::new(factor)
|
|
571
|
+
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
572
|
+
product = product.with_interpolation_options(options);
|
|
573
|
+
}
|
|
574
|
+
let bytes = product
|
|
537
575
|
.precise_interpolant_store_bytes()
|
|
538
576
|
.map_err(precise_artifact_error_without_bytes)?;
|
|
539
577
|
Ok(PyBytes::new(py, &bytes))
|
|
@@ -1098,13 +1136,38 @@ impl PyPreciseEphemerisSamples {
|
|
|
1098
1136
|
/// scales, a sample is non-finite, or an epoch is not representable as J2000
|
|
1099
1137
|
/// seconds.
|
|
1100
1138
|
#[staticmethod]
|
|
1101
|
-
|
|
1139
|
+
#[pyo3(signature = (samples, gap_threshold_factor = None))]
|
|
1140
|
+
fn from_samples(
|
|
1141
|
+
py: Python<'_>,
|
|
1142
|
+
samples: Vec<Py<PyPreciseEphemerisSample>>,
|
|
1143
|
+
gap_threshold_factor: Option<f64>,
|
|
1144
|
+
) -> PyResult<Self> {
|
|
1102
1145
|
let samples = samples.iter().map(|s| s.borrow(py).to_core());
|
|
1103
|
-
let inner = PreciseEphemerisSamples::from_samples(samples)
|
|
1146
|
+
let mut inner = PreciseEphemerisSamples::from_samples(samples)
|
|
1104
1147
|
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1148
|
+
if let Some(factor) = gap_threshold_factor {
|
|
1149
|
+
let options = Sp3InterpolationOptions::new(factor)
|
|
1150
|
+
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1151
|
+
inner = inner.with_interpolation_options(options);
|
|
1152
|
+
}
|
|
1105
1153
|
Ok(Self { inner })
|
|
1106
1154
|
}
|
|
1107
1155
|
|
|
1156
|
+
/// SP3 interpolation gap threshold factor carried by this source.
|
|
1157
|
+
#[getter]
|
|
1158
|
+
fn gap_threshold_factor(&self) -> f64 {
|
|
1159
|
+
self.inner.interpolation_options().gap_threshold_factor()
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
/// Return a copy of these samples with an explicit gap threshold factor.
|
|
1163
|
+
fn with_interpolation_options(&self, gap_threshold_factor: f64) -> PyResult<Self> {
|
|
1164
|
+
let options = Sp3InterpolationOptions::new(gap_threshold_factor)
|
|
1165
|
+
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1166
|
+
Ok(Self {
|
|
1167
|
+
inner: self.inner.clone().with_interpolation_options(options),
|
|
1168
|
+
})
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1108
1171
|
/// Time scale every sample epoch is expressed in.
|
|
1109
1172
|
#[getter]
|
|
1110
1173
|
fn time_scale(&self) -> PyTimeScale {
|
|
@@ -1141,27 +1204,65 @@ pub struct PyPreciseEphemerisInterpolant {
|
|
|
1141
1204
|
impl PyPreciseEphemerisInterpolant {
|
|
1142
1205
|
/// Build a cached interpolant from a parsed SP3 product.
|
|
1143
1206
|
#[staticmethod]
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1207
|
+
#[pyo3(signature = (source, gap_threshold_factor = None))]
|
|
1208
|
+
fn from_sp3(source: &PySp3, gap_threshold_factor: Option<f64>) -> PyResult<Self> {
|
|
1209
|
+
let mut inner = PreciseEphemerisInterpolant::from_sp3(&source.inner);
|
|
1210
|
+
if let Some(factor) = gap_threshold_factor {
|
|
1211
|
+
let options = Sp3InterpolationOptions::new(factor)
|
|
1212
|
+
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1213
|
+
inner = inner.with_interpolation_options(options);
|
|
1147
1214
|
}
|
|
1215
|
+
Ok(Self { inner })
|
|
1148
1216
|
}
|
|
1149
1217
|
|
|
1150
1218
|
/// Build a cached interpolant directly from precise-ephemeris samples.
|
|
1151
1219
|
#[staticmethod]
|
|
1152
|
-
|
|
1220
|
+
#[pyo3(signature = (samples, gap_threshold_factor = None))]
|
|
1221
|
+
fn from_samples(
|
|
1222
|
+
py: Python<'_>,
|
|
1223
|
+
samples: Vec<Py<PyPreciseEphemerisSample>>,
|
|
1224
|
+
gap_threshold_factor: Option<f64>,
|
|
1225
|
+
) -> PyResult<Self> {
|
|
1153
1226
|
let samples = samples.iter().map(|s| s.borrow(py).to_core());
|
|
1154
|
-
let inner = PreciseEphemerisInterpolant::from_samples(samples)
|
|
1227
|
+
let mut inner = PreciseEphemerisInterpolant::from_samples(samples)
|
|
1155
1228
|
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1229
|
+
if let Some(factor) = gap_threshold_factor {
|
|
1230
|
+
let options = Sp3InterpolationOptions::new(factor)
|
|
1231
|
+
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1232
|
+
inner = inner.with_interpolation_options(options);
|
|
1233
|
+
}
|
|
1156
1234
|
Ok(Self { inner })
|
|
1157
1235
|
}
|
|
1158
1236
|
|
|
1159
1237
|
/// Build a cached interpolant from an existing sample-backed source.
|
|
1160
1238
|
#[staticmethod]
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1239
|
+
#[pyo3(signature = (source, gap_threshold_factor = None))]
|
|
1240
|
+
fn from_precise_ephemeris_samples(
|
|
1241
|
+
source: &PyPreciseEphemerisSamples,
|
|
1242
|
+
gap_threshold_factor: Option<f64>,
|
|
1243
|
+
) -> PyResult<Self> {
|
|
1244
|
+
let mut inner = PreciseEphemerisInterpolant::from_precise_ephemeris_samples(&source.inner);
|
|
1245
|
+
if let Some(factor) = gap_threshold_factor {
|
|
1246
|
+
let options = Sp3InterpolationOptions::new(factor)
|
|
1247
|
+
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1248
|
+
inner = inner.with_interpolation_options(options);
|
|
1164
1249
|
}
|
|
1250
|
+
Ok(Self { inner })
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/// SP3 interpolation gap threshold factor carried by this interpolant.
|
|
1254
|
+
#[getter]
|
|
1255
|
+
fn gap_threshold_factor(&self) -> f64 {
|
|
1256
|
+
self.inner.interpolation_options().gap_threshold_factor()
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/// Return a copy of this interpolant with an explicit gap threshold factor.
|
|
1260
|
+
fn with_interpolation_options(&self, gap_threshold_factor: f64) -> PyResult<Self> {
|
|
1261
|
+
let options = Sp3InterpolationOptions::new(gap_threshold_factor)
|
|
1262
|
+
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1263
|
+
Ok(Self {
|
|
1264
|
+
inner: self.inner.clone().with_interpolation_options(options),
|
|
1265
|
+
})
|
|
1165
1266
|
}
|
|
1166
1267
|
|
|
1167
1268
|
/// Time scale of the source epochs used to build this handle.
|
|
@@ -1324,6 +1425,12 @@ impl PyPreciseInterpolantArtifact {
|
|
|
1324
1425
|
self.inner.time_scale().into()
|
|
1325
1426
|
}
|
|
1326
1427
|
|
|
1428
|
+
/// SP3 interpolation gap threshold factor read from the artifact header.
|
|
1429
|
+
#[getter]
|
|
1430
|
+
fn gap_threshold_factor(&self) -> f64 {
|
|
1431
|
+
self.inner.interpolation_options().gap_threshold_factor()
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1327
1434
|
/// Satellite tokens present in the artifact.
|
|
1328
1435
|
#[getter]
|
|
1329
1436
|
fn satellites(&self) -> Vec<String> {
|
|
@@ -1465,28 +1572,35 @@ fn precise_artifact_error(err: PreciseInterpolantStoreError, truncated: bool) ->
|
|
|
1465
1572
|
/// directly; or
|
|
1466
1573
|
/// - a path (`str` or `os.PathLike`): the file is read and parsed.
|
|
1467
1574
|
///
|
|
1575
|
+
/// `gap_threshold_factor` optionally configures the product-carried SP3
|
|
1576
|
+
/// coverage-gap interpolation policy; `None` leaves the core default (1.5).
|
|
1577
|
+
///
|
|
1468
1578
|
/// Raises [`Sp3ParseError`](crate::Sp3ParseError) on malformed content, `OSError`
|
|
1469
|
-
/// if the path cannot be read,
|
|
1470
|
-
/// path.
|
|
1579
|
+
/// if the path cannot be read, `ValueError` if `gap_threshold_factor` is not
|
|
1580
|
+
/// finite or <= 1.0, and `TypeError` if `source` is neither bytes nor a path.
|
|
1471
1581
|
#[pyfunction]
|
|
1472
|
-
|
|
1582
|
+
#[pyo3(signature = (source, gap_threshold_factor = None))]
|
|
1583
|
+
fn load_sp3(source: &Bound<'_, PyAny>, gap_threshold_factor: Option<f64>) -> PyResult<PySp3> {
|
|
1473
1584
|
// bytes-like first, so a `bytes` argument keeps the prior "content" meaning.
|
|
1474
|
-
if let Ok(bytes) = source.downcast::<PyBytes>() {
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
}
|
|
1478
|
-
if let Ok(buf) = source.downcast::<PyByteArray>() {
|
|
1585
|
+
let mut inner = if let Ok(bytes) = source.downcast::<PyBytes>() {
|
|
1586
|
+
sidereon::load_sp3(bytes.as_bytes()).map_err(to_sp3_err)?
|
|
1587
|
+
} else if let Ok(buf) = source.downcast::<PyByteArray>() {
|
|
1479
1588
|
// SAFETY: the buffer is copied into the parser synchronously here; no
|
|
1480
1589
|
// Python code runs in between to mutate or free it.
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1590
|
+
sidereon::load_sp3(unsafe { buf.as_bytes() }).map_err(to_sp3_err)?
|
|
1591
|
+
} else {
|
|
1592
|
+
// Otherwise treat it as a path (str / os.PathLike via PyO3's fspath support).
|
|
1593
|
+
let path: PathBuf = source.extract().map_err(|_| {
|
|
1594
|
+
PyValueError::new_err("load_sp3 expects bytes, bytearray, or a path (str/os.PathLike)")
|
|
1595
|
+
})?;
|
|
1596
|
+
let data = std::fs::read(&path)?;
|
|
1597
|
+
sidereon::load_sp3(&data).map_err(to_sp3_err)?
|
|
1598
|
+
};
|
|
1599
|
+
if let Some(factor) = gap_threshold_factor {
|
|
1600
|
+
let options = Sp3InterpolationOptions::new(factor)
|
|
1601
|
+
.map_err(|e| PyValueError::new_err(e.to_string()))?;
|
|
1602
|
+
inner = inner.with_interpolation_options(options);
|
|
1603
|
+
}
|
|
1490
1604
|
Ok(PySp3 { inner })
|
|
1491
1605
|
}
|
|
1492
1606
|
|
|
@@ -1645,11 +1759,13 @@ fn _validate_exact_sp3(
|
|
|
1645
1759
|
|
|
1646
1760
|
/// Build deterministic precise-interpolant artifact bytes from an SP3 product.
|
|
1647
1761
|
#[pyfunction]
|
|
1762
|
+
#[pyo3(signature = (sp3, gap_threshold_factor = None))]
|
|
1648
1763
|
fn build_precise_interpolant_artifact_bytes<'py>(
|
|
1649
1764
|
py: Python<'py>,
|
|
1650
1765
|
sp3: &PySp3,
|
|
1766
|
+
gap_threshold_factor: Option<f64>,
|
|
1651
1767
|
) -> PyResult<Bound<'py, PyBytes>> {
|
|
1652
|
-
sp3.precise_interpolant_artifact_bytes(py)
|
|
1768
|
+
sp3.precise_interpolant_artifact_bytes(py, gap_threshold_factor)
|
|
1653
1769
|
}
|
|
1654
1770
|
|
|
1655
1771
|
pub(crate) fn parse_satellites(tokens: &[String]) -> PyResult<Vec<GnssSatelliteId>> {
|