sidereon 3.0.4__tar.gz → 3.0.6__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 (357) hide show
  1. {sidereon-3.0.4 → sidereon-3.0.6}/.github/workflows/ci.yml +2 -0
  2. {sidereon-3.0.4 → sidereon-3.0.6}/.github/workflows/release.yml +4 -4
  3. {sidereon-3.0.4 → sidereon-3.0.6}/CHANGELOG.md +21 -1
  4. {sidereon-3.0.4 → sidereon-3.0.6}/Cargo.lock +5 -5
  5. {sidereon-3.0.4 → sidereon-3.0.6}/Cargo.toml +3 -3
  6. {sidereon-3.0.4 → sidereon-3.0.6}/PKG-INFO +7 -2
  7. {sidereon-3.0.4 → sidereon-3.0.6}/README.md +6 -1
  8. {sidereon-3.0.4 → sidereon-3.0.6}/THIRD-PARTY-NOTICES.md +3 -3
  9. {sidereon-3.0.4 → sidereon-3.0.6}/docs/public-gnss-distribution-sources.md +5 -5
  10. {sidereon-3.0.4 → sidereon-3.0.6}/docs/sp3.md +25 -5
  11. {sidereon-3.0.4 → sidereon-3.0.6}/pyproject.toml +4 -4
  12. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/__init__.py +17 -0
  13. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/__init__.pyi +75 -7
  14. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/data.py +68 -7
  15. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/data.pyi +10 -0
  16. {sidereon-3.0.4 → sidereon-3.0.6}/scripts/check-release.py +3 -3
  17. {sidereon-3.0.4 → sidereon-3.0.6}/scripts/core_source_identity.rs +1 -1
  18. sidereon-3.0.6/scripts/smoke-release.py +387 -0
  19. {sidereon-3.0.4 → sidereon-3.0.6}/src/ephemeris.rs +229 -6
  20. {sidereon-3.0.4 → sidereon-3.0.6}/src/lnav.rs +7 -7
  21. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/core_goldens.json +2 -2
  22. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/ppp_esbc.json +2 -2
  23. sidereon-3.0.6/tests/fixtures/sp3/accuracy_rounding/A-anchor.sp3 +29 -0
  24. sidereon-3.0.6/tests/fixtures/sp3/accuracy_rounding/A-velocity.sp3 +33 -0
  25. sidereon-3.0.6/tests/fixtures/sp3/accuracy_rounding/A.sp3 +27 -0
  26. sidereon-3.0.6/tests/fixtures/sp3/accuracy_rounding/B-anchor.sp3 +29 -0
  27. sidereon-3.0.6/tests/fixtures/sp3/accuracy_rounding/B-velocity.sp3 +33 -0
  28. sidereon-3.0.6/tests/fixtures/sp3/accuracy_rounding/B.sp3 +27 -0
  29. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_core_revision.py +34 -25
  30. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_data.py +14 -0
  31. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_lnav.py +110 -2
  32. sidereon-3.0.6/tests/test_sp3_accuracy_rounding.py +493 -0
  33. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_merge.py +28 -2
  34. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_write_and_records.py +34 -23
  35. sidereon-3.0.6/third_party_source/sidereon-core-3.0.6/tides/mod.rs +1501 -0
  36. sidereon-3.0.6/third_party_source/sidereon-core-3.0.6/tides/ocean.rs +987 -0
  37. sidereon-3.0.6/third_party_source/sidereon-core-3.0.6/tides/pole.rs +203 -0
  38. {sidereon-3.0.4 → sidereon-3.0.6}/uv.lock +1 -1
  39. sidereon-3.0.4/scripts/smoke-release.py +0 -52
  40. {sidereon-3.0.4 → sidereon-3.0.6}/.cargo/config.toml.disabled +0 -0
  41. {sidereon-3.0.4 → sidereon-3.0.6}/.gitattributes +0 -0
  42. {sidereon-3.0.4 → sidereon-3.0.6}/.gitignore +0 -0
  43. {sidereon-3.0.4 → sidereon-3.0.6}/COVERAGE.md +0 -0
  44. {sidereon-3.0.4 → sidereon-3.0.6}/LICENSE +0 -0
  45. {sidereon-3.0.4 → sidereon-3.0.6}/bench/README.md +0 -0
  46. {sidereon-3.0.4 → sidereon-3.0.6}/bench/bench_fleet.py +0 -0
  47. {sidereon-3.0.4 → sidereon-3.0.6}/docs/antex.md +0 -0
  48. {sidereon-3.0.4 → sidereon-3.0.6}/docs/observations.md +0 -0
  49. {sidereon-3.0.4 → sidereon-3.0.6}/docs/releasing.md +0 -0
  50. {sidereon-3.0.4 → sidereon-3.0.6}/docs/rinex-clock.md +0 -0
  51. {sidereon-3.0.4 → sidereon-3.0.6}/docs/tdm.md +0 -0
  52. {sidereon-3.0.4 → sidereon-3.0.6}/examples/clean_a_phone_track.ipynb +0 -0
  53. {sidereon-3.0.4 → sidereon-3.0.6}/examples/static_occupation_vs_cors.ipynb +0 -0
  54. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/_compression.py +0 -0
  55. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/_exact_cache.py +0 -0
  56. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/_ingress.py +0 -0
  57. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/distribution.py +0 -0
  58. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/distribution.pyi +0 -0
  59. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/exact_cache.py +0 -0
  60. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/exact_cache.pyi +0 -0
  61. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/exact_sp3.py +0 -0
  62. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/exact_sp3.pyi +0 -0
  63. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/ntrip.py +0 -0
  64. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/ntrip.pyi +0 -0
  65. {sidereon-3.0.4 → sidereon-3.0.6}/python/sidereon/py.typed +0 -0
  66. {sidereon-3.0.4 → sidereon-3.0.6}/rust-toolchain.toml +0 -0
  67. {sidereon-3.0.4 → sidereon-3.0.6}/scripts/coverage/bindmap.py +0 -0
  68. {sidereon-3.0.4 → sidereon-3.0.6}/scripts/coverage/pubapi.py +0 -0
  69. {sidereon-3.0.4 → sidereon-3.0.6}/scripts/coverage/render_coverage.py +0 -0
  70. {sidereon-3.0.4 → sidereon-3.0.6}/src/almanac.rs +0 -0
  71. {sidereon-3.0.4 → sidereon-3.0.6}/src/angles.rs +0 -0
  72. {sidereon-3.0.4 → sidereon-3.0.6}/src/anomaly.rs +0 -0
  73. {sidereon-3.0.4 → sidereon-3.0.6}/src/antex.rs +0 -0
  74. {sidereon-3.0.4 → sidereon-3.0.6}/src/araim.rs +0 -0
  75. {sidereon-3.0.4 → sidereon-3.0.6}/src/atmosphere.rs +0 -0
  76. {sidereon-3.0.4 → sidereon-3.0.6}/src/bias.rs +0 -0
  77. {sidereon-3.0.4 → sidereon-3.0.6}/src/bodies.rs +0 -0
  78. {sidereon-3.0.4 → sidereon-3.0.6}/src/body_observe.rs +0 -0
  79. {sidereon-3.0.4 → sidereon-3.0.6}/src/broadcast_comparison.rs +0 -0
  80. {sidereon-3.0.4 → sidereon-3.0.6}/src/cdm.rs +0 -0
  81. {sidereon-3.0.4 → sidereon-3.0.6}/src/clock_stability.rs +0 -0
  82. {sidereon-3.0.4 → sidereon-3.0.6}/src/conjunction.rs +0 -0
  83. {sidereon-3.0.4 → sidereon-3.0.6}/src/constellation.rs +0 -0
  84. {sidereon-3.0.4 → sidereon-3.0.6}/src/core_error_detail.rs +0 -0
  85. {sidereon-3.0.4 → sidereon-3.0.6}/src/covariance.rs +0 -0
  86. {sidereon-3.0.4 → sidereon-3.0.6}/src/coverage.rs +0 -0
  87. {sidereon-3.0.4 → sidereon-3.0.6}/src/data_catalog.rs +0 -0
  88. {sidereon-3.0.4 → sidereon-3.0.6}/src/defaults.rs +0 -0
  89. {sidereon-3.0.4 → sidereon-3.0.6}/src/dgnss.rs +0 -0
  90. {sidereon-3.0.4 → sidereon-3.0.6}/src/doppler.rs +0 -0
  91. {sidereon-3.0.4 → sidereon-3.0.6}/src/elements.rs +0 -0
  92. {sidereon-3.0.4 → sidereon-3.0.6}/src/emission.rs +0 -0
  93. {sidereon-3.0.4 → sidereon-3.0.6}/src/equinoctial.rs +0 -0
  94. {sidereon-3.0.4 → sidereon-3.0.6}/src/error_metrics.rs +0 -0
  95. {sidereon-3.0.4 → sidereon-3.0.6}/src/estimation.rs +0 -0
  96. {sidereon-3.0.4 → sidereon-3.0.6}/src/events.rs +0 -0
  97. {sidereon-3.0.4 → sidereon-3.0.6}/src/exact_cache.rs +0 -0
  98. {sidereon-3.0.4 → sidereon-3.0.6}/src/exact_time.rs +0 -0
  99. {sidereon-3.0.4 → sidereon-3.0.6}/src/fallback.rs +0 -0
  100. {sidereon-3.0.4 → sidereon-3.0.6}/src/forces.rs +0 -0
  101. {sidereon-3.0.4 → sidereon-3.0.6}/src/format_diagnostics.rs +0 -0
  102. {sidereon-3.0.4 → sidereon-3.0.6}/src/frame_catalog.rs +0 -0
  103. {sidereon-3.0.4 → sidereon-3.0.6}/src/frames.rs +0 -0
  104. {sidereon-3.0.4 → sidereon-3.0.6}/src/fusion.rs +0 -0
  105. {sidereon-3.0.4 → sidereon-3.0.6}/src/geodesic.rs +0 -0
  106. {sidereon-3.0.4 → sidereon-3.0.6}/src/geodetic_time_series.rs +0 -0
  107. {sidereon-3.0.4 → sidereon-3.0.6}/src/geofence.rs +0 -0
  108. {sidereon-3.0.4 → sidereon-3.0.6}/src/geoid.rs +0 -0
  109. {sidereon-3.0.4 → sidereon-3.0.6}/src/geometry.rs +0 -0
  110. {sidereon-3.0.4 → sidereon-3.0.6}/src/geometry_quality.rs +0 -0
  111. {sidereon-3.0.4 → sidereon-3.0.6}/src/ils.rs +0 -0
  112. {sidereon-3.0.4 → sidereon-3.0.6}/src/iod.rs +0 -0
  113. {sidereon-3.0.4 → sidereon-3.0.6}/src/ionex.rs +0 -0
  114. {sidereon-3.0.4 → sidereon-3.0.6}/src/lambert.rs +0 -0
  115. {sidereon-3.0.4 → sidereon-3.0.6}/src/leap.rs +0 -0
  116. {sidereon-3.0.4 → sidereon-3.0.6}/src/least_squares.rs +0 -0
  117. {sidereon-3.0.4 → sidereon-3.0.6}/src/lib.rs +0 -0
  118. {sidereon-3.0.4 → sidereon-3.0.6}/src/marshal.rs +0 -0
  119. {sidereon-3.0.4 → sidereon-3.0.6}/src/nmea.rs +0 -0
  120. {sidereon-3.0.4 → sidereon-3.0.6}/src/normality.rs +0 -0
  121. {sidereon-3.0.4 → sidereon-3.0.6}/src/ntrip.rs +0 -0
  122. {sidereon-3.0.4 → sidereon-3.0.6}/src/observables.rs +0 -0
  123. {sidereon-3.0.4 → sidereon-3.0.6}/src/observation.rs +0 -0
  124. {sidereon-3.0.4 → sidereon-3.0.6}/src/oem.rs +0 -0
  125. {sidereon-3.0.4 → sidereon-3.0.6}/src/omm.rs +0 -0
  126. {sidereon-3.0.4 → sidereon-3.0.6}/src/opm.rs +0 -0
  127. {sidereon-3.0.4 → sidereon-3.0.6}/src/orbit_determination.rs +0 -0
  128. {sidereon-3.0.4 → sidereon-3.0.6}/src/ppp.rs +0 -0
  129. {sidereon-3.0.4 → sidereon-3.0.6}/src/ppp_corrections.rs +0 -0
  130. {sidereon-3.0.4 → sidereon-3.0.6}/src/products.rs +0 -0
  131. {sidereon-3.0.4 → sidereon-3.0.6}/src/propagation.rs +0 -0
  132. {sidereon-3.0.4 → sidereon-3.0.6}/src/qc.rs +0 -0
  133. {sidereon-3.0.4 → sidereon-3.0.6}/src/reduced_orbit.rs +0 -0
  134. {sidereon-3.0.4 → sidereon-3.0.6}/src/relative.rs +0 -0
  135. {sidereon-3.0.4 → sidereon-3.0.6}/src/reliability.rs +0 -0
  136. {sidereon-3.0.4 → sidereon-3.0.6}/src/rf.rs +0 -0
  137. {sidereon-3.0.4 → sidereon-3.0.6}/src/rinex.rs +0 -0
  138. {sidereon-3.0.4 → sidereon-3.0.6}/src/rinex_clock.rs +0 -0
  139. {sidereon-3.0.4 → sidereon-3.0.6}/src/rtcm.rs +0 -0
  140. {sidereon-3.0.4 → sidereon-3.0.6}/src/rtk.rs +0 -0
  141. {sidereon-3.0.4 → sidereon-3.0.6}/src/sbas_pl.rs +0 -0
  142. {sidereon-3.0.4 → sidereon-3.0.6}/src/sbas_ssr.rs +0 -0
  143. {sidereon-3.0.4 → sidereon-3.0.6}/src/scenario.rs +0 -0
  144. {sidereon-3.0.4 → sidereon-3.0.6}/src/sidereal.rs +0 -0
  145. {sidereon-3.0.4 → sidereon-3.0.6}/src/signal_analysis.rs +0 -0
  146. {sidereon-3.0.4 → sidereon-3.0.6}/src/sky.rs +0 -0
  147. {sidereon-3.0.4 → sidereon-3.0.6}/src/solve_error_detail.rs +0 -0
  148. {sidereon-3.0.4 → sidereon-3.0.6}/src/source_localization.rs +0 -0
  149. {sidereon-3.0.4 → sidereon-3.0.6}/src/space_weather.rs +0 -0
  150. {sidereon-3.0.4 → sidereon-3.0.6}/src/spk.rs +0 -0
  151. {sidereon-3.0.4 → sidereon-3.0.6}/src/spp.rs +0 -0
  152. {sidereon-3.0.4 → sidereon-3.0.6}/src/spp_error_detail.rs +0 -0
  153. {sidereon-3.0.4 → sidereon-3.0.6}/src/staleness.rs +0 -0
  154. {sidereon-3.0.4 → sidereon-3.0.6}/src/static_positioning.rs +0 -0
  155. {sidereon-3.0.4 → sidereon-3.0.6}/src/tca.rs +0 -0
  156. {sidereon-3.0.4 → sidereon-3.0.6}/src/tdm.rs +0 -0
  157. {sidereon-3.0.4 → sidereon-3.0.6}/src/terrain.rs +0 -0
  158. {sidereon-3.0.4 → sidereon-3.0.6}/src/terrain_store.rs +0 -0
  159. {sidereon-3.0.4 → sidereon-3.0.6}/src/tides.rs +0 -0
  160. {sidereon-3.0.4 → sidereon-3.0.6}/src/time_model_error.rs +0 -0
  161. {sidereon-3.0.4 → sidereon-3.0.6}/src/tle_fit_error_detail.rs +0 -0
  162. {sidereon-3.0.4 → sidereon-3.0.6}/src/tropo.rs +0 -0
  163. {sidereon-3.0.4 → sidereon-3.0.6}/src/unix_compress.rs +0 -0
  164. {sidereon-3.0.4 → sidereon-3.0.6}/tests/_helpers.py +0 -0
  165. {sidereon-3.0.4 → sidereon-3.0.6}/tests/_sp3_merge.py +0 -0
  166. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/README.md +0 -0
  167. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/antex/igs20_pasa_scoa_gps.atx +0 -0
  168. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/antex/igs20_wettzell_trim.atx +0 -0
  169. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/batch_fleet.json +0 -0
  170. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/bodies/observe_de.bsp +0 -0
  171. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/cdm.json +0 -0
  172. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/clk/lossless/rinex_clock304_table_a17.clk +0 -0
  173. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/clk/lossless/rinex_clock304_table_a18.clk +0 -0
  174. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/clk/synthetic_rinex_clock.clk +0 -0
  175. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/conjunction.json +0 -0
  176. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/constellation/gps_ops_sample.json +0 -0
  177. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/constellation/navcen_forecast_cases.html +0 -0
  178. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/constellation/navcen_gps_sample.html +0 -0
  179. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/events_bodies_dop.json +0 -0
  180. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/format_diagnostics_inputs.json +0 -0
  181. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/frames_time.json +0 -0
  182. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/geodesic/geodtest_row.dat +0 -0
  183. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/geoid/egm2008_25_norcal_crop.bin +0 -0
  184. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/gnss_data/igs22376.sp3.Z.b64 +0 -0
  185. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/gnss_data/ncompress-current-b9-counter.Z.b64 +0 -0
  186. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/listings/aiub-iono-prd-20260923.csv +0 -0
  187. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/listings/gfz-ultra-w2430-20260804.html +0 -0
  188. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/nav/BRDC00GOP_R_20210010000_01D_MN.rnx +0 -0
  189. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/nav/ESBC00DNK_R_20201770000_01D_MN.rnx +0 -0
  190. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/nav/KMS300DNK_R_20221591000_01H_MN.rnx +0 -0
  191. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/numerical_propagation.json +0 -0
  192. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/obs/ESBC00DNK_R_20201770000_01D_30S_MO_trim.crx +0 -0
  193. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/obs/ESBC00DNK_R_20201770000_01D_30S_MO_trim.rnx +0 -0
  194. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/obs/algo0010_2015001_v1_trim.crx +0 -0
  195. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/obs/algo0010_2015001_v1_trim.rnx +0 -0
  196. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/omm.json +0 -0
  197. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/pass_finder.json +0 -0
  198. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/rf_link_budget.json +0 -0
  199. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/rtk_wtzr.json +0 -0
  200. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/scenario_base.json +0 -0
  201. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/sgp4_topocentric.json +0 -0
  202. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/sp3/IGS0OPSFIN_20261200945_02H30M_15M_ORB.SP3 +0 -0
  203. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/sp3-merge-input-v1.json +0 -0
  204. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/sp3-merge-report-v3.json +0 -0
  205. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/sp3_bodies.json +0 -0
  206. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/spk/horizons_eros_type21.bsp +0 -0
  207. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/tdm/annex_e_01.kvn +0 -0
  208. {sidereon-3.0.4 → sidereon-3.0.6}/tests/fixtures/tle_roundtrip.json +0 -0
  209. {sidereon-3.0.4 → sidereon-3.0.6}/tests/golden/sp3-terminal-record-v1.json +0 -0
  210. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_012_bindings.py +0 -0
  211. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_013_bindings.py +0 -0
  212. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_014_bindings.py +0 -0
  213. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_015_bindings.py +0 -0
  214. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_016_wave2_bindings.py +0 -0
  215. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_017_domain_exposure.py +0 -0
  216. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_018_domain_exposure.py +0 -0
  217. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_019_track.py +0 -0
  218. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_020_error_metrics.py +0 -0
  219. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_antex.py +0 -0
  220. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_antex_public_equality.py +0 -0
  221. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_antex_public_pcv_sample_fields.py +0 -0
  222. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_atmosphere.py +0 -0
  223. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_attested_open.py +0 -0
  224. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_batch.py +0 -0
  225. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_body_observe_reference.py +0 -0
  226. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_broadcast_comparison.py +0 -0
  227. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_broadcast_fallback.py +0 -0
  228. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_cdm.py +0 -0
  229. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_cdm_top_level.py +0 -0
  230. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_cdm_value_equality.py +0 -0
  231. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_codec_error_details.py +0 -0
  232. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_conjunction.py +0 -0
  233. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_constellation.py +0 -0
  234. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_constellation_fleet.py +0 -0
  235. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_core_defaults.py +0 -0
  236. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_core_public_api_parity.py +0 -0
  237. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_core_round3_surfaces.py +0 -0
  238. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_covariance.py +0 -0
  239. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_covariance_propagation.py +0 -0
  240. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_coverage.py +0 -0
  241. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_crinex.py +0 -0
  242. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_data_publication_resilience.py +0 -0
  243. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_dgnss.py +0 -0
  244. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_distribution.py +0 -0
  245. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_distribution_fallback.py +0 -0
  246. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_dop_at_epoch.py +0 -0
  247. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_dop_convention.py +0 -0
  248. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_doppler.py +0 -0
  249. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_dted_tile_error_details.py +0 -0
  250. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_egm96.py +0 -0
  251. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_elements.py +0 -0
  252. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_events_bodies_dop.py +0 -0
  253. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_exact_cache_atomicity.py +0 -0
  254. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_field_mode_bindings.py +0 -0
  255. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_forces.py +0 -0
  256. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_format_diagnostics.py +0 -0
  257. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_frames_time.py +0 -0
  258. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ftp_transport.py +0 -0
  259. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_fusion_error_details.py +0 -0
  260. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_geoid.py +0 -0
  261. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_geometry.py +0 -0
  262. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ils.py +0 -0
  263. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_import.py +0 -0
  264. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_inertial_mechanization.py +0 -0
  265. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ionex.py +0 -0
  266. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ionex_instant_queries.py +0 -0
  267. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ionex_policies.py +0 -0
  268. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ionex_public_equality.py +0 -0
  269. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ionosphere_nequick.py +0 -0
  270. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_klobuchar_native.py +0 -0
  271. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_lambert_iod.py +0 -0
  272. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_leap_seconds.py +0 -0
  273. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_least_squares.py +0 -0
  274. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_misc_public_equality.py +0 -0
  275. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_moving_baseline.py +0 -0
  276. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_new_core_api.py +0 -0
  277. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_next_issue_due.py +0 -0
  278. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_nmea_finish_output.py +0 -0
  279. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ntrip.py +0 -0
  280. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_numerical.py +0 -0
  281. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_obs_policies.py +0 -0
  282. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_observables.py +0 -0
  283. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_observables_broadcast.py +0 -0
  284. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_observables_error_details.py +0 -0
  285. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_observation.py +0 -0
  286. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_observation_qc.py +0 -0
  287. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_observe_batch.py +0 -0
  288. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_oem.py +0 -0
  289. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_oem_full_dto.py +0 -0
  290. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_omm.py +0 -0
  291. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_omm_full_dto.py +0 -0
  292. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_opm.py +0 -0
  293. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_opm_full_dto.py +0 -0
  294. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_parity_gaps.py +0 -0
  295. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_parse_tle_file.py +0 -0
  296. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_passes.py +0 -0
  297. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ppp.py +0 -0
  298. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ppp_corrections.py +0 -0
  299. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_ppp_range_corrections.py +0 -0
  300. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_precise_interpolant_public_values.py +0 -0
  301. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_precise_samples.py +0 -0
  302. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_precise_samples_errors.py +0 -0
  303. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_propagation.py +0 -0
  304. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_qc.py +0 -0
  305. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_reduced_orbit.py +0 -0
  306. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_reduced_orbit_piecewise.py +0 -0
  307. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_release_guard.py +0 -0
  308. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_residual_stats.py +0 -0
  309. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rf.py +0 -0
  310. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rinex_clock.py +0 -0
  311. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rinex_clock_equality.py +0 -0
  312. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rinex_clock_header_field_details.py +0 -0
  313. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rinex_clock_lossless.py +0 -0
  314. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rinex_nav.py +0 -0
  315. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rinex_obs.py +0 -0
  316. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rtcm.py +0 -0
  317. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rtcm_departure_message_number_contract.py +0 -0
  318. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rtk.py +0 -0
  319. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_rtk_unresolved_carriers.py +0 -0
  320. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sbas_decoded_lists.py +0 -0
  321. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sbas_unassigned_mask.py +0 -0
  322. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_scenario_external_sources.py +0 -0
  323. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sky.py +0 -0
  324. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_bodies.py +0 -0
  325. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_catalog_exact.py +0 -0
  326. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_clock_reference.py +0 -0
  327. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_interpolation_policy.py +0 -0
  328. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_merge_coverage.py +0 -0
  329. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_merge_per_epoch_provenance.py +0 -0
  330. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_public_metadata.py +0 -0
  331. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_terminal_record.py +0 -0
  332. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_sp3_window_continuity.py +0 -0
  333. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_spk.py +0 -0
  334. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_spp.py +0 -0
  335. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_spp_error_details.py +0 -0
  336. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_spp_glonass.py +0 -0
  337. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_spp_robustness.py +0 -0
  338. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_staleness.py +0 -0
  339. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_stub_accuracy.py +0 -0
  340. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_tca.py +0 -0
  341. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_tdm_policies.py +0 -0
  342. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_terrain_unknown_elevation.py +0 -0
  343. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_tides.py +0 -0
  344. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_time_model_error_details.py +0 -0
  345. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_time_offset_error_details.py +0 -0
  346. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_tle_fit.py +0 -0
  347. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_tle_roundtrip.py +0 -0
  348. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_tropo.py +0 -0
  349. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_unix_compress.py +0 -0
  350. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_visible_ground_track.py +0 -0
  351. {sidereon-3.0.4 → sidereon-3.0.6}/tests/test_weather_terrain_error_details.py +0 -0
  352. {sidereon-3.0.4 → sidereon-3.0.6}/third_party_licenses/ERFA-BSD-3-Clause.txt +0 -0
  353. {sidereon-3.0.4 → sidereon-3.0.6}/third_party_licenses/IERS-CONVENTIONS-SOFTWARE-LICENSE.txt +0 -0
  354. {sidereon-3.0.4 → sidereon-3.0.6}/third_party_licenses/SciPy-BSD-3-Clause.txt +0 -0
  355. {sidereon-3.0.4 → sidereon-3.0.6}/third_party_source/sidereon-core-3.0.4/tides/mod.rs +0 -0
  356. {sidereon-3.0.4 → sidereon-3.0.6}/third_party_source/sidereon-core-3.0.4/tides/ocean.rs +0 -0
  357. {sidereon-3.0.4 → sidereon-3.0.6}/third_party_source/sidereon-core-3.0.4/tides/pole.rs +0 -0
