sidereon 3.0.1__tar.gz → 3.0.2__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.
Files changed (345) hide show
  1. {sidereon-3.0.1 → sidereon-3.0.2}/.github/workflows/release.yml +40 -4
  2. {sidereon-3.0.1 → sidereon-3.0.2}/CHANGELOG.md +7 -0
  3. {sidereon-3.0.1 → sidereon-3.0.2}/Cargo.lock +5 -5
  4. {sidereon-3.0.1 → sidereon-3.0.2}/Cargo.toml +4 -4
  5. {sidereon-3.0.1 → sidereon-3.0.2}/PKG-INFO +1 -1
  6. {sidereon-3.0.1 → sidereon-3.0.2}/THIRD-PARTY-NOTICES.md +3 -3
  7. {sidereon-3.0.1 → sidereon-3.0.2}/pyproject.toml +4 -4
  8. {sidereon-3.0.1 → sidereon-3.0.2}/scripts/check-release.py +4 -4
  9. {sidereon-3.0.1 → sidereon-3.0.2}/scripts/core_source_identity.rs +1 -1
  10. sidereon-3.0.2/tests/fixtures/bodies/observe_de.bsp +0 -0
  11. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/core_goldens.json +1 -1
  12. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/ppp_esbc.json +1 -1
  13. sidereon-3.0.2/tests/test_body_observe_reference.py +311 -0
  14. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_core_revision.py +38 -23
  15. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_release_guard.py +1 -0
  16. {sidereon-3.0.1 → sidereon-3.0.2}/uv.lock +1 -1
  17. {sidereon-3.0.1 → sidereon-3.0.2}/.cargo/config.toml.disabled +0 -0
  18. {sidereon-3.0.1 → sidereon-3.0.2}/.gitattributes +0 -0
  19. {sidereon-3.0.1 → sidereon-3.0.2}/.github/workflows/ci.yml +0 -0
  20. {sidereon-3.0.1 → sidereon-3.0.2}/.gitignore +0 -0
  21. {sidereon-3.0.1 → sidereon-3.0.2}/COVERAGE.md +0 -0
  22. {sidereon-3.0.1 → sidereon-3.0.2}/LICENSE +0 -0
  23. {sidereon-3.0.1 → sidereon-3.0.2}/README.md +0 -0
  24. {sidereon-3.0.1 → sidereon-3.0.2}/bench/README.md +0 -0
  25. {sidereon-3.0.1 → sidereon-3.0.2}/bench/bench_fleet.py +0 -0
  26. {sidereon-3.0.1 → sidereon-3.0.2}/docs/antex.md +0 -0
  27. {sidereon-3.0.1 → sidereon-3.0.2}/docs/observations.md +0 -0
  28. {sidereon-3.0.1 → sidereon-3.0.2}/docs/public-gnss-distribution-sources.md +0 -0
  29. {sidereon-3.0.1 → sidereon-3.0.2}/docs/releasing.md +0 -0
  30. {sidereon-3.0.1 → sidereon-3.0.2}/docs/rinex-clock.md +0 -0
  31. {sidereon-3.0.1 → sidereon-3.0.2}/docs/sp3.md +0 -0
  32. {sidereon-3.0.1 → sidereon-3.0.2}/docs/tdm.md +0 -0
  33. {sidereon-3.0.1 → sidereon-3.0.2}/examples/clean_a_phone_track.ipynb +0 -0
  34. {sidereon-3.0.1 → sidereon-3.0.2}/examples/static_occupation_vs_cors.ipynb +0 -0
  35. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/__init__.py +0 -0
  36. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/__init__.pyi +0 -0
  37. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/_compression.py +0 -0
  38. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/_exact_cache.py +0 -0
  39. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/_ingress.py +0 -0
  40. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/data.py +0 -0
  41. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/data.pyi +0 -0
  42. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/distribution.py +0 -0
  43. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/distribution.pyi +0 -0
  44. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/exact_cache.py +0 -0
  45. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/exact_cache.pyi +0 -0
  46. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/exact_sp3.py +0 -0
  47. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/exact_sp3.pyi +0 -0
  48. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/ntrip.py +0 -0
  49. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/ntrip.pyi +0 -0
  50. {sidereon-3.0.1 → sidereon-3.0.2}/python/sidereon/py.typed +0 -0
  51. {sidereon-3.0.1 → sidereon-3.0.2}/rust-toolchain.toml +0 -0
  52. {sidereon-3.0.1 → sidereon-3.0.2}/scripts/coverage/bindmap.py +0 -0
  53. {sidereon-3.0.1 → sidereon-3.0.2}/scripts/coverage/pubapi.py +0 -0
  54. {sidereon-3.0.1 → sidereon-3.0.2}/scripts/coverage/render_coverage.py +0 -0
  55. {sidereon-3.0.1 → sidereon-3.0.2}/scripts/smoke-release.py +0 -0
  56. {sidereon-3.0.1 → sidereon-3.0.2}/src/almanac.rs +0 -0
  57. {sidereon-3.0.1 → sidereon-3.0.2}/src/angles.rs +0 -0
  58. {sidereon-3.0.1 → sidereon-3.0.2}/src/anomaly.rs +0 -0
  59. {sidereon-3.0.1 → sidereon-3.0.2}/src/antex.rs +0 -0
  60. {sidereon-3.0.1 → sidereon-3.0.2}/src/araim.rs +0 -0
  61. {sidereon-3.0.1 → sidereon-3.0.2}/src/atmosphere.rs +0 -0
  62. {sidereon-3.0.1 → sidereon-3.0.2}/src/bias.rs +0 -0
  63. {sidereon-3.0.1 → sidereon-3.0.2}/src/bodies.rs +0 -0
  64. {sidereon-3.0.1 → sidereon-3.0.2}/src/body_observe.rs +0 -0
  65. {sidereon-3.0.1 → sidereon-3.0.2}/src/broadcast_comparison.rs +0 -0
  66. {sidereon-3.0.1 → sidereon-3.0.2}/src/cdm.rs +0 -0
  67. {sidereon-3.0.1 → sidereon-3.0.2}/src/clock_stability.rs +0 -0
  68. {sidereon-3.0.1 → sidereon-3.0.2}/src/conjunction.rs +0 -0
  69. {sidereon-3.0.1 → sidereon-3.0.2}/src/constellation.rs +0 -0
  70. {sidereon-3.0.1 → sidereon-3.0.2}/src/core_error_detail.rs +0 -0
  71. {sidereon-3.0.1 → sidereon-3.0.2}/src/covariance.rs +0 -0
  72. {sidereon-3.0.1 → sidereon-3.0.2}/src/coverage.rs +0 -0
  73. {sidereon-3.0.1 → sidereon-3.0.2}/src/data_catalog.rs +0 -0
  74. {sidereon-3.0.1 → sidereon-3.0.2}/src/defaults.rs +0 -0
  75. {sidereon-3.0.1 → sidereon-3.0.2}/src/dgnss.rs +0 -0
  76. {sidereon-3.0.1 → sidereon-3.0.2}/src/doppler.rs +0 -0
  77. {sidereon-3.0.1 → sidereon-3.0.2}/src/elements.rs +0 -0
  78. {sidereon-3.0.1 → sidereon-3.0.2}/src/emission.rs +0 -0
  79. {sidereon-3.0.1 → sidereon-3.0.2}/src/ephemeris.rs +0 -0
  80. {sidereon-3.0.1 → sidereon-3.0.2}/src/equinoctial.rs +0 -0
  81. {sidereon-3.0.1 → sidereon-3.0.2}/src/error_metrics.rs +0 -0
  82. {sidereon-3.0.1 → sidereon-3.0.2}/src/estimation.rs +0 -0
  83. {sidereon-3.0.1 → sidereon-3.0.2}/src/events.rs +0 -0
  84. {sidereon-3.0.1 → sidereon-3.0.2}/src/exact_cache.rs +0 -0
  85. {sidereon-3.0.1 → sidereon-3.0.2}/src/exact_time.rs +0 -0
  86. {sidereon-3.0.1 → sidereon-3.0.2}/src/fallback.rs +0 -0
  87. {sidereon-3.0.1 → sidereon-3.0.2}/src/forces.rs +0 -0
  88. {sidereon-3.0.1 → sidereon-3.0.2}/src/format_diagnostics.rs +0 -0
  89. {sidereon-3.0.1 → sidereon-3.0.2}/src/frame_catalog.rs +0 -0
  90. {sidereon-3.0.1 → sidereon-3.0.2}/src/frames.rs +0 -0
  91. {sidereon-3.0.1 → sidereon-3.0.2}/src/fusion.rs +0 -0
  92. {sidereon-3.0.1 → sidereon-3.0.2}/src/geodesic.rs +0 -0
  93. {sidereon-3.0.1 → sidereon-3.0.2}/src/geodetic_time_series.rs +0 -0
  94. {sidereon-3.0.1 → sidereon-3.0.2}/src/geofence.rs +0 -0
  95. {sidereon-3.0.1 → sidereon-3.0.2}/src/geoid.rs +0 -0
  96. {sidereon-3.0.1 → sidereon-3.0.2}/src/geometry.rs +0 -0
  97. {sidereon-3.0.1 → sidereon-3.0.2}/src/geometry_quality.rs +0 -0
  98. {sidereon-3.0.1 → sidereon-3.0.2}/src/ils.rs +0 -0
  99. {sidereon-3.0.1 → sidereon-3.0.2}/src/iod.rs +0 -0
  100. {sidereon-3.0.1 → sidereon-3.0.2}/src/ionex.rs +0 -0
  101. {sidereon-3.0.1 → sidereon-3.0.2}/src/lambert.rs +0 -0
  102. {sidereon-3.0.1 → sidereon-3.0.2}/src/leap.rs +0 -0
  103. {sidereon-3.0.1 → sidereon-3.0.2}/src/least_squares.rs +0 -0
  104. {sidereon-3.0.1 → sidereon-3.0.2}/src/lib.rs +0 -0
  105. {sidereon-3.0.1 → sidereon-3.0.2}/src/lnav.rs +0 -0
  106. {sidereon-3.0.1 → sidereon-3.0.2}/src/marshal.rs +0 -0
  107. {sidereon-3.0.1 → sidereon-3.0.2}/src/nmea.rs +0 -0
  108. {sidereon-3.0.1 → sidereon-3.0.2}/src/normality.rs +0 -0
  109. {sidereon-3.0.1 → sidereon-3.0.2}/src/ntrip.rs +0 -0
  110. {sidereon-3.0.1 → sidereon-3.0.2}/src/observables.rs +0 -0
  111. {sidereon-3.0.1 → sidereon-3.0.2}/src/observation.rs +0 -0
  112. {sidereon-3.0.1 → sidereon-3.0.2}/src/oem.rs +0 -0
  113. {sidereon-3.0.1 → sidereon-3.0.2}/src/omm.rs +0 -0
  114. {sidereon-3.0.1 → sidereon-3.0.2}/src/opm.rs +0 -0
  115. {sidereon-3.0.1 → sidereon-3.0.2}/src/orbit_determination.rs +0 -0
  116. {sidereon-3.0.1 → sidereon-3.0.2}/src/ppp.rs +0 -0
  117. {sidereon-3.0.1 → sidereon-3.0.2}/src/ppp_corrections.rs +0 -0
  118. {sidereon-3.0.1 → sidereon-3.0.2}/src/products.rs +0 -0
  119. {sidereon-3.0.1 → sidereon-3.0.2}/src/propagation.rs +0 -0
  120. {sidereon-3.0.1 → sidereon-3.0.2}/src/qc.rs +0 -0
  121. {sidereon-3.0.1 → sidereon-3.0.2}/src/reduced_orbit.rs +0 -0
  122. {sidereon-3.0.1 → sidereon-3.0.2}/src/relative.rs +0 -0
  123. {sidereon-3.0.1 → sidereon-3.0.2}/src/reliability.rs +0 -0
  124. {sidereon-3.0.1 → sidereon-3.0.2}/src/rf.rs +0 -0
  125. {sidereon-3.0.1 → sidereon-3.0.2}/src/rinex.rs +0 -0
  126. {sidereon-3.0.1 → sidereon-3.0.2}/src/rinex_clock.rs +0 -0
  127. {sidereon-3.0.1 → sidereon-3.0.2}/src/rtcm.rs +0 -0
  128. {sidereon-3.0.1 → sidereon-3.0.2}/src/rtk.rs +0 -0
  129. {sidereon-3.0.1 → sidereon-3.0.2}/src/sbas_pl.rs +0 -0
  130. {sidereon-3.0.1 → sidereon-3.0.2}/src/sbas_ssr.rs +0 -0
  131. {sidereon-3.0.1 → sidereon-3.0.2}/src/scenario.rs +0 -0
  132. {sidereon-3.0.1 → sidereon-3.0.2}/src/sidereal.rs +0 -0
  133. {sidereon-3.0.1 → sidereon-3.0.2}/src/signal_analysis.rs +0 -0
  134. {sidereon-3.0.1 → sidereon-3.0.2}/src/sky.rs +0 -0
  135. {sidereon-3.0.1 → sidereon-3.0.2}/src/solve_error_detail.rs +0 -0
  136. {sidereon-3.0.1 → sidereon-3.0.2}/src/source_localization.rs +0 -0
  137. {sidereon-3.0.1 → sidereon-3.0.2}/src/space_weather.rs +0 -0
  138. {sidereon-3.0.1 → sidereon-3.0.2}/src/spk.rs +0 -0
  139. {sidereon-3.0.1 → sidereon-3.0.2}/src/spp.rs +0 -0
  140. {sidereon-3.0.1 → sidereon-3.0.2}/src/spp_error_detail.rs +0 -0
  141. {sidereon-3.0.1 → sidereon-3.0.2}/src/staleness.rs +0 -0
  142. {sidereon-3.0.1 → sidereon-3.0.2}/src/static_positioning.rs +0 -0
  143. {sidereon-3.0.1 → sidereon-3.0.2}/src/tca.rs +0 -0
  144. {sidereon-3.0.1 → sidereon-3.0.2}/src/tdm.rs +0 -0
  145. {sidereon-3.0.1 → sidereon-3.0.2}/src/terrain.rs +0 -0
  146. {sidereon-3.0.1 → sidereon-3.0.2}/src/terrain_store.rs +0 -0
  147. {sidereon-3.0.1 → sidereon-3.0.2}/src/tides.rs +0 -0
  148. {sidereon-3.0.1 → sidereon-3.0.2}/src/time_model_error.rs +0 -0
  149. {sidereon-3.0.1 → sidereon-3.0.2}/src/tle_fit_error_detail.rs +0 -0
  150. {sidereon-3.0.1 → sidereon-3.0.2}/src/tropo.rs +0 -0
  151. {sidereon-3.0.1 → sidereon-3.0.2}/src/unix_compress.rs +0 -0
  152. {sidereon-3.0.1 → sidereon-3.0.2}/tests/_helpers.py +0 -0
  153. {sidereon-3.0.1 → sidereon-3.0.2}/tests/_sp3_merge.py +0 -0
  154. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/README.md +0 -0
  155. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/antex/igs20_pasa_scoa_gps.atx +0 -0
  156. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/antex/igs20_wettzell_trim.atx +0 -0
  157. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/batch_fleet.json +0 -0
  158. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/cdm.json +0 -0
  159. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/clk/lossless/rinex_clock304_table_a17.clk +0 -0
  160. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/clk/lossless/rinex_clock304_table_a18.clk +0 -0
  161. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/clk/synthetic_rinex_clock.clk +0 -0
  162. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/conjunction.json +0 -0
  163. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/constellation/gps_ops_sample.json +0 -0
  164. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/constellation/navcen_forecast_cases.html +0 -0
  165. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/constellation/navcen_gps_sample.html +0 -0
  166. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/events_bodies_dop.json +0 -0
  167. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/format_diagnostics_inputs.json +0 -0
  168. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/frames_time.json +0 -0
  169. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/geodesic/geodtest_row.dat +0 -0
  170. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/geoid/egm2008_25_norcal_crop.bin +0 -0
  171. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/gnss_data/igs22376.sp3.Z.b64 +0 -0
  172. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/gnss_data/ncompress-current-b9-counter.Z.b64 +0 -0
  173. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/listings/aiub-iono-prd-20260923.csv +0 -0
  174. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/listings/gfz-ultra-w2430-20260804.html +0 -0
  175. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/nav/BRDC00GOP_R_20210010000_01D_MN.rnx +0 -0
  176. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/nav/ESBC00DNK_R_20201770000_01D_MN.rnx +0 -0
  177. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/nav/KMS300DNK_R_20221591000_01H_MN.rnx +0 -0
  178. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/numerical_propagation.json +0 -0
  179. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/obs/ESBC00DNK_R_20201770000_01D_30S_MO_trim.crx +0 -0
  180. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/obs/ESBC00DNK_R_20201770000_01D_30S_MO_trim.rnx +0 -0
  181. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/obs/algo0010_2015001_v1_trim.crx +0 -0
  182. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/obs/algo0010_2015001_v1_trim.rnx +0 -0
  183. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/omm.json +0 -0
  184. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/pass_finder.json +0 -0
  185. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/rf_link_budget.json +0 -0
  186. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/rtk_wtzr.json +0 -0
  187. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/scenario_base.json +0 -0
  188. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/sgp4_topocentric.json +0 -0
  189. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/sp3/IGS0OPSFIN_20261200945_02H30M_15M_ORB.SP3 +0 -0
  190. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/sp3-merge-input-v1.json +0 -0
  191. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/sp3-merge-report-v3.json +0 -0
  192. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/sp3_bodies.json +0 -0
  193. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/spk/horizons_eros_type21.bsp +0 -0
  194. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/tdm/annex_e_01.kvn +0 -0
  195. {sidereon-3.0.1 → sidereon-3.0.2}/tests/fixtures/tle_roundtrip.json +0 -0
  196. {sidereon-3.0.1 → sidereon-3.0.2}/tests/golden/sp3-terminal-record-v1.json +0 -0
  197. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_012_bindings.py +0 -0
  198. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_013_bindings.py +0 -0
  199. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_014_bindings.py +0 -0
  200. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_015_bindings.py +0 -0
  201. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_016_wave2_bindings.py +0 -0
  202. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_017_domain_exposure.py +0 -0
  203. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_018_domain_exposure.py +0 -0
  204. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_019_track.py +0 -0
  205. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_020_error_metrics.py +0 -0
  206. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_antex.py +0 -0
  207. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_antex_public_equality.py +0 -0
  208. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_antex_public_pcv_sample_fields.py +0 -0
  209. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_atmosphere.py +0 -0
  210. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_attested_open.py +0 -0
  211. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_batch.py +0 -0
  212. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_broadcast_comparison.py +0 -0
  213. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_broadcast_fallback.py +0 -0
  214. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_cdm.py +0 -0
  215. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_cdm_top_level.py +0 -0
  216. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_cdm_value_equality.py +0 -0
  217. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_codec_error_details.py +0 -0
  218. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_conjunction.py +0 -0
  219. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_constellation.py +0 -0
  220. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_constellation_fleet.py +0 -0
  221. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_core_defaults.py +0 -0
  222. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_core_public_api_parity.py +0 -0
  223. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_core_round3_surfaces.py +0 -0
  224. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_covariance.py +0 -0
  225. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_covariance_propagation.py +0 -0
  226. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_coverage.py +0 -0
  227. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_crinex.py +0 -0
  228. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_data.py +0 -0
  229. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_data_publication_resilience.py +0 -0
  230. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_dgnss.py +0 -0
  231. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_distribution.py +0 -0
  232. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_distribution_fallback.py +0 -0
  233. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_dop_at_epoch.py +0 -0
  234. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_dop_convention.py +0 -0
  235. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_doppler.py +0 -0
  236. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_dted_tile_error_details.py +0 -0
  237. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_egm96.py +0 -0
  238. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_elements.py +0 -0
  239. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_events_bodies_dop.py +0 -0
  240. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_exact_cache_atomicity.py +0 -0
  241. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_field_mode_bindings.py +0 -0
  242. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_forces.py +0 -0
  243. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_format_diagnostics.py +0 -0
  244. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_frames_time.py +0 -0
  245. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ftp_transport.py +0 -0
  246. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_fusion_error_details.py +0 -0
  247. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_geoid.py +0 -0
  248. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_geometry.py +0 -0
  249. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ils.py +0 -0
  250. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_import.py +0 -0
  251. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_inertial_mechanization.py +0 -0
  252. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ionex.py +0 -0
  253. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ionex_instant_queries.py +0 -0
  254. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ionex_policies.py +0 -0
  255. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ionex_public_equality.py +0 -0
  256. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ionosphere_nequick.py +0 -0
  257. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_klobuchar_native.py +0 -0
  258. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_lambert_iod.py +0 -0
  259. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_leap_seconds.py +0 -0
  260. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_least_squares.py +0 -0
  261. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_lnav.py +0 -0
  262. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_misc_public_equality.py +0 -0
  263. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_moving_baseline.py +0 -0
  264. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_new_core_api.py +0 -0
  265. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_next_issue_due.py +0 -0
  266. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_nmea_finish_output.py +0 -0
  267. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ntrip.py +0 -0
  268. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_numerical.py +0 -0
  269. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_obs_policies.py +0 -0
  270. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_observables.py +0 -0
  271. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_observables_broadcast.py +0 -0
  272. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_observables_error_details.py +0 -0
  273. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_observation.py +0 -0
  274. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_observation_qc.py +0 -0
  275. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_observe_batch.py +0 -0
  276. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_oem.py +0 -0
  277. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_oem_full_dto.py +0 -0
  278. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_omm.py +0 -0
  279. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_omm_full_dto.py +0 -0
  280. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_opm.py +0 -0
  281. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_opm_full_dto.py +0 -0
  282. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_parity_gaps.py +0 -0
  283. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_parse_tle_file.py +0 -0
  284. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_passes.py +0 -0
  285. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ppp.py +0 -0
  286. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ppp_corrections.py +0 -0
  287. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_ppp_range_corrections.py +0 -0
  288. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_precise_interpolant_public_values.py +0 -0
  289. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_precise_samples.py +0 -0
  290. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_precise_samples_errors.py +0 -0
  291. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_propagation.py +0 -0
  292. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_qc.py +0 -0
  293. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_reduced_orbit.py +0 -0
  294. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_reduced_orbit_piecewise.py +0 -0
  295. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_residual_stats.py +0 -0
  296. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rf.py +0 -0
  297. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rinex_clock.py +0 -0
  298. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rinex_clock_equality.py +0 -0
  299. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rinex_clock_header_field_details.py +0 -0
  300. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rinex_clock_lossless.py +0 -0
  301. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rinex_nav.py +0 -0
  302. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rinex_obs.py +0 -0
  303. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rtcm.py +0 -0
  304. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rtk.py +0 -0
  305. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_rtk_unresolved_carriers.py +0 -0
  306. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sbas_decoded_lists.py +0 -0
  307. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sbas_unassigned_mask.py +0 -0
  308. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_scenario_external_sources.py +0 -0
  309. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sky.py +0 -0
  310. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_bodies.py +0 -0
  311. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_catalog_exact.py +0 -0
  312. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_clock_reference.py +0 -0
  313. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_interpolation_policy.py +0 -0
  314. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_merge.py +0 -0
  315. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_merge_coverage.py +0 -0
  316. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_merge_per_epoch_provenance.py +0 -0
  317. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_public_metadata.py +0 -0
  318. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_terminal_record.py +0 -0
  319. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_window_continuity.py +0 -0
  320. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_sp3_write_and_records.py +0 -0
  321. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_spk.py +0 -0
  322. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_spp.py +0 -0
  323. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_spp_error_details.py +0 -0
  324. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_spp_glonass.py +0 -0
  325. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_spp_robustness.py +0 -0
  326. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_staleness.py +0 -0
  327. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_stub_accuracy.py +0 -0
  328. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_tca.py +0 -0
  329. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_tdm_policies.py +0 -0
  330. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_terrain_unknown_elevation.py +0 -0
  331. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_tides.py +0 -0
  332. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_time_model_error_details.py +0 -0
  333. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_time_offset_error_details.py +0 -0
  334. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_tle_fit.py +0 -0
  335. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_tle_roundtrip.py +0 -0
  336. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_tropo.py +0 -0
  337. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_unix_compress.py +0 -0
  338. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_visible_ground_track.py +0 -0
  339. {sidereon-3.0.1 → sidereon-3.0.2}/tests/test_weather_terrain_error_details.py +0 -0
  340. {sidereon-3.0.1 → sidereon-3.0.2}/third_party_licenses/ERFA-BSD-3-Clause.txt +0 -0
  341. {sidereon-3.0.1 → sidereon-3.0.2}/third_party_licenses/IERS-CONVENTIONS-SOFTWARE-LICENSE.txt +0 -0
  342. {sidereon-3.0.1 → sidereon-3.0.2}/third_party_licenses/SciPy-BSD-3-Clause.txt +0 -0
  343. {sidereon-3.0.1/third_party_source/sidereon-core-3.0.1 → sidereon-3.0.2/third_party_source/sidereon-core-3.0.2}/tides/mod.rs +0 -0
  344. {sidereon-3.0.1/third_party_source/sidereon-core-3.0.1 → sidereon-3.0.2/third_party_source/sidereon-core-3.0.2}/tides/ocean.rs +0 -0
  345. {sidereon-3.0.1/third_party_source/sidereon-core-3.0.1 → sidereon-3.0.2/third_party_source/sidereon-core-3.0.2}/tides/pole.rs +0 -0
