sidereon 3.0.0__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.0 → sidereon-3.0.2}/.github/workflows/release.yml +40 -4
  2. {sidereon-3.0.0 → sidereon-3.0.2}/CHANGELOG.md +21 -2
  3. {sidereon-3.0.0 → sidereon-3.0.2}/Cargo.lock +5 -5
  4. {sidereon-3.0.0 → sidereon-3.0.2}/Cargo.toml +4 -4
  5. {sidereon-3.0.0 → sidereon-3.0.2}/PKG-INFO +1 -1
  6. {sidereon-3.0.0 → sidereon-3.0.2}/THIRD-PARTY-NOTICES.md +4 -2
  7. {sidereon-3.0.0 → sidereon-3.0.2}/pyproject.toml +4 -4
  8. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/__init__.py +1 -1
  9. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/distribution.py +72 -3
  10. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/distribution.pyi +4 -1
  11. {sidereon-3.0.0 → sidereon-3.0.2}/scripts/check-release.py +5 -3
  12. {sidereon-3.0.0 → sidereon-3.0.2}/scripts/core_source_identity.rs +7 -7
  13. sidereon-3.0.2/tests/fixtures/bodies/observe_de.bsp +0 -0
  14. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/core_goldens.json +1 -1
  15. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/ppp_esbc.json +1 -1
  16. sidereon-3.0.2/tests/test_antex_public_equality.py +166 -0
  17. sidereon-3.0.2/tests/test_body_observe_reference.py +311 -0
  18. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_core_revision.py +38 -23
  19. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_distribution_fallback.py +73 -0
  20. sidereon-3.0.2/tests/test_ionex_public_equality.py +135 -0
  21. sidereon-3.0.2/tests/test_misc_public_equality.py +162 -0
  22. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_observation_qc.py +93 -0
  23. sidereon-3.0.2/tests/test_precise_interpolant_public_values.py +98 -0
  24. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_release_guard.py +35 -34
  25. sidereon-3.0.2/tests/test_rinex_clock_equality.py +199 -0
  26. sidereon-3.0.2/tests/test_sp3_public_metadata.py +83 -0
  27. {sidereon-3.0.0 → sidereon-3.0.2}/uv.lock +1 -1
  28. {sidereon-3.0.0 → sidereon-3.0.2}/.cargo/config.toml.disabled +0 -0
  29. {sidereon-3.0.0 → sidereon-3.0.2}/.gitattributes +0 -0
  30. {sidereon-3.0.0 → sidereon-3.0.2}/.github/workflows/ci.yml +0 -0
  31. {sidereon-3.0.0 → sidereon-3.0.2}/.gitignore +0 -0
  32. {sidereon-3.0.0 → sidereon-3.0.2}/COVERAGE.md +0 -0
  33. {sidereon-3.0.0 → sidereon-3.0.2}/LICENSE +0 -0
  34. {sidereon-3.0.0 → sidereon-3.0.2}/README.md +0 -0
  35. {sidereon-3.0.0 → sidereon-3.0.2}/bench/README.md +0 -0
  36. {sidereon-3.0.0 → sidereon-3.0.2}/bench/bench_fleet.py +0 -0
  37. {sidereon-3.0.0 → sidereon-3.0.2}/docs/antex.md +0 -0
  38. {sidereon-3.0.0 → sidereon-3.0.2}/docs/observations.md +0 -0
  39. {sidereon-3.0.0 → sidereon-3.0.2}/docs/public-gnss-distribution-sources.md +0 -0
  40. {sidereon-3.0.0 → sidereon-3.0.2}/docs/releasing.md +0 -0
  41. {sidereon-3.0.0 → sidereon-3.0.2}/docs/rinex-clock.md +0 -0
  42. {sidereon-3.0.0 → sidereon-3.0.2}/docs/sp3.md +0 -0
  43. {sidereon-3.0.0 → sidereon-3.0.2}/docs/tdm.md +0 -0
  44. {sidereon-3.0.0 → sidereon-3.0.2}/examples/clean_a_phone_track.ipynb +0 -0
  45. {sidereon-3.0.0 → sidereon-3.0.2}/examples/static_occupation_vs_cors.ipynb +0 -0
  46. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/__init__.pyi +0 -0
  47. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/_compression.py +0 -0
  48. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/_exact_cache.py +0 -0
  49. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/_ingress.py +0 -0
  50. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/data.py +0 -0
  51. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/data.pyi +0 -0
  52. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/exact_cache.py +0 -0
  53. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/exact_cache.pyi +0 -0
  54. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/exact_sp3.py +0 -0
  55. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/exact_sp3.pyi +0 -0
  56. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/ntrip.py +0 -0
  57. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/ntrip.pyi +0 -0
  58. {sidereon-3.0.0 → sidereon-3.0.2}/python/sidereon/py.typed +0 -0
  59. {sidereon-3.0.0 → sidereon-3.0.2}/rust-toolchain.toml +0 -0
  60. {sidereon-3.0.0 → sidereon-3.0.2}/scripts/coverage/bindmap.py +0 -0
  61. {sidereon-3.0.0 → sidereon-3.0.2}/scripts/coverage/pubapi.py +0 -0
  62. {sidereon-3.0.0 → sidereon-3.0.2}/scripts/coverage/render_coverage.py +0 -0
  63. {sidereon-3.0.0 → sidereon-3.0.2}/scripts/smoke-release.py +0 -0
  64. {sidereon-3.0.0 → sidereon-3.0.2}/src/almanac.rs +0 -0
  65. {sidereon-3.0.0 → sidereon-3.0.2}/src/angles.rs +0 -0
  66. {sidereon-3.0.0 → sidereon-3.0.2}/src/anomaly.rs +0 -0
  67. {sidereon-3.0.0 → sidereon-3.0.2}/src/antex.rs +0 -0
  68. {sidereon-3.0.0 → sidereon-3.0.2}/src/araim.rs +0 -0
  69. {sidereon-3.0.0 → sidereon-3.0.2}/src/atmosphere.rs +0 -0
  70. {sidereon-3.0.0 → sidereon-3.0.2}/src/bias.rs +0 -0
  71. {sidereon-3.0.0 → sidereon-3.0.2}/src/bodies.rs +0 -0
  72. {sidereon-3.0.0 → sidereon-3.0.2}/src/body_observe.rs +0 -0
  73. {sidereon-3.0.0 → sidereon-3.0.2}/src/broadcast_comparison.rs +0 -0
  74. {sidereon-3.0.0 → sidereon-3.0.2}/src/cdm.rs +0 -0
  75. {sidereon-3.0.0 → sidereon-3.0.2}/src/clock_stability.rs +0 -0
  76. {sidereon-3.0.0 → sidereon-3.0.2}/src/conjunction.rs +0 -0
  77. {sidereon-3.0.0 → sidereon-3.0.2}/src/constellation.rs +0 -0
  78. {sidereon-3.0.0 → sidereon-3.0.2}/src/core_error_detail.rs +0 -0
  79. {sidereon-3.0.0 → sidereon-3.0.2}/src/covariance.rs +0 -0
  80. {sidereon-3.0.0 → sidereon-3.0.2}/src/coverage.rs +0 -0
  81. {sidereon-3.0.0 → sidereon-3.0.2}/src/data_catalog.rs +0 -0
  82. {sidereon-3.0.0 → sidereon-3.0.2}/src/defaults.rs +0 -0
  83. {sidereon-3.0.0 → sidereon-3.0.2}/src/dgnss.rs +0 -0
  84. {sidereon-3.0.0 → sidereon-3.0.2}/src/doppler.rs +0 -0
  85. {sidereon-3.0.0 → sidereon-3.0.2}/src/elements.rs +0 -0
  86. {sidereon-3.0.0 → sidereon-3.0.2}/src/emission.rs +0 -0
  87. {sidereon-3.0.0 → sidereon-3.0.2}/src/ephemeris.rs +0 -0
  88. {sidereon-3.0.0 → sidereon-3.0.2}/src/equinoctial.rs +0 -0
  89. {sidereon-3.0.0 → sidereon-3.0.2}/src/error_metrics.rs +0 -0
  90. {sidereon-3.0.0 → sidereon-3.0.2}/src/estimation.rs +0 -0
  91. {sidereon-3.0.0 → sidereon-3.0.2}/src/events.rs +0 -0
  92. {sidereon-3.0.0 → sidereon-3.0.2}/src/exact_cache.rs +0 -0
  93. {sidereon-3.0.0 → sidereon-3.0.2}/src/exact_time.rs +0 -0
  94. {sidereon-3.0.0 → sidereon-3.0.2}/src/fallback.rs +0 -0
  95. {sidereon-3.0.0 → sidereon-3.0.2}/src/forces.rs +0 -0
  96. {sidereon-3.0.0 → sidereon-3.0.2}/src/format_diagnostics.rs +0 -0
  97. {sidereon-3.0.0 → sidereon-3.0.2}/src/frame_catalog.rs +0 -0
  98. {sidereon-3.0.0 → sidereon-3.0.2}/src/frames.rs +0 -0
  99. {sidereon-3.0.0 → sidereon-3.0.2}/src/fusion.rs +0 -0
  100. {sidereon-3.0.0 → sidereon-3.0.2}/src/geodesic.rs +0 -0
  101. {sidereon-3.0.0 → sidereon-3.0.2}/src/geodetic_time_series.rs +0 -0
  102. {sidereon-3.0.0 → sidereon-3.0.2}/src/geofence.rs +0 -0
  103. {sidereon-3.0.0 → sidereon-3.0.2}/src/geoid.rs +0 -0
  104. {sidereon-3.0.0 → sidereon-3.0.2}/src/geometry.rs +0 -0
  105. {sidereon-3.0.0 → sidereon-3.0.2}/src/geometry_quality.rs +0 -0
  106. {sidereon-3.0.0 → sidereon-3.0.2}/src/ils.rs +0 -0
  107. {sidereon-3.0.0 → sidereon-3.0.2}/src/iod.rs +0 -0
  108. {sidereon-3.0.0 → sidereon-3.0.2}/src/ionex.rs +0 -0
  109. {sidereon-3.0.0 → sidereon-3.0.2}/src/lambert.rs +0 -0
  110. {sidereon-3.0.0 → sidereon-3.0.2}/src/leap.rs +0 -0
  111. {sidereon-3.0.0 → sidereon-3.0.2}/src/least_squares.rs +0 -0
  112. {sidereon-3.0.0 → sidereon-3.0.2}/src/lib.rs +0 -0
  113. {sidereon-3.0.0 → sidereon-3.0.2}/src/lnav.rs +0 -0
  114. {sidereon-3.0.0 → sidereon-3.0.2}/src/marshal.rs +0 -0
  115. {sidereon-3.0.0 → sidereon-3.0.2}/src/nmea.rs +0 -0
  116. {sidereon-3.0.0 → sidereon-3.0.2}/src/normality.rs +0 -0
  117. {sidereon-3.0.0 → sidereon-3.0.2}/src/ntrip.rs +0 -0
  118. {sidereon-3.0.0 → sidereon-3.0.2}/src/observables.rs +0 -0
  119. {sidereon-3.0.0 → sidereon-3.0.2}/src/observation.rs +0 -0
  120. {sidereon-3.0.0 → sidereon-3.0.2}/src/oem.rs +0 -0
  121. {sidereon-3.0.0 → sidereon-3.0.2}/src/omm.rs +0 -0
  122. {sidereon-3.0.0 → sidereon-3.0.2}/src/opm.rs +0 -0
  123. {sidereon-3.0.0 → sidereon-3.0.2}/src/orbit_determination.rs +0 -0
  124. {sidereon-3.0.0 → sidereon-3.0.2}/src/ppp.rs +0 -0
  125. {sidereon-3.0.0 → sidereon-3.0.2}/src/ppp_corrections.rs +0 -0
  126. {sidereon-3.0.0 → sidereon-3.0.2}/src/products.rs +0 -0
  127. {sidereon-3.0.0 → sidereon-3.0.2}/src/propagation.rs +0 -0
  128. {sidereon-3.0.0 → sidereon-3.0.2}/src/qc.rs +0 -0
  129. {sidereon-3.0.0 → sidereon-3.0.2}/src/reduced_orbit.rs +0 -0
  130. {sidereon-3.0.0 → sidereon-3.0.2}/src/relative.rs +0 -0
  131. {sidereon-3.0.0 → sidereon-3.0.2}/src/reliability.rs +0 -0
  132. {sidereon-3.0.0 → sidereon-3.0.2}/src/rf.rs +0 -0
  133. {sidereon-3.0.0 → sidereon-3.0.2}/src/rinex.rs +0 -0
  134. {sidereon-3.0.0 → sidereon-3.0.2}/src/rinex_clock.rs +0 -0
  135. {sidereon-3.0.0 → sidereon-3.0.2}/src/rtcm.rs +0 -0
  136. {sidereon-3.0.0 → sidereon-3.0.2}/src/rtk.rs +0 -0
  137. {sidereon-3.0.0 → sidereon-3.0.2}/src/sbas_pl.rs +0 -0
  138. {sidereon-3.0.0 → sidereon-3.0.2}/src/sbas_ssr.rs +0 -0
  139. {sidereon-3.0.0 → sidereon-3.0.2}/src/scenario.rs +0 -0
  140. {sidereon-3.0.0 → sidereon-3.0.2}/src/sidereal.rs +0 -0
  141. {sidereon-3.0.0 → sidereon-3.0.2}/src/signal_analysis.rs +0 -0
  142. {sidereon-3.0.0 → sidereon-3.0.2}/src/sky.rs +0 -0
  143. {sidereon-3.0.0 → sidereon-3.0.2}/src/solve_error_detail.rs +0 -0
  144. {sidereon-3.0.0 → sidereon-3.0.2}/src/source_localization.rs +0 -0
  145. {sidereon-3.0.0 → sidereon-3.0.2}/src/space_weather.rs +0 -0
  146. {sidereon-3.0.0 → sidereon-3.0.2}/src/spk.rs +0 -0
  147. {sidereon-3.0.0 → sidereon-3.0.2}/src/spp.rs +0 -0
  148. {sidereon-3.0.0 → sidereon-3.0.2}/src/spp_error_detail.rs +0 -0
  149. {sidereon-3.0.0 → sidereon-3.0.2}/src/staleness.rs +0 -0
  150. {sidereon-3.0.0 → sidereon-3.0.2}/src/static_positioning.rs +0 -0
  151. {sidereon-3.0.0 → sidereon-3.0.2}/src/tca.rs +0 -0
  152. {sidereon-3.0.0 → sidereon-3.0.2}/src/tdm.rs +0 -0
  153. {sidereon-3.0.0 → sidereon-3.0.2}/src/terrain.rs +0 -0
  154. {sidereon-3.0.0 → sidereon-3.0.2}/src/terrain_store.rs +0 -0
  155. {sidereon-3.0.0 → sidereon-3.0.2}/src/tides.rs +0 -0
  156. {sidereon-3.0.0 → sidereon-3.0.2}/src/time_model_error.rs +0 -0
  157. {sidereon-3.0.0 → sidereon-3.0.2}/src/tle_fit_error_detail.rs +0 -0
  158. {sidereon-3.0.0 → sidereon-3.0.2}/src/tropo.rs +0 -0
  159. {sidereon-3.0.0 → sidereon-3.0.2}/src/unix_compress.rs +0 -0
  160. {sidereon-3.0.0 → sidereon-3.0.2}/tests/_helpers.py +0 -0
  161. {sidereon-3.0.0 → sidereon-3.0.2}/tests/_sp3_merge.py +0 -0
  162. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/README.md +0 -0
  163. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/antex/igs20_pasa_scoa_gps.atx +0 -0
  164. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/antex/igs20_wettzell_trim.atx +0 -0
  165. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/batch_fleet.json +0 -0
  166. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/cdm.json +0 -0
  167. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/clk/lossless/rinex_clock304_table_a17.clk +0 -0
  168. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/clk/lossless/rinex_clock304_table_a18.clk +0 -0
  169. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/clk/synthetic_rinex_clock.clk +0 -0
  170. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/conjunction.json +0 -0
  171. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/constellation/gps_ops_sample.json +0 -0
  172. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/constellation/navcen_forecast_cases.html +0 -0
  173. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/constellation/navcen_gps_sample.html +0 -0
  174. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/events_bodies_dop.json +0 -0
  175. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/format_diagnostics_inputs.json +0 -0
  176. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/frames_time.json +0 -0
  177. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/geodesic/geodtest_row.dat +0 -0
  178. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/geoid/egm2008_25_norcal_crop.bin +0 -0
  179. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/gnss_data/igs22376.sp3.Z.b64 +0 -0
  180. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/gnss_data/ncompress-current-b9-counter.Z.b64 +0 -0
  181. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/listings/aiub-iono-prd-20260923.csv +0 -0
  182. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/listings/gfz-ultra-w2430-20260804.html +0 -0
  183. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/nav/BRDC00GOP_R_20210010000_01D_MN.rnx +0 -0
  184. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/nav/ESBC00DNK_R_20201770000_01D_MN.rnx +0 -0
  185. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/nav/KMS300DNK_R_20221591000_01H_MN.rnx +0 -0
  186. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/numerical_propagation.json +0 -0
  187. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/obs/ESBC00DNK_R_20201770000_01D_30S_MO_trim.crx +0 -0
  188. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/obs/ESBC00DNK_R_20201770000_01D_30S_MO_trim.rnx +0 -0
  189. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/obs/algo0010_2015001_v1_trim.crx +0 -0
  190. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/obs/algo0010_2015001_v1_trim.rnx +0 -0
  191. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/omm.json +0 -0
  192. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/pass_finder.json +0 -0
  193. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/rf_link_budget.json +0 -0
  194. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/rtk_wtzr.json +0 -0
  195. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/scenario_base.json +0 -0
  196. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/sgp4_topocentric.json +0 -0
  197. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/sp3/IGS0OPSFIN_20261200945_02H30M_15M_ORB.SP3 +0 -0
  198. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/sp3-merge-input-v1.json +0 -0
  199. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/sp3-merge-report-v3.json +0 -0
  200. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/sp3_bodies.json +0 -0
  201. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/spk/horizons_eros_type21.bsp +0 -0
  202. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/tdm/annex_e_01.kvn +0 -0
  203. {sidereon-3.0.0 → sidereon-3.0.2}/tests/fixtures/tle_roundtrip.json +0 -0
  204. {sidereon-3.0.0 → sidereon-3.0.2}/tests/golden/sp3-terminal-record-v1.json +0 -0
  205. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_012_bindings.py +0 -0
  206. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_013_bindings.py +0 -0
  207. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_014_bindings.py +0 -0
  208. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_015_bindings.py +0 -0
  209. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_016_wave2_bindings.py +0 -0
  210. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_017_domain_exposure.py +0 -0
  211. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_018_domain_exposure.py +0 -0
  212. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_019_track.py +0 -0
  213. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_020_error_metrics.py +0 -0
  214. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_antex.py +0 -0
  215. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_antex_public_pcv_sample_fields.py +0 -0
  216. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_atmosphere.py +0 -0
  217. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_attested_open.py +0 -0
  218. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_batch.py +0 -0
  219. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_broadcast_comparison.py +0 -0
  220. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_broadcast_fallback.py +0 -0
  221. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_cdm.py +0 -0
  222. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_cdm_top_level.py +0 -0
  223. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_cdm_value_equality.py +0 -0
  224. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_codec_error_details.py +0 -0
  225. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_conjunction.py +0 -0
  226. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_constellation.py +0 -0
  227. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_constellation_fleet.py +0 -0
  228. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_core_defaults.py +0 -0
  229. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_core_public_api_parity.py +0 -0
  230. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_core_round3_surfaces.py +0 -0
  231. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_covariance.py +0 -0
  232. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_covariance_propagation.py +0 -0
  233. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_coverage.py +0 -0
  234. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_crinex.py +0 -0
  235. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_data.py +0 -0
  236. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_data_publication_resilience.py +0 -0
  237. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_dgnss.py +0 -0
  238. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_distribution.py +0 -0
  239. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_dop_at_epoch.py +0 -0
  240. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_dop_convention.py +0 -0
  241. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_doppler.py +0 -0
  242. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_dted_tile_error_details.py +0 -0
  243. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_egm96.py +0 -0
  244. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_elements.py +0 -0
  245. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_events_bodies_dop.py +0 -0
  246. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_exact_cache_atomicity.py +0 -0
  247. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_field_mode_bindings.py +0 -0
  248. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_forces.py +0 -0
  249. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_format_diagnostics.py +0 -0
  250. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_frames_time.py +0 -0
  251. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ftp_transport.py +0 -0
  252. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_fusion_error_details.py +0 -0
  253. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_geoid.py +0 -0
  254. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_geometry.py +0 -0
  255. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ils.py +0 -0
  256. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_import.py +0 -0
  257. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_inertial_mechanization.py +0 -0
  258. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ionex.py +0 -0
  259. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ionex_instant_queries.py +0 -0
  260. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ionex_policies.py +0 -0
  261. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ionosphere_nequick.py +0 -0
  262. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_klobuchar_native.py +0 -0
  263. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_lambert_iod.py +0 -0
  264. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_leap_seconds.py +0 -0
  265. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_least_squares.py +0 -0
  266. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_lnav.py +0 -0
  267. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_moving_baseline.py +0 -0
  268. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_new_core_api.py +0 -0
  269. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_next_issue_due.py +0 -0
  270. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_nmea_finish_output.py +0 -0
  271. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ntrip.py +0 -0
  272. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_numerical.py +0 -0
  273. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_obs_policies.py +0 -0
  274. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_observables.py +0 -0
  275. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_observables_broadcast.py +0 -0
  276. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_observables_error_details.py +0 -0
  277. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_observation.py +0 -0
  278. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_observe_batch.py +0 -0
  279. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_oem.py +0 -0
  280. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_oem_full_dto.py +0 -0
  281. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_omm.py +0 -0
  282. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_omm_full_dto.py +0 -0
  283. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_opm.py +0 -0
  284. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_opm_full_dto.py +0 -0
  285. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_parity_gaps.py +0 -0
  286. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_parse_tle_file.py +0 -0
  287. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_passes.py +0 -0
  288. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ppp.py +0 -0
  289. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ppp_corrections.py +0 -0
  290. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_ppp_range_corrections.py +0 -0
  291. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_precise_samples.py +0 -0
  292. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_precise_samples_errors.py +0 -0
  293. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_propagation.py +0 -0
  294. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_qc.py +0 -0
  295. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_reduced_orbit.py +0 -0
  296. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_reduced_orbit_piecewise.py +0 -0
  297. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_residual_stats.py +0 -0
  298. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rf.py +0 -0
  299. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rinex_clock.py +0 -0
  300. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rinex_clock_header_field_details.py +0 -0
  301. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rinex_clock_lossless.py +0 -0
  302. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rinex_nav.py +0 -0
  303. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rinex_obs.py +0 -0
  304. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rtcm.py +0 -0
  305. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rtk.py +0 -0
  306. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_rtk_unresolved_carriers.py +0 -0
  307. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sbas_decoded_lists.py +0 -0
  308. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sbas_unassigned_mask.py +0 -0
  309. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_scenario_external_sources.py +0 -0
  310. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sky.py +0 -0
  311. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_bodies.py +0 -0
  312. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_catalog_exact.py +0 -0
  313. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_clock_reference.py +0 -0
  314. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_interpolation_policy.py +0 -0
  315. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_merge.py +0 -0
  316. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_merge_coverage.py +0 -0
  317. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_merge_per_epoch_provenance.py +0 -0
  318. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_terminal_record.py +0 -0
  319. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_window_continuity.py +0 -0
  320. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_sp3_write_and_records.py +0 -0
  321. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_spk.py +0 -0
  322. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_spp.py +0 -0
  323. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_spp_error_details.py +0 -0
  324. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_spp_glonass.py +0 -0
  325. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_spp_robustness.py +0 -0
  326. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_staleness.py +0 -0
  327. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_stub_accuracy.py +0 -0
  328. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_tca.py +0 -0
  329. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_tdm_policies.py +0 -0
  330. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_terrain_unknown_elevation.py +0 -0
  331. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_tides.py +0 -0
  332. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_time_model_error_details.py +0 -0
  333. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_time_offset_error_details.py +0 -0
  334. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_tle_fit.py +0 -0
  335. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_tle_roundtrip.py +0 -0
  336. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_tropo.py +0 -0
  337. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_unix_compress.py +0 -0
  338. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_visible_ground_track.py +0 -0
  339. {sidereon-3.0.0 → sidereon-3.0.2}/tests/test_weather_terrain_error_details.py +0 -0
  340. {sidereon-3.0.0 → sidereon-3.0.2}/third_party_licenses/ERFA-BSD-3-Clause.txt +0 -0
  341. {sidereon-3.0.0 → sidereon-3.0.2}/third_party_licenses/IERS-CONVENTIONS-SOFTWARE-LICENSE.txt +0 -0
  342. {sidereon-3.0.0 → sidereon-3.0.2}/third_party_licenses/SciPy-BSD-3-Clause.txt +0 -0
  343. {sidereon-3.0.0/third_party_source/sidereon-core-3.0.0 → sidereon-3.0.2/third_party_source/sidereon-core-3.0.2}/tides/mod.rs +0 -0
  344. {sidereon-3.0.0/third_party_source/sidereon-core-3.0.0 → sidereon-3.0.2/third_party_source/sidereon-core-3.0.2}/tides/ocean.rs +0 -0
  345. {sidereon-3.0.0/third_party_source/sidereon-core-3.0.0 → 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.0/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.0/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.0/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.0/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
@@ -2,7 +2,26 @@
2
2
 
3
3
  All notable changes to the Sidereon Python interface are documented here.
4
4
 
5
- ## [3.0.0]
5
+ ## [Unreleased]
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
+
14
+ ## [3.0.1] - 2026-10-04
15
+
16
+ ### Added
17
+
18
+ - Opt-in `acquire(..., http_client_exception_diagnostics=callback)` reports
19
+ the class and bounded module/function metadata of unexpected exceptions
20
+ from caller-supplied HTTP transports. Diagnostics omit messages, arguments,
21
+ URLs and request/response data; observer failures leave the original terminal
22
+ exception unchanged.
23
+
24
+ ## [3.0.0] - 2026-10-04
6
25
 
7
26
  ### Added
8
27
 
@@ -70,7 +89,7 @@ All notable changes to the Sidereon Python interface are documented here.
70
89
  - `Sp3MergeOptions` no longer refuses a `target_epoch_interval_s` that is not a whole number of seconds. The merge takes any interval a whole number of 10-nanosecond ticks states and the core refuses the rest; the persisted merge-input identity still binds whole seconds only, and the core refuses a fractional interval there.
71
90
  - **Breaking.** The robust (Huber) reweighting runs until it settles: `SPP_DEFAULT_ROBUST_MAX_OUTER`, the default `SppRobustConfig.max_outer`, is 100 instead of 5, a safeguard rather than a working budget. A reweighting that cycles stops with the new status `outer_oscillation`.
72
91
 
73
- - Engine update: sidereon and sidereon-core 3.0.0, at core revision `2373644611953e1833c0deb94057acc34bcec973`. Every core breaking change below reaches Python as the core states it; the core CHANGELOG gives the reason for each.
92
+ - Engine update: sidereon and sidereon-core 3.0.0, at core revision `e2fb3dfdc392d23087ed8aa1ee028a0056b4021b`. Every core breaking change below reaches Python as the core states it; the core CHANGELOG gives the reason for each.
74
93
  - **Breaking.** `NmeaDiagnostics.skips` and `warnings` are lists of `FormatSkip` and `FormatWarning` instead of strings, and `NmeaDiagnostics` is `FormatDiagnostics`.
75
94
  - **Breaking.** `BiasSet.code_osb_seconds`, `phase_osb_cycles`, `code_dsb_seconds` and `code_bias_model_m` return a `BiasLookup` whose `status` names the outcome (`available`, `absent`, `unsupported_scale`, `ambiguous`, `carrier_frequency_required`, `invalid_carrier_frequency`, `carrier_frequency_unknown`, `undefined_slope_reference`, `invalid_epoch`) instead of a float or `None`. A phase bias stated in nanoseconds needs `carrier_hz`. `BiasSet.time_scale` is `None` for a product with no usable time scale, and a lookup on such a product needs `time_scale`. `BiasRecord.is_phase` follows the observable code, so a phase bias stated in nanoseconds is a phase bias.
76
95
  - **Breaking.** The Bias-SINEX readers are strict by default and raise `ValueError` for a file that departs from Bias-SINEX 1.00 or states another version; `BiasReadPolicy.LENIENT` reads it and reports each departure in `BiasSet.notices`. A CODE DCB title whose time-system label names no known scale is refused likewise.
@@ -611,9 +611,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
611
611
 
612
612
  [[package]]
613
613
  name = "sidereon"
614
- version = "3.0.0"
614
+ version = "3.0.2"
615
615
  source = "registry+https://github.com/rust-lang/crates.io-index"
616
- checksum = "83ff5c00bdcbd35203e3bfc34dfcd6c08783d522f1163b64e45dceb31d61fc69"
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.0"
625
+ version = "3.0.2"
626
626
  source = "registry+https://github.com/rust-lang/crates.io-index"
627
- checksum = "a2f968098493ebfd6694cc318f4a4d15df5933342e0bbd1210dbc46c176d3994"
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.0"
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.0"
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.0" }
29
- sidereon-core = { version = "=3.0.0", 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.0
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,8 +330,10 @@ 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.0/tides/` in wheels and source
334
- distributions. The authoritative original is available from:
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
+ The authoritative original is available from:
335
337
 
336
338
  <https://iers-conventions.obspm.fr/content/chapter7/software/dehanttideinel/DEHANTTIDEINEL.F>
337
339
 
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "sidereon"
7
- version = "3.0.0"
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.0/tides/mod.rs",
86
- "third_party_source/sidereon-core-3.0.0/tides/ocean.rs",
87
- "third_party_source/sidereon-core-3.0.0/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
  ]
@@ -3213,4 +3213,4 @@ try:
3213
3213
 
3214
3214
  __version__ = _pkg_version("sidereon")
3215
3215
  except Exception: # package metadata unavailable (e.g. running from a source tree)
3216
- __version__ = "3.0.0"
3216
+ __version__ = "3.0.1"
@@ -16,10 +16,11 @@ import json
16
16
  import netrc
17
17
  import os
18
18
  import time
19
+ from collections import deque
19
20
  from dataclasses import asdict, dataclass, field, replace
20
21
  from enum import Enum
21
22
  from pathlib import Path
22
- from typing import Mapping, Optional, Sequence, Tuple, Union, cast
23
+ from typing import Callable, Mapping, Optional, Sequence, Tuple, Union, cast
23
24
  from urllib.parse import urljoin, urlsplit, urlunsplit
24
25
 
25
26
  import httpx
@@ -660,8 +661,19 @@ def acquire(
660
661
  max_archive_bytes: int = _DEFAULT_MAX_ARCHIVE_BYTES,
661
662
  max_product_bytes: int = _DEFAULT_MAX_PRODUCT_BYTES,
662
663
  http_client: Optional[httpx.Client] = None,
664
+ http_client_exception_diagnostics: Optional[
665
+ Callable[[Mapping[str, object]], None]
666
+ ] = None,
663
667
  ) -> AcquiredProduct:
664
- """Acquire the exact product from only the ordered allowed distributors."""
668
+ """Acquire an exact product from the ordered distributors.
669
+
670
+ When a caller-supplied HTTP transport raises an unexpected exception, the
671
+ optional diagnostic callback receives only the exception class, a client
672
+ callsite, and up to eight module/function/arity frames. Exception messages,
673
+ arguments, URLs, headers, and response bodies are omitted. The original
674
+ exception is re-raised unchanged, and callback failures are ignored.
675
+ Payload keys are ``exception_class``, ``client_callsite`` and ``stack_frames``.
676
+ """
665
677
  return _acquire_impl(
666
678
  exact_request,
667
679
  cache_dir=cache_dir,
@@ -675,6 +687,7 @@ def acquire(
675
687
  max_archive_bytes=max_archive_bytes,
676
688
  max_product_bytes=max_product_bytes,
677
689
  http_client=http_client,
690
+ http_client_exception_diagnostics=http_client_exception_diagnostics,
678
691
  direct_location=None,
679
692
  )
680
693
 
@@ -706,6 +719,9 @@ def _acquire_impl(
706
719
  max_archive_bytes: int = _DEFAULT_MAX_ARCHIVE_BYTES,
707
720
  max_product_bytes: int = _DEFAULT_MAX_PRODUCT_BYTES,
708
721
  http_client: Optional[httpx.Client] = None,
722
+ http_client_exception_diagnostics: Optional[
723
+ Callable[[Mapping[str, object]], None]
724
+ ] = None,
709
725
  direct_location: Optional[tuple[str, str]],
710
726
  ) -> AcquiredProduct:
711
727
  _validate_requested_identity(exact_request.identity)
@@ -779,6 +795,7 @@ def _acquire_impl(
779
795
  max_archive_bytes,
780
796
  max_product_bytes,
781
797
  http_client,
798
+ http_client_exception_diagnostics,
782
799
  attempts,
783
800
  exact_cache,
784
801
  direct_location,
@@ -850,6 +867,7 @@ def _acquire_one(
850
867
  max_archive_bytes: int,
851
868
  max_product_bytes: int,
852
869
  http_client: Optional[httpx.Client],
870
+ http_client_exception_diagnostics: Optional[Callable[[Mapping[str, object]], None]],
853
871
  attempts: Sequence[SourceFailure],
854
872
  exact_cache: _exact_cache.ExactCache,
855
873
  direct_location: Optional[tuple[str, str]],
@@ -871,6 +889,7 @@ def _acquire_one(
871
889
  backoff_s,
872
890
  max_archive_bytes,
873
891
  http_client,
892
+ http_client_exception_diagnostics,
874
893
  )
875
894
  archive = download.archive
876
895
  elif source is DistributionSource.NASA_CDDIS:
@@ -885,6 +904,7 @@ def _acquire_one(
885
904
  backoff_s,
886
905
  max_archive_bytes,
887
906
  http_client,
907
+ http_client_exception_diagnostics,
888
908
  )
889
909
  archive = download.archive
890
910
  elif source is DistributionSource.LOCAL_FILE:
@@ -1081,6 +1101,7 @@ def _download_http(
1081
1101
  backoff_s: float,
1082
1102
  max_bytes: int,
1083
1103
  supplied_client: Optional[httpx.Client],
1104
+ http_client_exception_diagnostics: Optional[Callable[[Mapping[str, object]], None]],
1084
1105
  ) -> _Download:
1085
1106
  last_error: Optional[AcquisitionError] = None
1086
1107
  if urlsplit(original_url).scheme == "ftp":
@@ -1100,7 +1121,17 @@ def _download_http(
1100
1121
  for attempt in range(retries):
1101
1122
  try:
1102
1123
  return _download_http_once(
1103
- client, original_url, source, auth, timeout_s, max_bytes
1124
+ client,
1125
+ original_url,
1126
+ source,
1127
+ auth,
1128
+ timeout_s,
1129
+ max_bytes,
1130
+ (
1131
+ http_client_exception_diagnostics
1132
+ if supplied_client is not None
1133
+ else None
1134
+ ),
1104
1135
  )
1105
1136
  except AcquisitionError as error:
1106
1137
  last_error = error
@@ -1114,6 +1145,34 @@ def _download_http(
1114
1145
  client.close()
1115
1146
 
1116
1147
 
1148
+ def _http_client_exception_diagnostic(error: Exception) -> Mapping[str, object]:
1149
+ """Return bounded exception metadata without reading message or arguments."""
1150
+ exception_type = type(error)
1151
+ class_name = f"{exception_type.__module__}.{exception_type.__qualname__}"[:256]
1152
+ frames: deque[tuple[str, str, int]] = deque(maxlen=8)
1153
+ traceback = error.__traceback__
1154
+ while traceback is not None:
1155
+ frame = traceback.tb_frame
1156
+ module = frame.f_globals.get("__name__", "")
1157
+ module_name = module[:256] if isinstance(module, str) else ""
1158
+ function_name = frame.f_code.co_name[:256]
1159
+ frames.append((module_name, function_name, frame.f_code.co_argcount))
1160
+ traceback = traceback.tb_next
1161
+ stack_frames = tuple(frames)
1162
+ caller_frames = [
1163
+ frame
1164
+ for frame in stack_frames
1165
+ if not frame[0].startswith("httpx.")
1166
+ and frame[0] not in {"contextlib", __name__}
1167
+ ]
1168
+ callsite = caller_frames[-1] if caller_frames else ("", "", 0)
1169
+ return {
1170
+ "exception_class": class_name,
1171
+ "client_callsite": callsite,
1172
+ "stack_frames": stack_frames,
1173
+ }
1174
+
1175
+
1117
1176
  def _download_http_once(
1118
1177
  client: httpx.Client,
1119
1178
  original_url: str,
@@ -1121,6 +1180,7 @@ def _download_http_once(
1121
1180
  auth: EarthdataAuth,
1122
1181
  timeout_s: float,
1123
1182
  max_bytes: int,
1183
+ http_client_exception_diagnostics: Optional[Callable[[Mapping[str, object]], None]],
1124
1184
  ) -> _Download:
1125
1185
  current = original_url
1126
1186
  for redirect_count in range(_MAX_REDIRECTS + 1):
@@ -1208,6 +1268,15 @@ def _download_http_once(
1208
1268
  raise TransportFailure("connection", current) from None
1209
1269
  except httpx.RequestError:
1210
1270
  raise TransportFailure("other", current) from None
1271
+ except Exception as error: # noqa: BLE001 - caller transports may raise arbitrary errors
1272
+ if http_client_exception_diagnostics is not None:
1273
+ try:
1274
+ diagnostic = _http_client_exception_diagnostic(error)
1275
+ if diagnostic["client_callsite"] != ("", "", 0):
1276
+ http_client_exception_diagnostics(diagnostic)
1277
+ except BaseException: # noqa: BLE001 - diagnostics never mask the transport
1278
+ pass
1279
+ raise
1211
1280
  raise RedirectPolicyFailure(f"too many redirects for {_sanitize_url(original_url)}")
1212
1281
 
1213
1282
 
@@ -4,7 +4,7 @@ import datetime as dt
4
4
  import enum
5
5
  import os
6
6
  from dataclasses import dataclass
7
- from typing import Mapping, Optional, Sequence, Tuple, Union
7
+ from typing import Callable, Mapping, Optional, Sequence, Tuple, Union
8
8
 
9
9
  import httpx
10
10
 
@@ -185,6 +185,9 @@ def acquire(
185
185
  max_archive_bytes: int = ...,
186
186
  max_product_bytes: int = ...,
187
187
  http_client: Optional[httpx.Client] = ...,
188
+ http_client_exception_diagnostics: Optional[
189
+ Callable[[Mapping[str, object]], None]
190
+ ] = ...,
188
191
  ) -> AcquiredProduct: ...
189
192
 
190
193
  __all__: list[str]
@@ -13,6 +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 = "8694648892d8ab065d94014e2e21d6cd5054818e"
16
17
 
17
18
 
18
19
  def registry_version(name, value, package_version):
@@ -113,7 +114,7 @@ def validate_trust_region_dependency(
113
114
  value, engine_dependencies, package_version, *, allow_candidate
114
115
  ):
115
116
  """Accept the registry release or its exact coordinated Git candidate."""
116
- if value == "0.11.0":
117
+ if value in ("0.11.0", "=0.11.0"):
117
118
  return
118
119
  if not allow_candidate:
119
120
  raise ValueError(
@@ -308,7 +309,7 @@ def main() -> None:
308
309
  "ocean.rs": "25946677944425671a92717860ac2d70f255de5403eeb1fbf98b361716821d5c",
309
310
  "pole.rs": "b4cc4c16bdd8ce1d8f04073602ab47dfb85a002b946ab192e8d4d2d600f0a1f8",
310
311
  }
311
- tide_root = ROOT / "third_party_source" / "sidereon-core-3.0.0" / "tides"
312
+ tide_root = ROOT / "third_party_source" / "sidereon-core-3.0.2" / "tides"
312
313
  for filename, expected_digest in tide_sources.items():
313
314
  source = tide_root / filename
314
315
  if not source.is_file():
@@ -317,7 +318,8 @@ def main() -> None:
317
318
  if digest != expected_digest:
318
319
  raise SystemExit(
319
320
  f"IERS-derived source disclosure {source} has digest {digest}, "
320
- f"expected {expected_digest} from sidereon-core 3.0.0"
321
+ f"expected {expected_digest} from sidereon-core 3.0.2 at "
322
+ f"{CORE_SOURCE_REVISION}"
321
323
  )
322
324
 
323
325
  if args.tag is not None and args.tag != f"v{python_version}":
@@ -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.0";
11
+ const RELEASE_VERSION: &str = "3.0.2";
12
12
 
13
13
  #[derive(Debug)]
14
14
  struct LockedPackage {
@@ -343,7 +343,7 @@ mod tests {
343
343
  NEXT_TEMP.fetch_add(1, Ordering::Relaxed)
344
344
  ));
345
345
  let cache = temp.join("registry/cache/test-index");
346
- let extracted = temp.join("registry/src/test-index/sidereon-3.0.0");
346
+ let extracted = temp.join("registry/src/test-index/sidereon-3.0.1");
347
347
  fs::create_dir_all(&cache).expect("create registry cache");
348
348
  fs::create_dir_all(&extracted).expect("create extracted source");
349
349
  fs::write(
@@ -355,17 +355,17 @@ mod tests {
355
355
  let revision = "0123456789abcdef0123456789abcdef01234567";
356
356
  let archive = crate_archive(
357
357
  "sidereon",
358
- "3.0.0",
359
- "[package]\nname = \"sidereon\"\nversion = \"3.0.0\"\n",
358
+ "3.0.1",
359
+ "[package]\nname = \"sidereon\"\nversion = \"3.0.1\"\n",
360
360
  &format!(r#"{{"git":{{"sha1":"{revision}","dirty":false}}}}"#),
361
361
  );
362
362
  let checksum = format!("{:x}", Sha256::digest(&archive));
363
- fs::write(cache.join("sidereon-3.0.0.crate"), archive).expect("write crate archive");
363
+ fs::write(cache.join("sidereon-3.0.1.crate"), archive).expect("write crate archive");
364
364
  assert_eq!(
365
- registry_revision(&temp, "sidereon", "3.0.0", &checksum).unwrap(),
365
+ registry_revision(&temp, "sidereon", "3.0.1", &checksum).unwrap(),
366
366
  revision
367
367
  );
368
- assert!(registry_revision(&temp, "sidereon", "3.0.0", &"f".repeat(64)).is_err());
368
+ assert!(registry_revision(&temp, "sidereon", "3.0.1", &"f".repeat(64)).is_err());
369
369
  fs::remove_dir_all(temp).expect("remove registry test directory");
370
370
  }
371
371
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "source": "scripts/core_goldens",
3
- "core_revision": "e2fb3dfdc392d23087ed8aa1ee028a0056b4021b",
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": "e2fb3dfdc392d23087ed8aa1ee028a0056b4021b"
16523
+ "core_revision": "8694648892d8ab065d94014e2e21d6cd5054818e"
16524
16524
  }
@@ -0,0 +1,166 @@
1
+ """Direct equality coverage for public ANTEX values and parse details."""
2
+
3
+ import sidereon
4
+
5
+
6
+ def _record(content, label):
7
+ assert len(content) <= 60
8
+ return f"{content:<60}{label}"
9
+
10
+
11
+ def _antex(
12
+ *,
13
+ version="1.4",
14
+ system="M",
15
+ header_comment="header note",
16
+ pcv_type="R",
17
+ reference_type="AOAD/M_T",
18
+ reference_serial="",
19
+ outer_comment="outside note",
20
+ grid=" 0.0 10.0 5.0",
21
+ calibration_date="01-JAN-20",
22
+ pco=" 1.00 2.00 3.00",
23
+ rms_pco=" 0.10 0.20 0.30",
24
+ sample=" NOAZI 1.00 2.00 3.00",
25
+ ):
26
+ """Return a valid, small ANTEX document with all nested value types."""
27
+ pcv_record = f"{pcv_type:<1}{'':19}{reference_type:<20}{reference_serial:<20}"
28
+ calibration = f"{'FIELD':<20}{'IGS':<20}{2:6d}{'':4}{calibration_date:<10}"
29
+ lines = [
30
+ _record(f" {version:<3} {system}", "ANTEX VERSION / SYST"),
31
+ _record(pcv_record, "PCV TYPE / REFANT"),
32
+ _record(header_comment, "COMMENT"),
33
+ _record("", "END OF HEADER"),
34
+ _record(outer_comment, "COMMENT"),
35
+ _record("", "START OF ANTENNA"),
36
+ _record(f"{'TESTANT':<20}{'SERIAL-1':<20}", "TYPE / SERIAL NO"),
37
+ _record(calibration, "METH / BY / # / DATE"),
38
+ _record(" 1", "# OF FREQUENCIES"),
39
+ _record(grid, "ZEN1 / ZEN2 / DZEN"),
40
+ _record(" G01", "START OF FREQUENCY"),
41
+ _record(pco, "NORTH / EAST / UP"),
42
+ sample,
43
+ _record(" G01", "END OF FREQUENCY"),
44
+ _record(" G01", "START OF FREQ RMS"),
45
+ _record(rms_pco, "NORTH / EAST / UP"),
46
+ _record(" NOAZI 0.01 0.02 0.03", ""),
47
+ _record(" G01", "END OF FREQ RMS"),
48
+ _record("", "END OF ANTENNA"),
49
+ _record("after block", "COMMENT"),
50
+ ]
51
+ return ("\n".join(lines) + "\n").encode("ascii")
52
+
53
+
54
+ def _invalid_pcv_type(value):
55
+ text = "\n".join(
56
+ [
57
+ _record(" 1.4 M", "ANTEX VERSION / SYST"),
58
+ _record(value, "PCV TYPE / REFANT"),
59
+ _record("", "END OF HEADER"),
60
+ ]
61
+ )
62
+ try:
63
+ sidereon.load_antex(text.encode("ascii"))
64
+ except sidereon.AntexParseError as error:
65
+ assert error.detail is not None
66
+ return error.detail
67
+ raise AssertionError("invalid PCV type unexpectedly parsed")
68
+
69
+
70
+ def _assert_eq_and_ne(equal_left, equal_again, different):
71
+ # These wrappers define __eq__ but rely on Python's normal __ne__ fallback.
72
+ assert equal_left == equal_again
73
+ assert not (equal_left != equal_again)
74
+ assert equal_left != different
75
+ assert not (equal_left == different)
76
+
77
+
78
+ def test_public_antex_value_and_error_detail_equality_protocol():
79
+ source = _antex()
80
+ parsed = sidereon.load_antex(source)
81
+ repeated = sidereon.load_antex(source)
82
+
83
+ header_changed = sidereon.load_antex(_antex(header_comment="changed header"))
84
+ version_changed = sidereon.load_antex(_antex(system="G"))
85
+ pcv_type_changed = sidereon.load_antex(
86
+ _antex(reference_type="OTHER ANT", reference_serial="SERIAL-2")
87
+ )
88
+ outer_comment_changed = sidereon.load_antex(_antex(outer_comment="changed outer"))
89
+ grid_changed = sidereon.load_antex(_antex(grid=" 0.0 12.0 6.0"))
90
+ calibration_changed = sidereon.load_antex(_antex(calibration_date="02-JAN-20"))
91
+ frequency_changed = sidereon.load_antex(
92
+ _antex(pco=" 1.50 2.00 3.00")
93
+ )
94
+ rms_changed = sidereon.load_antex(_antex(rms_pco=" 0.11 0.20 0.30"))
95
+ sample_changed = sidereon.load_antex(
96
+ _antex(sample=" NOAZI 1.00 2.50 3.00")
97
+ )
98
+
99
+ _assert_eq_and_ne(parsed, repeated, header_changed)
100
+
101
+ antenna = parsed.antenna("TESTANT SERIAL-1")
102
+ repeated_antenna = repeated.antenna("TESTANT SERIAL-1")
103
+ changed_antenna = grid_changed.antenna("TESTANT SERIAL-1")
104
+ assert antenna is not None and repeated_antenna is not None
105
+ assert changed_antenna is not None
106
+ _assert_eq_and_ne(antenna, repeated_antenna, changed_antenna)
107
+
108
+ _assert_eq_and_ne(parsed.header, repeated.header, header_changed.header)
109
+ _assert_eq_and_ne(
110
+ parsed.header.version,
111
+ repeated.header.version,
112
+ version_changed.header.version,
113
+ )
114
+ _assert_eq_and_ne(
115
+ parsed.header.pcv_type,
116
+ repeated.header.pcv_type,
117
+ pcv_type_changed.header.pcv_type,
118
+ )
119
+
120
+ _assert_eq_and_ne(
121
+ parsed.outer_comments[0],
122
+ repeated.outer_comments[0],
123
+ outer_comment_changed.outer_comments[0],
124
+ )
125
+ assert len(parsed.outer_comments) == 2
126
+
127
+ assert antenna.zenith_grid is not None
128
+ assert repeated_antenna.zenith_grid is not None
129
+ assert changed_antenna.zenith_grid is not None
130
+ _assert_eq_and_ne(
131
+ antenna.zenith_grid,
132
+ repeated_antenna.zenith_grid,
133
+ changed_antenna.zenith_grid,
134
+ )
135
+ _assert_eq_and_ne(
136
+ antenna.calibrations[0],
137
+ repeated_antenna.calibrations[0],
138
+ calibration_changed.antenna("TESTANT SERIAL-1").calibrations[0],
139
+ )
140
+
141
+ frequency = antenna.frequency_sections[0]
142
+ repeated_frequency = repeated_antenna.frequency_sections[0]
143
+ _assert_eq_and_ne(
144
+ frequency,
145
+ repeated_frequency,
146
+ frequency_changed.antenna("TESTANT SERIAL-1").frequency_sections[0],
147
+ )
148
+ assert frequency.rms is not None and repeated_frequency.rms is not None
149
+ changed_rms = (
150
+ rms_changed.antenna("TESTANT SERIAL-1").frequency_sections[0].rms
151
+ )
152
+ assert changed_rms is not None
153
+ _assert_eq_and_ne(frequency.rms, repeated_frequency.rms, changed_rms)
154
+ _assert_eq_and_ne(
155
+ frequency.pcv_samples[1],
156
+ repeated_frequency.pcv_samples[1],
157
+ sample_changed.antenna("TESTANT SERIAL-1")
158
+ .frequency_sections[0]
159
+ .pcv_samples[1],
160
+ )
161
+
162
+ _assert_eq_and_ne(
163
+ _invalid_pcv_type("X"),
164
+ _invalid_pcv_type("X"),
165
+ _invalid_pcv_type("Y"),
166
+ )