@@ -169,6 +169,8 @@ jobs:
169
169
  run: uv run --locked --extra test --group dev mypy
170
170
  - name: Build the extension (maturin develop)
171
171
  run: uv run --locked --extra test --group dev maturin develop --locked --features exact-cache-test-failpoints
172
+ - name: Release consumer contract
173
+ run: uv run --locked --extra test --group dev python scripts/smoke-release.py
172
174
  - name: stub accuracy
173
175
  env:
174
176
  SIDEREON_CORE_FIXTURES: ${{ github.workspace }}/.core-checkout/crates/sidereon-core/tests/fixtures
@@ -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.4/tides/$source" \
156
+ "third_party_source/sidereon-core-3.0.6/tides/$source" \
157
157
  >"$RUNNER_TEMP/$source"
158
158
  cmp "$RUNNER_TEMP/$source" \
159
- "third_party_source/sidereon-core-3.0.4/tides/$source"
159
+ "third_party_source/sidereon-core-3.0.6/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.4/tides/$source" \
176
+ "*/third_party_source/sidereon-core-3.0.6/tides/$source" \
177
177
  >"$RUNNER_TEMP/sdist-$source"
178
178
  cmp "$RUNNER_TEMP/sdist-$source" \
179
- "third_party_source/sidereon-core-3.0.4/tides/$source"
179
+ "third_party_source/sidereon-core-3.0.6/tides/$source"
180
180
  done