@@ -153,10 +153,10 @@ jobs:
153
153
  done
154
154
  for source in mod.rs ocean.rs pole.rs; do
155
155
  unzip -p "$wheel" \
156
- "third_party_source/sidereon-core-3.0.1/tides/$source" \
156
+ "third_party_source/sidereon-core-3.0.2/tides/$source" \
157
157
  >"$RUNNER_TEMP/$source"
158
158
  cmp "$RUNNER_TEMP/$source" \
159
- "third_party_source/sidereon-core-3.0.1/tides/$source"
159
+ "third_party_source/sidereon-core-3.0.2/tides/$source"
160
160
  done
161
161
  done
162
162
  notice="$RUNNER_TEMP/sdist.notices"
@@ -173,10 +173,10 @@ jobs:
173
173
  done
174
174
  for source in mod.rs ocean.rs pole.rs; do
175
175
  tar -xOf "${sdists[0]}" --wildcards \
176
- "*/third_party_source/sidereon-core-3.0.1/tides/$source" \
176
+ "*/third_party_source/sidereon-core-3.0.2/tides/$source" \
177
177
  >"$RUNNER_TEMP/sdist-$source"
178
178
  cmp "$RUNNER_TEMP/sdist-$source" \
179
- "third_party_source/sidereon-core-3.0.1/tides/$source"
179
+ "third_party_source/sidereon-core-3.0.2/tides/$source"
180
180
  done