181
181
  - name: Smoke-test installed x86_64 Linux wheel
182
182
  shell: bash
@@ -2,7 +2,27 @@
2
2
 
3
3
  All notable changes to the Sidereon Python interface are documented here.
4
4
 
5
- ## [Unreleased]
5
+ ## [3.0.6] - 2026-10-30
6
+
7
+ ### Added
8
+
9
+ - Adds an explicit conservative accuracy-sigma rounding policy alongside the
10
+ existing record-value writer, with detailed SI change reports and an atomic
11
+ file writer.
12
+
13
+ ## [3.0.5] - 2026-10-30
14
+
15
+ ### Added
16
+
17
+ - Add opt-in SP3 column rounding writers that report maximum absolute SI changes; strict writers remain unchanged.
18
+ - Preserve strictly writable SP3 columns for grid-aligned merge and alignment products.
19
+
20
+ ### Fixed
21
+
22
+ - Normalize extreme-scale quaternions robustly while preserving ordinary quaternion rounding.
23
+ - Reject zero probability values where probability inputs must be strictly between zero and one.
24
+ - Describe the seven raw LNAV broadcast angular fields as semicircles or
25
+ semicircles/second, matching their unchanged transmitted-integer scaling.
6
26
 
7
27
  ## [3.0.4] - 2026-10-18
8
28
 
@@ -611,9 +611,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
611
611
 
612
612
  [[package]]
613
613
  name = "sidereon"
614
- version = "3.0.4"
614
+ version = "3.0.6"
615
615
  source = "registry+https://github.com/rust-lang/crates.io-index"
616
- checksum = "c042d74f42d0e2a9f8b2645109fd06f407e42ecab964fb91c492b18a2c034096"
616
+ checksum = "6cd7daa65eef0d76f212f7e68a7f9d9ec201fe6864e0a8b247d9eef1cc388832"
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.4"
625
+ version = "3.0.6"
626
626
  source = "registry+https://github.com/rust-lang/crates.io-index"
627
- checksum = "3634af272103625414a0e63bacbf2b76e181c33f8652690823acdfa0e25c938e"
627
+ checksum = "70cea8ad60c539244039dd513857a9c18bcca8e54840e091df3d31db4dad0e99"
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.4"
649
+ version = "3.0.6"
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.4"
3
+ version = "3.0.6"
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,8 +25,8 @@ 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 = "=3.0.4"
29
- sidereon-core = { version = "=3.0.4", features = ["mmap"] }
28
+ sidereon = { version = "=3.0.6" }
29
+ sidereon-core = { version = "=3.0.6", 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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sidereon
3
- Version: 3.0.4
3
+ Version: 3.0.6
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: Topic :: Scientific/Engineering
@@ -280,7 +280,12 @@ The Python package mirrors the full breadth of the engine.
280
280
  defines, including exact validity seconds (see `docs/antex.md`).
281
281
  - **Data acquisition:** the `sidereon.data` module downloads and caches GNSS
282
282
  products (SP3 and IONEX from IGS/MGEX analysis centers, including merged
283
- multi-center SP3) and DTED terrain tiles. Exact-product requests can select
283
+ multi-center SP3) and DTED terrain tiles. Merges from column-grid inputs
284
+ retain SP3-writable records; higher-precision inputs can still need the
285
+ opt-in record rounding writer. The accuracy-aware writer requires an
286
+ explicit conservative policy when encoded standard deviations need column
287
+ adjustment, and reports each maximum absolute SI increase. Agreement metrics use combined values before final
288
+ column quantization. Exact-product requests can select
284
289
  direct archives, NASA CDDIS/Earthdata, local files, or caller-provided bytes