181
181
  - name: Smoke-test installed x86_64 Linux wheel
182
182
  shell: bash
@@ -218,3 +218,39 @@ jobs:
218
218
  uses: pypa/gh-action-pypi-publish@release/v1
219
219
  with:
220
220
  attestations: true
221
+
222
+ public-consumer:
223
+ name: Verify public PyPI consumer
224
+ runs-on: ubuntu-24.04-arm
225
+ timeout-minutes: 15
226
+ needs: publish
227
+ steps:
228
+ - uses: actions/checkout@v4
229
+ - uses: actions/setup-python@v5
230
+ with:
231
+ python-version: '3.12'
232
+ - name: Install exact published wheel and exercise public references
233
+ shell: bash
234
+ run: |
235
+ set -euo pipefail
236
+ version="${GITHUB_REF_NAME#v}"
237
+ scratch="$(mktemp -d)"
238
+ trap 'rm -rf "$scratch"' EXIT
239
+ python -m venv "$scratch/venv"
240
+ installed=false
241
+ for attempt in 1 2 3 4 5 6; do
242
+ if "$scratch/venv/bin/python" -m pip install --only-binary=sidereon "sidereon==$version" pytest; then
243
+ installed=true
244
+ break
245
+ fi
246
+ sleep 10
247
+ done
248
+ test "$installed" = true
249
+ mkdir -p "$scratch/tests/fixtures/bodies"
250
+ cp scripts/smoke-release.py "$scratch/smoke-release.py"
251
+ cp tests/test_body_observe_reference.py "$scratch/tests/"
252
+ cp tests/fixtures/bodies/observe_de.bsp "$scratch/tests/fixtures/bodies/"
253
+ cd "$scratch"
254
+ unset PYTHONPATH
255
+ SIDEREON_EXPECTED_VERSION="$version" "$scratch/venv/bin/python" -I smoke-release.py
256
+ "$scratch/venv/bin/python" -I -m pytest -q tests/test_body_observe_reference.py
@@ -4,6 +4,13 @@ All notable changes to the Sidereon Python interface are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.0.2] - 2026-10-05
8
+
9
+ ### Changed
10
+
11
+ - Use the coordinated 3.0.2 Rust engine release, including NaN-safe Portable ULP comparisons.
12
+ - Retain the existing Python API while keeping package versions and shipped source disclosures aligned across all libraries.
13
+
7
14
  ## [3.0.1] - 2026-10-04