285
290
  without changing product identity. `ExactSp3Request` validates the declared
286
291
  cadence, regular epoch grid, exact half-open or inclusive coverage, producer,
@@ -261,7 +261,12 @@ The Python package mirrors the full breadth of the engine.
261
261
  defines, including exact validity seconds (see `docs/antex.md`).
262
262
  - **Data acquisition:** the `sidereon.data` module downloads and caches GNSS
263
263
  products (SP3 and IONEX from IGS/MGEX analysis centers, including merged
264
- multi-center SP3) and DTED terrain tiles. Exact-product requests can select
264
+ multi-center SP3) and DTED terrain tiles. Merges from column-grid inputs
265
+ retain SP3-writable records; higher-precision inputs can still need the
266
+ opt-in record rounding writer. The accuracy-aware writer requires an
267
+ explicit conservative policy when encoded standard deviations need column
268
+ adjustment, and reports each maximum absolute SI increase. Agreement metrics use combined values before final
269
+ column quantization. Exact-product requests can select
265
270
  direct archives, NASA CDDIS/Earthdata, local files, or caller-provided bytes
266
271
  without changing product identity. `ExactSp3Request` validates the declared
267
272
  cadence, regular epoch grid, exact half-open or inclusive coverage, producer,
@@ -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.4/tides/` in wheels and source
334
- distributions. These files match sidereon-core 3.0.4 at commit 5cfcdf929b57a5998e304df786735b171ba8afaf.
335
- Source: [sidereon-core tides at the pinned commit](https://github.com/neilberkman/sidereon/tree/5cfcdf929b57a5998e304df786735b171ba8afaf/crates/sidereon-core/src/tides).
333
+ `third_party_source/sidereon-core-3.0.6/tides/` in wheels and source
334
+ distributions. These files match sidereon-core 3.0.6 at commit ae9ce1a0f532c90fccb33cd6922b2188828ddb42.
335
+ Source: [sidereon-core tides at the pinned commit](https://github.com/neilberkman/sidereon/tree/ae9ce1a0f532c90fccb33cd6922b2188828ddb42/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>
@@ -278,11 +278,11 @@ except sidereon.Sp3WriteError as error:
278
278
  assert data.verify_merge_report(json.loads(persisted_report_json))
279
279
  ```
280
280
 
281
- The file helpers write SP3 text only when every value restates exactly. The
282
- default mean merge of two centers holds positions finer than the millimetre
283
- record column wherever the centers differ, and a clock moved onto the reference
284
- clock datum can be finer than its column; `Sp3WriteError` names the value
285
- instead of the helper rounding it.
281
+ The merge path preserves grid-in/grid-out for record values derived from
282
+ on-grid inputs, including means between column values. The file helpers write
283
+ those merged products directly. Other products can still hold higher-precision
284
+ values than the record columns; the strict writer names those with
285
+ `Sp3WriteError`, while the explicit rounding writer reports its SI changes.
286
286
 
287
287
  `report.to_dict()` writes merged-SP3 report schema 3. A merge keeps a
288
288
  satellite whose orbit is missing but whose clock reached a consensus as a
@@ -25,6 +25,8 @@ parsing, rounding or repair of its own.
25
25
  | `clock_record(satellite, epoch_index)` | `Sp3ClockRecord`: a clock beside a missing orbit |
26
26
  | `clock_records_at(epoch_index)` | dict from satellite token to `Sp3ClockRecord` at one epoch, ascending |
27
27
  | `to_sp3_string()` | the SP3 text, or `Sp3WriteError` |
28
+ | `to_sp3_string_with_rounding()` | `(text, Sp3RoundingReport)`, or `Sp3WriteError` |
29
+ | `to_sp3_string_with_accuracy_rounding(policy)` | `(text, Sp3DetailedRoundingReport)`, or `Sp3WriteError` |
28
30
 
29
31
  `state` and `clock_record` raise `IndexError` for an epoch index past the end
30
32
  and `KeyError` when the satellite has no such record at that epoch.
@@ -86,11 +88,29 @@ back unchanged; it can still be refused on a header base finer than its field,
86
88
  on header text wider than its columns, or on velocity records under a `P`
87
89
  header.
88
90
 
89
- A mean or median merge usually holds positions finer than the millimetre
90
- `F14.6` column, and a clock shifted onto the reference clock datum can hold a
91
- clock finer than its column, so writing such a merge raises
92
- `RecordValueNotRepresentable` naming the value. `data.write_sp3` and
93
- `data.fetch_merged_sp3_file` write nothing in that case.
91
+ `to_sp3_string_with_rounding()` is an explicit alternative for products whose
92
+ record values need the precision of their SP3 columns. It returns
93
+ `(text, Sp3RoundingReport)`. The immutable report gives maximum absolute changes
94
+ per field in SI units (`position_*_m`, `velocity_*_m_s`, `clock_s`, and
95
+ `clock_rate_s_s`). It applies no rounding to header fields or epochs; refusals
96
+ there still raise `Sp3WriteError`. `data.write_sp3_with_rounding()` writes the
97
+ text atomically and returns `(path, report)`.
98
+
99
+ For products whose encoded accuracy sigmas need a conservative increase under
100
+ the selected output bases,
101
+ `to_sp3_string_with_accuracy_rounding(Sp3AccuracyRoundingPolicy.CONSERVATIVE)`
102
+ requires an explicit policy and returns a nested report with `record_values`
103
+ and `accuracy_sigmas`. Conservative rounding never lowers a finite standard
104
+ deviation; the latter report gives the maximum absolute SI increase per field.
105
+ Unspecified accuracy remains unspecified. `data.write_sp3_with_accuracy_rounding()`
106
+ uses the same policy and atomic file replacement, and returns `(path, report)`.
107
+
108
+ The merge path preserves grid-in/grid-out for record values derived from
109
+ on-grid inputs, including means between column values. Agreement and dispersion
110
+ metrics are computed from combined values before the final column quantization;
111
+ their arithmetic is unchanged. Other products can still hold record values
112
+ finer than their columns; the strict writer refuses those, and the explicit
113
+ rounding writer can state them while reporting the maximum SI change.
94
114
 
95
115
  ### Sp3WriteError
96
116
 
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "sidereon"
7
- version = "3.0.4"
7
+ version = "3.0.6"
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.4/tides/mod.rs",
86
- "third_party_source/sidereon-core-3.0.4/tides/ocean.rs",
87
- "third_party_source/sidereon-core-3.0.4/tides/pole.rs",
85
+ "third_party_source/sidereon-core-3.0.6/tides/mod.rs",
86
+ "third_party_source/sidereon-core-3.0.6/tides/ocean.rs",
87
+ "third_party_source/sidereon-core-3.0.6/tides/pole.rs",
88
88
  ]
@@ -69,6 +69,7 @@ value, and :class:`SolveError` for a solve or propagation failure. Invalid
69
69
  arguments raise the built-in :class:`TypeError` / :class:`ValueError`.
70
70
  """
71
71
 
72
+ from enum import Enum
72
73
  import warnings
73
74
 
74
75
  from ._sidereon import (
@@ -118,6 +119,9 @@ from ._sidereon import (
118
119
  Sp3PositionClockAccuracy,
119
120
  Sp3VelocityAccuracy,
120
121
  Sp3RecordAccuracy,
122
+ Sp3RoundingReport,
123
+ Sp3AccuracyRoundingReport,
124
+ Sp3DetailedRoundingReport,
121
125
  Sp3Interpolation,
122
126
  Sp3ClockReferenceOffset,
123
127
  Sp3State,
@@ -1437,6 +1441,8 @@ from ._sidereon import (
1437
1441
  signal_dll_lower_bound,
1438
1442
  signal_multipath_error_envelope,
1439
1443
  )
1444
+
1445
+
1440
1446
  from .exact_sp3 import (
1441
1447
  ExactSp3Coverage,
1442
1448
  ExactSp3Request,
@@ -1641,6 +1647,13 @@ from ._sidereon import (
1641
1647
  SsrRtcmIngest,
1642
1648
  )
1643
1649
 
1650
+
1651
+ class Sp3AccuracyRoundingPolicy(str, Enum):
1652
+ """Required policy for rounding decoded SP3 accuracy standard deviations."""
1653
+
1654
+ CONSERVATIVE = "conservative"
1655
+
1656
+
1644
1657
  OCEAN_LOADING_CONSTITUENTS = tuple(ocean_loading_constituents())
1645
1658
 
1646
1659
  __all__ = [
@@ -1689,6 +1702,10 @@ __all__ = [
1689
1702
  "Sp3PositionClockAccuracy",
1690
1703
  "Sp3VelocityAccuracy",
1691
1704
  "Sp3RecordAccuracy",
1705
+ "Sp3AccuracyRoundingPolicy",
1706
+ "Sp3RoundingReport",
1707
+ "Sp3AccuracyRoundingReport",
1708
+ "Sp3DetailedRoundingReport",
1692
1709
  "Sp3Interpolation",
1693
1710
  "Sp3ClockReferenceOffset",
1694
1711
  "Sp3State",
@@ -509,6 +509,12 @@ class Sp3:
509
509
  def to_sp3_string(self) -> str:
510
510
  """Serialize to SP3 text; raises `Sp3WriteError` rather than change a