8
15
 
9
16
  ### Added
@@ -611,9 +611,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
611
611
 
612
612
  [[package]]
613
613
  name = "sidereon"
614
- version = "3.0.1"
614
+ version = "3.0.2"
615
615
  source = "registry+https://github.com/rust-lang/crates.io-index"
616
- checksum = "b9783598080524b015c13894f68c0df9ccbaa22ad34fd6e59c5aadfdb8b30bec"
616
+ checksum = "8a424bd9dea809b4cf2b816c27aa129a3e437b95139389ecbc94416aeee27951"
617
617
  dependencies = [
618
618
  "crc32fast",
619
619
  "flate2",
@@ -622,9 +622,9 @@ dependencies = [
622
622
 
623
623
  [[package]]
624
624
  name = "sidereon-core"
625
- version = "3.0.1"
625
+ version = "3.0.2"
626
626
  source = "registry+https://github.com/rust-lang/crates.io-index"
627
- checksum = "0c3c49cae7e3b74a9de16cb576c831985158176141d713871eb3b4ec93db91e3"
627
+ checksum = "29f682ba7089c512d83cfea712bdc6ce9caac5e46d9acbeca7a1b2151ad8ee5b"
628
628
  dependencies = [
629
629
  "approx",
630
630
  "cc",
@@ -646,7 +646,7 @@ dependencies = [
646
646
 
647
647
  [[package]]
648
648
  name = "sidereon-py"
649
- version = "3.0.1"
649
+ version = "3.0.2"
650
650
  dependencies = [
651
651
  "nalgebra",
652
652
  "numpy",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sidereon-py"
3
- version = "3.0.1"
3
+ version = "3.0.2"
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"
@@ -25,12 +25,12 @@ exact-cache-test-failpoints = ["sidereon-core/exact-cache-test-failpoints"]
25
25
  # The thin ergonomic engine surface this binding marshals to. No modeling logic
26
26
  # lives here: every solve delegates to a `sidereon` entry point, which in turn
27
27
  # delegates to `sidereon-core`.
28
- sidereon = { version = "=3.0.1" }
29
- sidereon-core = { version = "=3.0.1", features = ["mmap"] }
28
+ sidereon = "=3.0.2"
29
+ sidereon-core = { version = "=3.0.2", features = ["mmap"] }
30
30
  # The standalone general-purpose trust-region least-squares solver: a data-driven
31
31
  # `least_squares` whose residual and Jacobian are evaluated entirely in Rust, so
32
32
  # no per-iteration Python callback is paid.
33
- trust-region-least-squares = "0.11.0"
33
+ trust-region-least-squares = "=0.11.0"
34
34
  pyo3 = { version = "0.25", features = ["abi3-py39"] }
35
35
  # Zero-copy ndarray marshalling so a batched propagate/look-angle call crosses
36
36
  # the FFI boundary once (1-D epochs in, 2-D states / 1-D angles out) instead of a
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sidereon
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Topic :: Scientific/Engineering
@@ -330,9 +330,9 @@ endorsed by the IERS Conventions Center. The Rust source describes the origin,
330
330
  the renamed routines, and the differences from the Fortran implementation.
331
331
  The complete non-test tide source distributed in the compiled extension is
332
332
  included under
333
- `third_party_source/sidereon-core-3.0.1/tides/` in wheels and source
334
- distributions. These files match sidereon-core 3.0.1 at commit 592ca2bd293177bcd901286080baa6350522020f.
335
- Source: [sidereon-core tides at the pinned commit](https://github.com/neilberkman/sidereon/tree/592ca2bd293177bcd901286080baa6350522020f/crates/sidereon-core/src/tides).
333
+ `third_party_source/sidereon-core-3.0.2/tides/` in wheels and source
334
+ distributions. These files match sidereon-core 3.0.2 at commit 8694648892d8ab065d94014e2e21d6cd5054818e.
335
+ Source: [sidereon-core tides at the pinned commit](https://github.com/neilberkman/sidereon/tree/8694648892d8ab065d94014e2e21d6cd5054818e/crates/sidereon-core/src/tides).
336
336
  The authoritative original is available from:
337
337
 
338
338
  <https://iers-conventions.obspm.fr/content/chapter7/software/dehanttideinel/DEHANTTIDEINEL.F>
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "sidereon"
7
- version = "3.0.1"
7
+ version = "3.0.2"
8
8
  description = "Python (PyO3) bindings over the sidereon engine: SP3 loading and SPP/RTK/PPP positioning solves"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -82,7 +82,7 @@ include = [
82
82
  "third_party_licenses/ERFA-BSD-3-Clause.txt",
83
83
  "third_party_licenses/IERS-CONVENTIONS-SOFTWARE-LICENSE.txt",
84
84
  "third_party_licenses/SciPy-BSD-3-Clause.txt",
85
- "third_party_source/sidereon-core-3.0.1/tides/mod.rs",
86
- "third_party_source/sidereon-core-3.0.1/tides/ocean.rs",
87
- "third_party_source/sidereon-core-3.0.1/tides/pole.rs",
85
+ "third_party_source/sidereon-core-3.0.2/tides/mod.rs",
86
+ "third_party_source/sidereon-core-3.0.2/tides/ocean.rs",
87
+ "third_party_source/sidereon-core-3.0.2/tides/pole.rs",
88
88
  ]
@@ -13,7 +13,7 @@ import tomllib
13
13
  ROOT = Path(__file__).resolve().parents[1]
14
14
  CORE_GIT_URL = "https://github.com/neilberkman/sidereon"
15
15
  FULL_GIT_REVISION = re.compile(r"^[0-9a-f]{40}$")
16
- CORE_SOURCE_REVISION = "592ca2bd293177bcd901286080baa6350522020f"
16
+ CORE_SOURCE_REVISION = "8694648892d8ab065d94014e2e21d6cd5054818e"
17
17
 
18
18
 
19
19
  def registry_version(name, value, package_version):
@@ -114,7 +114,7 @@ def validate_trust_region_dependency(
114
114
  value, engine_dependencies, package_version, *, allow_candidate
115
115
  ):
116
116
  """Accept the registry release or its exact coordinated Git candidate."""
117
- if value == "0.11.0":
117
+ if value in ("0.11.0", "=0.11.0"):
118
118
  return
119
119
  if not allow_candidate:
120
120
  raise ValueError(
@@ -309,7 +309,7 @@ def main() -> None:
309
309
  "ocean.rs": "25946677944425671a92717860ac2d70f255de5403eeb1fbf98b361716821d5c",
310
310
  "pole.rs": "b4cc4c16bdd8ce1d8f04073602ab47dfb85a002b946ab192e8d4d2d600f0a1f8",
311
311
  }
312
- tide_root = ROOT / "third_party_source" / "sidereon-core-3.0.1" / "tides"
312
+ tide_root = ROOT / "third_party_source" / "sidereon-core-3.0.2" / "tides"
313
313
  for filename, expected_digest in tide_sources.items():
314
314
  source = tide_root / filename
315
315
  if not source.is_file():
@@ -318,7 +318,7 @@ def main() -> None:
318
318
  if digest != expected_digest:
319
319
  raise SystemExit(
320
320
  f"IERS-derived source disclosure {source} has digest {digest}, "
321
- f"expected {expected_digest} from sidereon-core 3.0.1 at "
321
+ f"expected {expected_digest} from sidereon-core 3.0.2 at "
322
322
  f"{CORE_SOURCE_REVISION}"
323
323
  )
324
324
 
@@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
8
8
 
9
9
  const REGISTRY_SOURCE: &str = "registry+https://github.com/rust-lang/crates.io-index";
10
10
  const GIT_SOURCE_PREFIX: &str = "git+https://github.com/neilberkman/sidereon?rev=";
11
- const RELEASE_VERSION: &str = "3.0.1";
11
+ const RELEASE_VERSION: &str = "3.0.2";
12
12
 
13
13
  #[derive(Debug)]
14
14
  struct LockedPackage {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "source": "scripts/core_goldens",
3
- "core_revision": "592ca2bd293177bcd901286080baa6350522020f",
3
+ "core_revision": "8694648892d8ab065d94014e2e21d6cd5054818e",
4
4
  "spp_trace_default": {
5
5
  "position_m": [
6
6
  "0x41511b0a63f49487",
@@ -16520,5 +16520,5 @@
16520
16520
  ]
16521
16521
  }
16522
16522
  },
16523
- "core_revision": "592ca2bd293177bcd901286080baa6350522020f"
16523
+ "core_revision": "8694648892d8ab065d94014e2e21d6cd5054818e"
16524
16524
  }
@@ -0,0 +1,311 @@
1
+ """Public body-observation checks against independent ephemeris references.
2
+
3
+ JPL values are reproducible with GET https://ssd.jpl.nasa.gov/api/horizons.api
4
+ and format=text, OBJ_DATA=NO, MAKE_EPHEM=YES, EPHEM_TYPE=OBSERVER,
5
+ CENTER=coord@399, COORD_TYPE=GEODETIC, SITE_COORD=0,51.4769,0.046,
6
+ STEP_SIZE=1 m, TIME_TYPE=UT, QUANTITIES=2,4,20, ANG_FORMAT=DEG,
7
+ APPARENT=AIRLESS, RANGE_UNITS=KM. For the Sun use COMMAND=10,
8
+ START_TIME=2024-06-20 12:01:42, STOP_TIME=2024-06-20 12:02:42; for the
9
+ Moon use COMMAND=301, START_TIME=2024-04-23 23:55:59,
10
+ STOP_TIME=2024-04-24 00:00:59. Both responses identify DE441. First data
11
+ rows (apparent RA/Dec, az/el, range km) are Sun 89.61786, 23.43665,
12
+ 179.997296, 61.959749, 1.5201128641E+08; Moon 211.50898, -15.40328,
13
+ 179.999082, 23.119822, 3.9720636319E+05. The JPL manual documents q2 apparent
14
+ coordinates as true-equator/equinox-of-date for Earth sites and q4 azimuth as
15
+ clockwise from north. Sidereon's analytic Sun/Moon series are intended for
16
+ planning and visualization, not almanac precision, so the angular tolerances
17
+ are deliberately sub-degree.
18
+ """
19
+
20
+ from datetime import datetime, timezone
21
+
22
+ import pytest
23
+ import sidereon
24
+
25
+
26
+ @pytest.mark.parametrize(
27
+ (
28
+ "target",
29
+ "epoch",
30
+ "apparent_ra_deg",
31
+ "apparent_dec_deg",
32
+ "azimuth_deg",
33
+ "elevation_deg",
34
+ "range_km",
35
+ "angle_tolerance_deg",
36
+ "range_tolerance_km",
37
+ ),
38
+ [
39
+ (
40
+ sidereon.Target.sun(),
41
+ datetime(2024, 6, 20, 12, 1, 42, tzinfo=timezone.utc),
42
+ 89.61786,
43
+ 23.43665,
44
+ 179.997296,
45
+ 61.959749,
46
+ 152_011_286.41,
47
+ 0.01,
48
+ 100_000.0,
49
+ ),
50
+ (
51
+ sidereon.Target.moon(),
52
+ datetime(2024, 4, 23, 23, 55, 59, tzinfo=timezone.utc),
53
+ 211.50898,
54
+ -15.40328,
55
+ 179.999082,
56
+ 23.119822,
57
+ 397_206.36319,
58
+ 0.95,
59
+ 1_000.0,
60
+ ),
61
+ ],
62
+ ids=("sun", "moon"),
63
+ )
64
+ def test_ground_observation_tracks_independent_horizons_reference(
65
+ target,
66
+ epoch,
67
+ apparent_ra_deg,
68
+ apparent_dec_deg,
69
+ azimuth_deg,
70
+ elevation_deg,
71
+ range_km,
72
+ angle_tolerance_deg,
73
+ range_tolerance_km,
74
+ ):
75
+ epoch_unix_us = int(epoch.timestamp()) * 1_000_000
76
+
77
+ observation = sidereon.observe_body(
78
+ 51.4769,
79
+ 0.0,
80
+ 0.046,
81
+ epoch_unix_us,
82
+ target,
83
+ )
84
+
85
+ assert observation.reduced is True
86
+ assert 0.0 <= observation.apparent.right_ascension_deg < 360.0
87
+ assert 0.0 <= observation.apparent.right_ascension_hours < 24.0
88
+ assert -180.0 < observation.hour_angle_deg <= 180.0
89
+ assert -12.0 < observation.hour_angle_hours <= 12.0
90
+ assert 0.0 <= observation.horizontal.azimuth_deg < 360.0
91
+ assert observation.apparent.right_ascension_deg == pytest.approx(
92
+ apparent_ra_deg, abs=angle_tolerance_deg, rel=0.0
93
+ )
94
+ assert observation.apparent.declination_deg == pytest.approx(
95
+ apparent_dec_deg, abs=angle_tolerance_deg, rel=0.0
96
+ )
97
+ assert observation.horizontal.azimuth_deg == pytest.approx(
98
+ azimuth_deg, abs=angle_tolerance_deg, rel=0.0
99
+ )
100
+ assert observation.horizontal.elevation_deg == pytest.approx(
101
+ elevation_deg, abs=angle_tolerance_deg, rel=0.0
102
+ )
103
+ assert observation.horizontal.range_km == pytest.approx(
104
+ range_km, abs=range_tolerance_km, rel=0.0
105
+ )
106
+
107
+ # The API exposes both degree and hour forms of RA/hour angle.
108
+ assert observation.apparent.right_ascension_hours * 15.0 == pytest.approx(
109
+ observation.apparent.right_ascension_deg, abs=1e-12, rel=0.0
110
+ )
111
+ assert observation.hour_angle_hours * 15.0 == pytest.approx(
112
+ observation.hour_angle_deg, abs=1e-12, rel=0.0
113
+ )
114
+
115
+
116
+ # Full-target values are generated independently by the pinned Skyfield
117
+ # fixture generator also used by the core integration test. Its source is
118
+ # tests/fixtures/bodies/gen_observe_golden.py in sidereon-core; that generator
119
+ # uses observe_de.bsp and applies Sun-only gravitational deflection.
120
+ @pytest.mark.parametrize(
121
+ (
122
+ "naif_id",
123
+ "latitude_deg",
124
+ "longitude_deg",
125
+ "altitude_km",
126
+ "apparent_ra_deg",
127
+ "apparent_dec_deg",
128
+ "apparent_distance_km",
129
+ "apparent_icrs_ra_deg",
130
+ "apparent_icrs_dec_deg",
131
+ "apparent_icrs_distance_km",
132
+ "astrometric_ra_deg",
133
+ "astrometric_dec_deg",
134
+ "astrometric_distance_km",
135
+ "azimuth_deg",
136
+ "elevation_deg",
137
+ "range_km",
138
+ "ecliptic_longitude_deg",
139
+ "ecliptic_latitude_deg",
140
+ "ecliptic_distance_km",
141
+ ),
142
+ [
143
+ (
144
+ 4,
145
+ 51.4779,
146
+ -0.0015,
147
+ 0.046,
148
+ 267.0545293733468,
149
+ -23.96185179890772,
150
+ 362_601_889.49888045,
151
+ 266.689454311102,
152
+ -23.95235998065674,
153
+ 362_601_889.4988812,
154
+ 266.69569176937006,
155
+ -23.95250704362053,
156
+ 362_601_889.498879,
157
+ 25.029803759785487,
158
+ -60.701353351529,
159
+ 362_601_889.4988805,
160
+ 267.3084528018354,
161
+ -0.550968260129117,
162
+ 362_601_889.49888045,
163
+ ),
164
+ (
165
+ 5,
166
+ -33.8568,
167
+ 151.2153,
168
+ 0.040,
169
+ 33.685726736672464,
170
+ 12.26418101472206,
171
+ 670_428_149.8823149,
172
+ 33.36411888660434,
173
+ 12.152064228124974,
174
+ 670_428_149.8823142,
175
+ 33.36174062892731,
176
+ 12.151348694023607,
177
+ 670_428_149.8823142,
178
+ 113.07414946418533,
179
+ -49.51460784852244,
180
+ 670_428_149.8823149,
181
+ 35.582668467646254,
182
+ -1.1853565914672937,
183
+ 670_428_149.8823149,
184
+ ),
185
+ ],
186
+ ids=("mars-greenwich", "jupiter-sydney"),
187
+ )
188
+ def test_spk_target_projects_complete_skyfield_golden(
189
+ naif_id,
190
+ latitude_deg,
191
+ longitude_deg,
192
+ altitude_km,
193
+ apparent_ra_deg,
194
+ apparent_dec_deg,
195
+ apparent_distance_km,
196
+ apparent_icrs_ra_deg,
197
+ apparent_icrs_dec_deg,
198
+ apparent_icrs_distance_km,
199
+ astrometric_ra_deg,
200
+ astrometric_dec_deg,
201
+ astrometric_distance_km,
202
+ azimuth_deg,
203
+ elevation_deg,
204
+ range_km,
205
+ ecliptic_longitude_deg,
206
+ ecliptic_latitude_deg,
207
+ ecliptic_distance_km,
208
+ ):
209
+ import math
210
+ from pathlib import Path
211
+
212
+ kernel = sidereon.load_spk(
213
+ Path(__file__).parent / "fixtures" / "bodies" / "observe_de.bsp"
214
+ )
215
+ observation = sidereon.observe_body(
216
+ latitude_deg,
217
+ longitude_deg,
218
+ altitude_km,
219
+ int(datetime(2024, 1, 1, tzinfo=timezone.utc).timestamp()) * 1_000_000,
220
+ sidereon.Target.spk(kernel, naif_id),
221
+ )
222
+
223
+ def equatorial_separation_arcsec(coords, expected_ra_deg, expected_dec_deg):
224
+ def vector(ra_deg, dec_deg):
225
+ ra = math.radians(ra_deg)
226
+ dec = math.radians(dec_deg)
227
+ return (
228
+ math.cos(dec) * math.cos(ra),
229
+ math.cos(dec) * math.sin(ra),
230
+ math.sin(dec),
231
+ )
232
+
233
+ actual = vector(coords.right_ascension_deg, coords.declination_deg)
234
+ expected = vector(expected_ra_deg, expected_dec_deg)
235
+ cross = (
236
+ actual[1] * expected[2] - actual[2] * expected[1],
237
+ actual[2] * expected[0] - actual[0] * expected[2],
238
+ actual[0] * expected[1] - actual[1] * expected[0],
239
+ )
240
+ cross_norm = math.sqrt(sum(component * component for component in cross))
241
+ dot = sum(a * b for a, b in zip(actual, expected))
242
+ return math.degrees(math.atan2(cross_norm, dot)) * 3600.0
243
+
244
+ def relative_distance(actual, expected):
245
+ return abs(actual - expected) / max(abs(expected), 1.0)
246
+
247
+ assert observation.reduced is False
248
+ for coords, ra, dec, distance in (
249
+ (
250
+ observation.astrometric,
251
+ astrometric_ra_deg,
252
+ astrometric_dec_deg,
253
+ astrometric_distance_km,
254
+ ),
255
+ (
256
+ observation.apparent_icrs,
257
+ apparent_icrs_ra_deg,
258
+ apparent_icrs_dec_deg,
259
+ apparent_icrs_distance_km,
260
+ ),
261
+ (
262
+ observation.apparent,
263
+ apparent_ra_deg,
264
+ apparent_dec_deg,
265
+ apparent_distance_km,
266
+ ),
267
+ ):
268
+ assert 0.0 <= coords.right_ascension_deg < 360.0
269
+ assert 0.0 <= coords.right_ascension_hours < 24.0
270
+ assert equatorial_separation_arcsec(coords, ra, dec) < 1.0
271
+ assert relative_distance(coords.distance_km, distance) < 1e-8
272
+ assert coords.right_ascension_hours * 15.0 == pytest.approx(
273
+ coords.right_ascension_deg, abs=1e-12, rel=0.0
274
+ )
275
+
276
+ assert -180.0 < observation.hour_angle_deg <= 180.0
277
+ assert -12.0 < observation.hour_angle_hours <= 12.0
278
+ assert 0.0 <= observation.horizontal.azimuth_deg < 360.0
279
+ assert observation.horizontal.azimuth_deg == pytest.approx(
280
+ azimuth_deg, abs=2.0 / 3600.0, rel=0.0
281
+ )
282
+ assert observation.horizontal.elevation_deg == pytest.approx(
283
+ elevation_deg, abs=2.0 / 3600.0, rel=0.0
284
+ )
285
+ assert relative_distance(observation.horizontal.range_km, range_km) < 1e-8
286
+ assert 0.0 <= observation.ecliptic.longitude_deg < 360.0
287
+ assert observation.ecliptic.longitude_deg == pytest.approx(
288
+ ecliptic_longitude_deg, abs=1.0 / 3600.0, rel=0.0
289
+ )
290
+ assert observation.ecliptic.latitude_deg == pytest.approx(
291
+ ecliptic_latitude_deg, abs=1.0 / 3600.0, rel=0.0
292
+ )
293
+ assert (
294
+ relative_distance(observation.ecliptic.distance_km, ecliptic_distance_km) < 1e-8
295
+ )
296
+
297
+ # Recover local hour angle from the independent Skyfield alt/az values.
298
+ lat = math.radians(latitude_deg)
299
+ az = math.radians(azimuth_deg)
300
+ el = math.radians(elevation_deg)
301
+ hour_angle = math.degrees(
302
+ math.atan2(
303
+ -math.sin(az) * math.cos(el),
304
+ math.sin(el) * math.cos(lat) - math.cos(el) * math.sin(lat) * math.cos(az),
305
+ )
306
+ )
307
+ delta_hour_angle = (observation.hour_angle_deg - hour_angle + 180.0) % 360.0 - 180.0
308
+ assert abs(delta_hour_angle) < 5.0 / 3600.0
309
+ assert observation.hour_angle_hours * 15.0 == pytest.approx(
310
+ observation.hour_angle_deg, abs=1e-12, rel=0.0
311
+ )