511
511
  value to fit its columns."""
512
+ def to_sp3_string_with_rounding(self) -> tuple[str, Sp3RoundingReport]:
513
+ """Write rounded record values and return their maximum SI deltas."""
514
+ def to_sp3_string_with_accuracy_rounding(
515
+ self, policy: Sp3AccuracyRoundingPolicy
516
+ ) -> tuple[str, Sp3DetailedRoundingReport]:
517
+ """Round record values and conservatively raise encoded accuracy sigmas."""
512
518
  def precise_ephemeris_samples(self) -> list[PreciseEphemerisSample]: ...
513
519
  def precise_ephemeris_accuracy_samples(
514
520
  self,
@@ -518,6 +524,54 @@ class Sp3:
518
524
  ) -> bytes: ...
519
525
  def __repr__(self) -> str: ...
520
526
 
527
+ class Sp3RoundingReport:
528
+ """Immutable maximum absolute SI changes made by the SP3 rounding writer."""
529
+ @property
530
+ def position_x_m(self) -> float: ...
531
+ @property
532
+ def position_y_m(self) -> float: ...
533
+ @property
534
+ def position_z_m(self) -> float: ...
535
+ @property
536
+ def velocity_x_m_s(self) -> float: ...
537
+ @property
538
+ def velocity_y_m_s(self) -> float: ...
539
+ @property
540
+ def velocity_z_m_s(self) -> float: ...
541
+ @property
542
+ def clock_s(self) -> float: ...
543
+ @property
544
+ def clock_rate_s_s(self) -> float: ...
545
+ def __repr__(self) -> str: ...
546
+
547
+ class Sp3AccuracyRoundingPolicy(str, enum.Enum):
548
+ CONSERVATIVE = "conservative"
549
+
550
+ class Sp3AccuracyRoundingReport:
551
+ """Maximum absolute SI changes to the decoded accuracy standard deviations."""
552
+ @property
553
+ def position_sigma_x_m(self) -> float: ...
554
+ @property
555
+ def position_sigma_y_m(self) -> float: ...
556
+ @property
557
+ def position_sigma_z_m(self) -> float: ...
558
+ @property
559
+ def velocity_sigma_x_m_s(self) -> float: ...
560
+ @property
561
+ def velocity_sigma_y_m_s(self) -> float: ...
562
+ @property
563
+ def velocity_sigma_z_m_s(self) -> float: ...
564
+ @property
565
+ def clock_sigma_m(self) -> float: ...
566
+ @property
567
+ def clock_rate_sigma_m_s(self) -> float: ...
568
+
569
+ class Sp3DetailedRoundingReport:
570
+ @property
571
+ def record_values(self) -> Sp3RoundingReport: ...
572
+ @property
573
+ def accuracy_sigmas(self) -> Sp3AccuracyRoundingReport: ...
574
+
521
575
  class ExactSp3ValidationError(ValueError):
522
576
  kind: str | None
523
577
  detail: dict[str, object] | None
@@ -13177,9 +13231,13 @@ class LnavDecoded:
13177
13231
  @property
13178
13232
  def crs(self) -> float: ...
13179
13233
  @property
13180
- def delta_n(self) -> float: ...
13234
+ def delta_n(self) -> float:
13235
+ """Mean motion difference, semicircles/second."""
13236
+ ...
13181
13237
  @property
13182
- def m0(self) -> float: ...
13238
+ def m0(self) -> float:
13239
+ """Mean anomaly at reference time, semicircles."""
13240
+ ...
13183
13241
  @property
13184
13242
  def cuc(self) -> float: ...
13185
13243
  @property
@@ -13197,19 +13255,29 @@ class LnavDecoded:
13197
13255
  @property
13198
13256
  def cic(self) -> float: ...
13199
13257
  @property
13200
- def omega0(self) -> float: ...
13258
+ def omega0(self) -> float:
13259
+ """Longitude of ascending node at reference time, semicircles."""
13260
+ ...
13201
13261
  @property
13202
13262
  def cis(self) -> float: ...
13203
13263
  @property
13204
- def i0(self) -> float: ...
13264
+ def i0(self) -> float:
13265
+ """Inclination at reference time, semicircles."""
13266
+ ...
13205
13267
  @property
13206
13268
  def crc(self) -> float: ...
13207
13269
  @property
13208
- def omega(self) -> float: ...
13270
+ def omega(self) -> float:
13271
+ """Argument of perigee, semicircles."""
13272
+ ...
13209
13273
  @property
13210
- def omega_dot(self) -> float: ...
13274
+ def omega_dot(self) -> float:
13275
+ """Rate of right ascension, semicircles/second."""
13276
+ ...
13211
13277
  @property
13212
- def idot(self) -> float: ...
13278
+ def idot(self) -> float:
13279
+ """Rate of inclination, semicircles/second."""
13280
+ ...
13213
13281
  def __repr__(self) -> str: ...
13214
13282
 
13215
13283
  def lnav_tow(bits: Sequence[int] | bytes) -> int | None: ...
@@ -284,6 +284,8 @@ __all__ = [
284
284
  "sp3_merge_input_identity",
285
285
  "verify_merge_report",
286
286
  "write_sp3",
287
+ "write_sp3_with_rounding",
288
+ "write_sp3_with_accuracy_rounding",
287
289
  "DistributionSource",
288
290
  "Distribution",
289
291
  "ProductIdentity",
@@ -5872,9 +5874,13 @@ def write_sp3(sp3: "sidereon.Sp3", path: str, *, gzip: bool = False) -> str:
5872
5874
 
5873
5875
  Pass ``gzip=True`` to gzip-compress the output (pair it with a ``.gz``
5874
5876
  extension on ``path``). Returns the written path. Raises
5875
- :class:`sidereon.Sp3WriteError` before anything is written when the
5876
- product holds a value SP3 text cannot state, such as a mean-merged position
5877
- finer than a millimetre; ``detail`` names the value.
5877
+ :class:`sidereon.Sp3WriteError` before anything is written when a product
5878
+ holds record values finer than SP3 columns; ``detail`` names the value.
5879
+ Merges from column-grid inputs keep records on that grid, including means.
5880
+ Higher-precision records can still require :func:`write_sp3_with_rounding`;
5881
+ products whose accuracy sigmas need conservative increases under their
5882
+ output bases
5883
+ can use :func:`write_sp3_with_accuracy_rounding`.
5878
5884
  """
5879
5885
  data = sp3.to_sp3_string().encode("ascii")
5880
5886
  if gzip:
@@ -5890,6 +5896,58 @@ def write_sp3(sp3: "sidereon.Sp3", path: str, *, gzip: bool = False) -> str:
5890
5896
  return path
5891
5897
 
5892
5898
 
5899
+ def write_sp3_with_rounding(
5900
+ sp3: "sidereon.Sp3", path: str, *, gzip: bool = False
5901
+ ) -> tuple[str, "sidereon.Sp3RoundingReport"]:
5902
+ """Write rounded SP3 record values atomically and return ``(path, report)``.
5903
+
5904
+ Header fields and epochs retain the exact checks of the strict writer. The
5905
+ immutable report gives maximum absolute changes in SI units for each field.
5906
+ """
5907
+ text, report = sp3.to_sp3_string_with_rounding()
5908
+ data = text.encode("ascii")
5909
+ if gzip:
5910
+ data = _gzip.compress(data)
5911
+ directory = _os.path.dirname(_os.path.abspath(path))
5912
+ _ensure_dir(directory)
5913
+ tmp = _write_temp(directory, data)
5914
+ try:
5915
+ _os.replace(tmp, path)
5916
+ except OSError as exc:
5917
+ _silent_remove(tmp)
5918
+ raise CacheNotWritable(f"cannot write {path}: {exc}") from exc
5919
+ return path, report
5920
+
5921
+
5922
+ def write_sp3_with_accuracy_rounding(
5923
+ sp3: "sidereon.Sp3",
5924
+ path: str,
5925
+ *,
5926
+ policy: "sidereon.Sp3AccuracyRoundingPolicy",
5927
+ gzip: bool = False,
5928
+ ) -> tuple[str, "sidereon.Sp3DetailedRoundingReport"]:
5929
+ """Write with the required accuracy-sigma policy, atomically.
5930
+
5931
+ The conservative policy rounds record values and raises encoded finite
5932
+ accuracy standard deviations only as much as the SP3 columns require.
5933
+ Returns ``(path, detailed_report)``. No file is replaced if serialization
5934
+ fails.
5935
+ """
5936
+ text, report = sp3.to_sp3_string_with_accuracy_rounding(policy)
5937
+ data = text.encode("ascii")
5938
+ if gzip:
5939
+ data = _gzip.compress(data)
5940
+ directory = _os.path.dirname(_os.path.abspath(path))
5941
+ _ensure_dir(directory)
5942
+ tmp = _write_temp(directory, data)
5943
+ try:
5944
+ _os.replace(tmp, path)
5945
+ except OSError as exc:
5946
+ _silent_remove(tmp)
5947
+ raise CacheNotWritable(f"cannot write {path}: {exc}") from exc
5948
+ return path, report
5949
+
5950
+
5893
5951
  def fetch_merged_sp3_file(
5894
5952
  target: Union[_dt.date, _dt.datetime],
5895
5953
  centers: Sequence[str],
@@ -5911,10 +5969,13 @@ def fetch_merged_sp3_file(
5911
5969
  path by default. Pass ``return_report=True`` to receive ``(path, report)``
5912
5970
  and retain the exact contributor provenance and stable input identity.
5913
5971
  Nothing is written if the fetch/merge step raises, or if the merged product
5914
- cannot be written as SP3 text (:class:`sidereon.Sp3WriteError`). A mean or
5915
- median of positions that differ below the column resolution, or a clock
5916
- shifted onto the reference clock datum, can be finer than its SP3 column;
5917
- ``detail`` on the exception names the value.
5972
+ cannot be written as SP3 text (:class:`sidereon.Sp3WriteError`). When
5973
+ contributors are on the SP3 column grid, merge results stay on that grid,
5974
+ including means, medians and clock-datum adjustments. Higher-precision
5975
+ contributor values can still require :func:`write_sp3_with_rounding` or
5976
+ :func:`write_sp3_with_accuracy_rounding` when accuracy columns need
5977
+ conservative increases;
5978
+ ``detail`` on a strict-writer exception names the value.
5918
5979
  """
5919
5980
  merged, report = fetch_merged_sp3(
5920
5981
  target,
@@ -494,3 +494,13 @@ def fetch_merged_sp3_file(
494
494
  **fetch_opts: object,
495
495
  ) -> Union[str, tuple[str, MergeReport]]: ...
496
496
  def write_sp3(sp3: sidereon.Sp3, path: str, *, gzip: bool = ...) -> str: ...
497
+ def write_sp3_with_rounding(
498
+ sp3: sidereon.Sp3, path: str, *, gzip: bool = ...
499
+ ) -> tuple[str, sidereon.Sp3RoundingReport]: ...
500
+ def write_sp3_with_accuracy_rounding(
501
+ sp3: sidereon.Sp3,
502
+ path: str,
503
+ *,
504
+ policy: sidereon.Sp3AccuracyRoundingPolicy,
505
+ gzip: bool = ...,
506
+ ) -> tuple[str, sidereon.Sp3DetailedRoundingReport]: ...
@@ -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 = "5cfcdf929b57a5998e304df786735b171ba8afaf"
16
+ CORE_SOURCE_REVISION = "39e5f3d67866c24f08b01d51db7fc09b0d06cd88"
17
17
 
18
18
 
19
19
  def registry_version(name, value, package_version):
@@ -307,7 +307,7 @@ def main() -> None:
307
307
  "ocean.rs": "25946677944425671a92717860ac2d70f255de5403eeb1fbf98b361716821d5c",
308
308
  "pole.rs": "b4cc4c16bdd8ce1d8f04073602ab47dfb85a002b946ab192e8d4d2d600f0a1f8",
309
309
  }
310
- tide_root = ROOT / "third_party_source" / "sidereon-core-3.0.4" / "tides"
310
+ tide_root = ROOT / "third_party_source" / "sidereon-core-3.0.6" / "tides"
311
311
  for filename, expected_digest in tide_sources.items():
312
312
  source = tide_root / filename
313
313
  if not source.is_file():
@@ -316,7 +316,7 @@ def main() -> None:
316
316
  if digest != expected_digest:
317
317
  raise SystemExit(
318
318
  f"IERS-derived source disclosure {source} has digest {digest}, "
319
- f"expected {expected_digest} from sidereon-core 3.0.4 at "
319
+ f"expected {expected_digest} from sidereon-core 3.0.6 at "
320
320
  f"{CORE_SOURCE_REVISION}"
321
321
  )
322
322
 
@@ -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.4";
11
+ const RELEASE_VERSION: &str = "3.0.6";
12
12
 
13
13
  #[derive(Debug)]
14
14
  struct LockedPackage {