tabsim 0.0.13__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. {tabsim-0.0.13 → tabsim-0.1.0}/PKG-INFO +173 -11
  2. {tabsim-0.0.13 → tabsim-0.1.0}/README.md +155 -6
  3. tabsim-0.1.0/benchmarks/README.md +346 -0
  4. tabsim-0.1.0/benchmarks/__init__.py +1 -0
  5. tabsim-0.1.0/benchmarks/cases.py +84 -0
  6. tabsim-0.1.0/benchmarks/conftest.py +56 -0
  7. tabsim-0.1.0/benchmarks/execution_sweep.py +154 -0
  8. tabsim-0.1.0/benchmarks/execution_worker.py +87 -0
  9. tabsim-0.1.0/benchmarks/fixtures/cases.json +131 -0
  10. tabsim-0.1.0/benchmarks/geometry_setup_sweep.py +45 -0
  11. tabsim-0.1.0/benchmarks/geometry_setup_worker.py +25 -0
  12. tabsim-0.1.0/benchmarks/harness.py +321 -0
  13. tabsim-0.1.0/benchmarks/mapped_diagnostics.py +88 -0
  14. tabsim-0.1.0/benchmarks/noise_comparison.py +79 -0
  15. tabsim-0.1.0/benchmarks/noise_scaling.py +133 -0
  16. tabsim-0.1.0/benchmarks/output_sweep.py +157 -0
  17. tabsim-0.1.0/benchmarks/output_worker.py +45 -0
  18. tabsim-0.1.0/benchmarks/precision_sweep.py +274 -0
  19. tabsim-0.1.0/benchmarks/precision_worker.py +238 -0
  20. tabsim-0.1.0/benchmarks/rfi_kernel_probe.py +188 -0
  21. tabsim-0.1.0/benchmarks/run.py +261 -0
  22. tabsim-0.1.0/benchmarks/selected_capacity_run.py +30 -0
  23. tabsim-0.1.0/benchmarks/selected_capacity_worker.py +38 -0
  24. tabsim-0.1.0/benchmarks/stack_profile.py +40 -0
  25. tabsim-0.1.0/benchmarks/stack_sweep.py +86 -0
  26. tabsim-0.1.0/benchmarks/stack_worker.py +78 -0
  27. tabsim-0.1.0/benchmarks/staged_plugin.py +63 -0
  28. tabsim-0.1.0/benchmarks/staged_zarr.py +16 -0
  29. tabsim-0.1.0/benchmarks/test_benchmarks.py +168 -0
  30. tabsim-0.1.0/benchmarks/trace_summary.py +48 -0
  31. {tabsim-0.0.13 → tabsim-0.1.0}/pyproject.toml +24 -8
  32. tabsim-0.1.0/tabsim/beam.py +32 -0
  33. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/config.py +518 -128
  34. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/dask/coordinates.py +141 -57
  35. tabsim-0.1.0/tabsim/dask/extras.py +131 -0
  36. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/dask/interferometry.py +314 -59
  37. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/dask/observation.py +341 -168
  38. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/config/sim_config_base.yaml +41 -19
  39. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/config/tab_config_base.yaml +6 -5
  40. tabsim-0.1.0/tabsim/data/telescopes/SKA-Low-AA0.5.itrf.txt +4 -0
  41. tabsim-0.1.0/tabsim/data/telescopes/SKA-Low-AA1.itrf.txt +16 -0
  42. tabsim-0.1.0/tabsim/data/telescopes/SKA-Low-AA2.itrf.txt +68 -0
  43. tabsim-0.1.0/tabsim/data/telescopes/SKA-Low-AA4.itrf.txt +512 -0
  44. tabsim-0.1.0/tabsim/data/telescopes/SKA-Low-AAstar-Phase-1.itrf.txt +108 -0
  45. tabsim-0.1.0/tabsim/data/telescopes/SKA-Low-AAstar.itrf.txt +307 -0
  46. tabsim-0.1.0/tabsim/data/telescopes/SKA-Low-PROVENANCE.md +68 -0
  47. tabsim-0.1.0/tabsim/data/telescopes/_telescopes.yaml +72 -0
  48. tabsim-0.1.0/tabsim/execution.py +102 -0
  49. tabsim-0.1.0/tabsim/jax/beam.py +90 -0
  50. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/jax/coordinates.py +76 -20
  51. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/jax/interferometry.py +367 -88
  52. tabsim-0.1.0/tabsim/orbit.py +1231 -0
  53. tabsim-0.1.0/tabsim/orbit_config.py +384 -0
  54. tabsim-0.1.0/tabsim/precision.py +10 -0
  55. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/read_ms.py +42 -7
  56. tabsim-0.1.0/tabsim/satchecker_names.py +362 -0
  57. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/scripts/ast_region.py +1 -1
  58. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/scripts/import_tle_files.py +20 -24
  59. tabsim-0.1.0/tabsim/scripts/sim_vis.py +274 -0
  60. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/scripts/tle_sat_region.py +52 -32
  61. tabsim-0.1.0/tabsim/sky.py +264 -0
  62. tabsim-0.1.0/tabsim/staged.py +346 -0
  63. tabsim-0.1.0/tabsim/time.py +450 -0
  64. tabsim-0.1.0/tabsim/tle.py +669 -0
  65. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/write.py +108 -13
  66. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim.egg-info/PKG-INFO +173 -11
  67. tabsim-0.1.0/tabsim.egg-info/SOURCES.txt +132 -0
  68. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim.egg-info/entry_points.txt +1 -2
  69. tabsim-0.1.0/tabsim.egg-info/requires.txt +46 -0
  70. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim.egg-info/top_level.txt +2 -0
  71. tabsim-0.1.0/tests/conftest.py +68 -0
  72. tabsim-0.1.0/tests/live_service.py +57 -0
  73. tabsim-0.1.0/tests/orbit_helpers.py +577 -0
  74. tabsim-0.1.0/tests/test_benchmark_harness.py +373 -0
  75. tabsim-0.1.0/tests/test_chunk_planning.py +107 -0
  76. tabsim-0.1.0/tests/test_execution.py +148 -0
  77. tabsim-0.1.0/tests/test_execution_drivers.py +59 -0
  78. tabsim-0.1.0/tests/test_geometry_setup_driver.py +15 -0
  79. tabsim-0.1.0/tests/test_host_beam.py +137 -0
  80. tabsim-0.1.0/tests/test_irregular_chunks.py +91 -0
  81. tabsim-0.1.0/tests/test_lazy_geometry.py +164 -0
  82. tabsim-0.1.0/tests/test_live_service.py +204 -0
  83. tabsim-0.1.0/tests/test_mapped_jax.py +125 -0
  84. tabsim-0.1.0/tests/test_mapped_profile_ownership.py +45 -0
  85. tabsim-0.1.0/tests/test_ms_precision.py +230 -0
  86. tabsim-0.1.0/tests/test_orbit.py +2143 -0
  87. tabsim-0.1.0/tests/test_orbit_adoption.py +1503 -0
  88. tabsim-0.1.0/tests/test_orbit_cli.py +357 -0
  89. tabsim-0.1.0/tests/test_orbit_config.py +249 -0
  90. tabsim-0.1.0/tests/test_output_drivers.py +59 -0
  91. tabsim-0.1.0/tests/test_phase_centre_of_date.py +809 -0
  92. tabsim-0.1.0/tests/test_precision_driver.py +304 -0
  93. tabsim-0.1.0/tests/test_random_sky_params.py +302 -0
  94. tabsim-0.1.0/tests/test_random_sources_config.py +175 -0
  95. tabsim-0.1.0/tests/test_rfi_beam.py +158 -0
  96. tabsim-0.1.0/tests/test_rfi_vis_kernel.py +210 -0
  97. tabsim-0.1.0/tests/test_sim-vis.py +599 -0
  98. tabsim-0.1.0/tests/test_single_integration.py +50 -0
  99. tabsim-0.1.0/tests/test_ska_low_generator.py +27 -0
  100. tabsim-0.1.0/tests/test_sky.py +427 -0
  101. tabsim-0.1.0/tests/test_staged_api.py +294 -0
  102. tabsim-0.1.0/tests/test_staged_plugin_guard.py +16 -0
  103. tabsim-0.1.0/tests/test_staged_zarr.py +286 -0
  104. tabsim-0.1.0/tests/test_telescope_data.py +69 -0
  105. tabsim-0.1.0/tests/test_timeouts.py +80 -0
  106. tabsim-0.1.0/tests/test_visibility_noise.py +113 -0
  107. tabsim-0.1.0/tests/test_visibility_precision.py +321 -0
  108. tabsim-0.1.0/tests/timeouts.py +50 -0
  109. tabsim-0.1.0/tools/generate_ska_low.py +99 -0
  110. tabsim-0.0.13/tabsim/dask/extras.py +0 -60
  111. tabsim-0.0.13/tabsim/data/rfi/tles/2023-02-21-navstar.json +0 -1
  112. tabsim-0.0.13/tabsim/data/rfi/tles/2024-01-15-1T78VE.json +0 -1
  113. tabsim-0.0.13/tabsim/data/rfi/tles/2024-01-15-ISS.json +0 -1
  114. tabsim-0.0.13/tabsim/data/telescopes/_telescopes.yaml +0 -19
  115. tabsim-0.0.13/tabsim/scripts/setup_spacetrack_login.py +0 -103
  116. tabsim-0.0.13/tabsim/scripts/sim_vis.py +0 -158
  117. tabsim-0.0.13/tabsim/sky.py +0 -139
  118. tabsim-0.0.13/tabsim/tle.py +0 -879
  119. tabsim-0.0.13/tabsim.egg-info/SOURCES.txt +0 -55
  120. tabsim-0.0.13/tabsim.egg-info/requires.txt +0 -30
  121. tabsim-0.0.13/tests/test_sim-vis.py +0 -40
  122. tabsim-0.0.13/tests/test_tle_api.py +0 -512
  123. {tabsim-0.0.13 → tabsim-0.1.0}/LICENSE +0 -0
  124. {tabsim-0.0.13 → tabsim-0.1.0}/docs/conf.py +0 -0
  125. {tabsim-0.0.13 → tabsim-0.1.0}/setup.cfg +0 -0
  126. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/__init__.py +0 -0
  127. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/dask/__init__.py +0 -0
  128. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/__init__.py +0 -0
  129. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/rfi/norad_satellite.rfimodel +0 -0
  130. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/rfi/satellite.ole +0 -0
  131. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/rfi/satellite.rfimodel +0 -0
  132. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/rfi/stationary.loc +0 -0
  133. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/rfi/stationary.rfimodel +0 -0
  134. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/sky/calibrator.skymodel +0 -0
  135. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/telescopes/EDA2.itrf.txt +0 -0
  136. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/telescopes/MWA.itrf.txt +0 -0
  137. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/data/telescopes/MeerKAT.itrf.txt +0 -0
  138. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/jax/__init__.py +0 -0
  139. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/jax/utils.py +0 -0
  140. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/plot.py +0 -0
  141. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/scripts/__init__.py +0 -0
  142. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/scripts/flag_data.py +0 -0
  143. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/scripts/write_config_from_ms.py +0 -0
  144. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim/tools.py +0 -0
  145. {tabsim-0.0.13 → tabsim-0.1.0}/tabsim.egg-info/dependency_links.txt +0 -0
  146. {tabsim-0.0.13 → tabsim-0.1.0}/tests/test_basic.py +0 -0
  147. {tabsim-0.0.13 → tabsim-0.1.0}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tabsim
3
- Version: 0.0.13
3
+ Version: 0.1.0
4
4
  Summary: Trajectory-based Radio Frequency Interference (RFI) simulations for radio interferometeric data.
5
5
  Author-email: Chris Finlay <christopher.finlay@epfl.ch>
6
6
  Maintainer-email: Chris Finlay <christopher.finlay@epfl.ch>
@@ -680,24 +680,31 @@ License: GNU GENERAL PUBLIC LICENSE
680
680
  <https://www.gnu.org/licenses/why-not-lgpl.html>.
681
681
 
682
682
  Project-URL: Repository, https://github.com/chrisfinlay/tab-sim.git
683
- Requires-Python: >=3.9
683
+ Requires-Python: >=3.10
684
684
  Description-Content-Type: text/markdown
685
685
  License-File: LICENSE
686
686
  Requires-Dist: jax
687
687
  Requires-Dist: dask
688
- Requires-Dist: xarray
688
+ Requires-Dist: psutil
689
+ Requires-Dist: xarray<2026.9
689
690
  Requires-Dist: zarr<3.0.0
690
691
  Requires-Dist: dask-ms>=0.2.21
691
692
  Requires-Dist: scipy
692
693
  Requires-Dist: tqdm
693
694
  Requires-Dist: matplotlib
694
695
  Requires-Dist: astropy
695
- Requires-Dist: spacetrack
696
- Requires-Dist: skyfield
696
+ Requires-Dist: skyfield<2,>=1.45
697
+ Requires-Dist: sgp4
698
+ Requires-Dist: platformdirs
697
699
  Requires-Dist: numpy
698
700
  Requires-Dist: pyyaml
701
+ Requires-Dist: pandas
702
+ Requires-Dist: pyerfa
703
+ Requires-Dist: satchecker-client>=0.2.0
704
+ Requires-Dist: ri_kernels>=0.2.1
699
705
  Provides-Extra: gpu
700
706
  Requires-Dist: jax[cuda12]; extra == "gpu"
707
+ Requires-Dist: ri_kernels[cuda12]; extra == "gpu"
701
708
  Provides-Extra: sat
702
709
  Requires-Dist: regions; extra == "sat"
703
710
  Provides-Extra: ipy
@@ -705,6 +712,12 @@ Requires-Dist: ipython; extra == "ipy"
705
712
  Requires-Dist: ipykernel; extra == "ipy"
706
713
  Provides-Extra: test
707
714
  Requires-Dist: pytest; extra == "test"
715
+ Requires-Dist: psutil>=5.9; extra == "test"
716
+ Provides-Extra: benchmark
717
+ Requires-Dist: pytest>=7; extra == "benchmark"
718
+ Requires-Dist: pytest-benchmark<6,>=5.1; extra == "benchmark"
719
+ Requires-Dist: psutil>=5.9; extra == "benchmark"
720
+ Requires-Dist: nvidia-ml-py>=12; sys_platform == "linux" and extra == "benchmark"
708
721
  Provides-Extra: docs
709
722
  Requires-Dist: sphinx; extra == "docs"
710
723
  Requires-Dist: sphinx-rtd-theme; extra == "docs"
@@ -804,19 +817,156 @@ To enable GPU compute you need the GPU version of `jaxlib` installed. The easies
804
817
 
805
818
  ### Including TLE-based satelllites
806
819
 
807
- You will need to provide [Space-Track](https://www.space-track.org/auth/login) login details as a YAML file. The filename can be `spacetrack_login.yaml` for example and should look like
820
+ Orbital records come from the [IAU CPS SatChecker](https://satchecker.cps.iau.org/)
821
+ service. **No account or credentials are required** — the previous Space-Track login
822
+ file is no longer used, and the `spacetrack` dependency is gone.
823
+
824
+ The machinery that executes a selection — asking the sources in order, choosing the
825
+ record nearest the observation, reusing the cache, falling back to the other
826
+ archive, and writing and reading the frozen-replay files — is
827
+ [`satchecker-client`](https://github.com/epfl-radio-astro/satchecker-client), shared
828
+ with TABASCAL so there is one implementation of it rather than two. The policy is
829
+ still `tab-sim`'s and is stated on every call: the source order, the defaults
830
+ below, what an acceptable record is, what happens when a satellite cannot be
831
+ resolved, and every message in this section. `tab-sim` requires `satchecker-client`
832
+ 0.2.0 or later, the first release with the API it uses.
833
+
834
+ Name the satellites you want in the config, either by NORAD catalogue ID or by name:
808
835
 
809
836
  ```yaml
810
- username: user@email.com
811
- password: password123
837
+ rfi_sources:
838
+ tle_satellite:
839
+ norad_ids: [25544] # every listed ID must resolve, or the run stops
840
+ sat_names: [navstar] # substring match against the catalogue
841
+ ```
842
+
843
+ SatChecker serves two record formats from two archives that do not overlap: TLEs up
844
+ to 2026-07-11, and OMM (Orbit Mean-Elements Message) element sets from 2026-07-12
845
+ onward. `tab-sim` handles both and picks the archive your observation epoch falls
846
+ in, so nothing about the configuration changes across the boundary. Selection is
847
+ *not* globally nearest across both: the epoch chooses one archive, and the other is
848
+ asked only when the first has nothing acceptable.
849
+
850
+ #### Naming satellites
851
+
852
+ `sat_names` is a **substring** match, as Space-Track's `like` search was, so
853
+ `navstar` selects every `NAVSTAR nn (USA nnn)` entry. The catalogue is written
854
+ almost entirely in upper case and the service matches case-sensitively, so your
855
+ query is upper-cased for you; a few mixed-case names (`DMSat-1`) cannot be reached
856
+ by any single spelling, and `%` and `_` are SQL wildcards. Satellites the catalogue
857
+ says had decayed — or had not launched — **at the observation epoch** are excluded,
858
+ so a historical observation keeps the satellites that were in orbit then.
859
+
860
+ A name search is cached beside the orbit records and reused for
861
+ `search_cache_max_age_days` (default 1) of wall-clock time. That is a different
862
+ question from how old an orbital *record* may be: `remote_max_age_days` (default 3)
863
+ is the hard ceiling on `|record epoch − observation epoch|`, and
864
+ `cache_reuse_max_age_days` (default 1) is when a cached record is close enough to
865
+ skip a request. Set `remote_max_age_days: 1` for the old ±1-day acceptance bound.
866
+ Three days is a broader window, not a promise of three-day accuracy.
867
+
868
+ A broad name is expensive: SatChecker serves one record per request, so
869
+ `sat_names: [starlink]` is tens of thousands of requests and as many propagations.
870
+ `tab-sim` warns above 500 satellites and never truncates silently.
871
+
872
+ #### Caches, offline runs and frozen replay
873
+
874
+ Records are cached per satellite under the platform user-cache directory
875
+ (`~/.cache/orbit-cache`, `~/Library/Caches/orbit-cache`); set `ORBIT_CACHE_DIR` to
876
+ put it elsewhere. Catalogue searches are cached in the same directory. Cached rows
877
+ are the validated copy of what came back, so each states its `RECORD_KIND` and, for
878
+ a TLE, that its checksum digits were verified — no reader has to re-infer either.
879
+ The file scheme is unchanged and the cache is shared with other applications using
880
+ the same client, including older versions of it, so there is nothing to migrate and
881
+ nothing to purge. Records nothing has verified still never enter it.
882
+
883
+ Where several records are held for one satellite — the normal state of a cache that
884
+ has been used — the one selected is the nearest to the observation epoch **that this
885
+ run can use**, not the nearest of all of them. A record the run's checksum policy
886
+ refuses is not a candidate at all, so a nearer unusable row neither displaces a
887
+ usable one nor provokes a request the configuration does not ask for: what is chosen
888
+ is still inside `remote_max_age_days`, and if it is also inside
889
+ `cache_reuse_max_age_days` no request is sent. It is also what lets `--offline`
890
+ resolve from an acceptable record you hold instead of failing because a nearer row
891
+ happens to be unreadable. Earlier versions looked at the nearest record first and
892
+ went to SatChecker when it turned out to be unusable, which could fetch a closer
893
+ record — and, offline, could fail with an acceptable one in hand.
894
+
895
+ ```bash
896
+ sim-vis -c observation.yaml # online
897
+ sim-vis -c observation.yaml --offline # no requests at all
898
+ sim-vis -c observation.yaml --replay-orbit-dir previous/input_data # frozen replay
812
899
  ```
813
900
 
901
+ `--offline` forbids every SatChecker request. Cached searches are reused whatever
902
+ their age, with a warning saying how old they are; cached orbit records still have
903
+ to satisfy `remote_max_age_days`, because offline is about what can be reached and
904
+ not about what an acceptable record is. A satellite with no acceptable local record
905
+ stops the run saying so — it never claims SatChecker has no record for it.
906
+
907
+ Every simulation writes its final selection to `input_data/norad_ids.yaml` and the
908
+ records it propagated to `input_data/used_orbits.json`.
909
+ `--replay-orbit-dir <run>/input_data` makes those two files authoritative: the saved
910
+ IDs *are* the satellites, with no catalogue search, no cache, no request, no
911
+ visibility reselection and no `max_n_sat`. Anything missing or ambiguous in them
912
+ stops the run rather than being resolved from somewhere else. Reproducing a run's
913
+ visibilities exactly also assumes the same observation, spectral models, random
914
+ seeds and numerical environment; what replay freezes is the orbital input.
915
+
916
+ Replay directories are readable in both directions across this change: a pair
917
+ written before it replays now, and a pair written now replays with the earlier
918
+ code. The two files are the same two files, holding the same identities, the same
919
+ retained values and the same checksum provenance — the contract is what reads back,
920
+ not that the bytes match.
921
+
922
+ `extra_orbit_dir` (`--extra-orbit-dir`, also `-eod`) is a different thing: ordinary
923
+ per-NORAD-ID source precedence, searched ahead of the cache and SatChecker, while
924
+ the run still chooses its own satellites from its own names, IDs, visibility cuts
925
+ and `max_n_sat`. Use it to supply your own records — convert TLE text files with
926
+ `tabsim-import-tles` and point it at the output directory. Every `*.json` in that
927
+ directory must be a readable orbit table: one that is not stops the run naming
928
+ itself, rather than letting the run quietly fall back to records you said not to
929
+ use. `replay_orbit_dir` and `extra_orbit_dir` cannot be combined.
930
+
931
+ #### Checksums
932
+
933
+ A TLE line carries a checksum digit, and `tab-sim` rejects a line whose checksum
934
+ does not verify — always, under every setting. Some of SatChecker's historical
935
+ archive (roughly 2001–2018) reached it without that digit at all; those lines are
936
+ rejected by default, which can leave no record for those dates.
937
+ `allow_missing_checksum: true` (or `--allow-missing-checksum`) accepts them and
938
+ marks each record `unverified_missing_checksum` for the rest of its life: the
939
+ status is provenance, so it survives a save and a reload and is never upgraded
940
+ because the lines now parse. The same setting applies to remote records, to
941
+ `extra_orbit_dir` and to replay, so a permissive run cannot be laundered into a
942
+ strict one by saving it. Such records are deliberately kept out of the shared orbit
943
+ cache, which other tools and older client versions read, so the saved run records
944
+ are the only way to reproduce them:
945
+
946
+ ```bash
947
+ sim-vis -c observation.yaml --replay-orbit-dir previous/input_data \
948
+ --allow-missing-checksum
949
+ ```
950
+
951
+ #### Migrating from the Space-Track backend
952
+
953
+ `rfi_sources.tle_satellite.tle_dir` and `spacetrack_path` are rejected on sight —
954
+ they used to decide where records came from, and a run that kept one would
955
+ otherwise succeed while quietly ignoring it. Use `extra_orbit_dir` for existing
956
+ orbit JSON files and `ORBIT_CACHE_DIR` to relocate the managed cache; delete
957
+ `spacetrack_path`, along with the login file and the `-st` flag.
958
+
959
+ Historical completeness is bounded by what SatChecker holds: it cannot reconstruct
960
+ records it does not have, missing checksum evidence, or the catalogue's name
961
+ membership as it stood at a past epoch. Those limits surface as explicit
962
+ diagnostics rather than as a successful simulation with satellites missing from it.
963
+
814
964
  ### Running a simulation
815
965
 
816
966
  To run a simulation of a target field with 100 randomly distributed point sources and some GPS satellites simply run
817
967
 
818
968
  ```bash
819
- sim-vis -c sim_target_16A.yaml -st spacetrack_login.yaml
969
+ sim-vis -c sim_target_16A.yaml
820
970
  ```
821
971
 
822
972
  You can run the help function to see what other command line options there are.
@@ -825,6 +975,15 @@ You can run the help function to see what other command line options there are.
825
975
  sim-vis -h
826
976
  ```
827
977
 
978
+ ### Staged output and memory controls
979
+
980
+ Simulation output now stages components into one Zarr store before composing
981
+ observed visibilities. This is the default for CLI and Python writers. Large
982
+ `rfi_*_A` diagnostics and signal-statistic reductions are omitted by default;
983
+ output arrays, compute chunk size, component concurrency and resource guards are
984
+ configurable. See [staged output](docs/staged-output.md) for CLI, YAML and Python
985
+ examples and the distinction between task-boundary memory guards and hard caps.
986
+
828
987
  ### Config File Definition
829
988
 
830
989
  The simulation configuration file has many options with set defaults such that minimal configurations can be set unless more is required.
@@ -942,9 +1101,12 @@ The structure of the `.zarr` file is as follows:
942
1101
  | `n_stat_src` | Number of stationary RFI sources |
943
1102
  | `n_time` | Number of time steps |
944
1103
  | `n_time_fine` | Number of fine grained time steps |
945
- | `target_dec` | Declination of the target direction {deg} |
1104
+ | `geometry_time_scale` | Time scale the epochs behind the geometry are read on: `utc`. See [Phase tracking](docs/phase-tracking.md) |
1105
+ | `phase_tracking` | Direction the visibilities were fringe-stopped to: `apparent` is the target's apparent place of date. See [Phase tracking](docs/phase-tracking.md) |
1106
+ | `target_dec` | Declination of the ICRS target direction {deg} |
946
1107
  | `target_name` | Name of the target |
947
- | `target_ra` | Right Ascension of the target direction {deg} |
1108
+ | `target_ra` | Right Ascension of the ICRS target direction {deg} |
1109
+ | `target_frame` | Reference frame of the target coordinates (`ICRS`) |
948
1110
  | `tel_elevation` | Telescope elevation {m} |
949
1111
  | `tel_latitude` | Telescope latitude {deg} |
950
1112
  | `tel_longitude` | Telescope logitude {deg} |
@@ -92,19 +92,156 @@ To enable GPU compute you need the GPU version of `jaxlib` installed. The easies
92
92
 
93
93
  ### Including TLE-based satelllites
94
94
 
95
- You will need to provide [Space-Track](https://www.space-track.org/auth/login) login details as a YAML file. The filename can be `spacetrack_login.yaml` for example and should look like
95
+ Orbital records come from the [IAU CPS SatChecker](https://satchecker.cps.iau.org/)
96
+ service. **No account or credentials are required** — the previous Space-Track login
97
+ file is no longer used, and the `spacetrack` dependency is gone.
98
+
99
+ The machinery that executes a selection — asking the sources in order, choosing the
100
+ record nearest the observation, reusing the cache, falling back to the other
101
+ archive, and writing and reading the frozen-replay files — is
102
+ [`satchecker-client`](https://github.com/epfl-radio-astro/satchecker-client), shared
103
+ with TABASCAL so there is one implementation of it rather than two. The policy is
104
+ still `tab-sim`'s and is stated on every call: the source order, the defaults
105
+ below, what an acceptable record is, what happens when a satellite cannot be
106
+ resolved, and every message in this section. `tab-sim` requires `satchecker-client`
107
+ 0.2.0 or later, the first release with the API it uses.
108
+
109
+ Name the satellites you want in the config, either by NORAD catalogue ID or by name:
96
110
 
97
111
  ```yaml
98
- username: user@email.com
99
- password: password123
112
+ rfi_sources:
113
+ tle_satellite:
114
+ norad_ids: [25544] # every listed ID must resolve, or the run stops
115
+ sat_names: [navstar] # substring match against the catalogue
100
116
  ```
101
117
 
118
+ SatChecker serves two record formats from two archives that do not overlap: TLEs up
119
+ to 2026-07-11, and OMM (Orbit Mean-Elements Message) element sets from 2026-07-12
120
+ onward. `tab-sim` handles both and picks the archive your observation epoch falls
121
+ in, so nothing about the configuration changes across the boundary. Selection is
122
+ *not* globally nearest across both: the epoch chooses one archive, and the other is
123
+ asked only when the first has nothing acceptable.
124
+
125
+ #### Naming satellites
126
+
127
+ `sat_names` is a **substring** match, as Space-Track's `like` search was, so
128
+ `navstar` selects every `NAVSTAR nn (USA nnn)` entry. The catalogue is written
129
+ almost entirely in upper case and the service matches case-sensitively, so your
130
+ query is upper-cased for you; a few mixed-case names (`DMSat-1`) cannot be reached
131
+ by any single spelling, and `%` and `_` are SQL wildcards. Satellites the catalogue
132
+ says had decayed — or had not launched — **at the observation epoch** are excluded,
133
+ so a historical observation keeps the satellites that were in orbit then.
134
+
135
+ A name search is cached beside the orbit records and reused for
136
+ `search_cache_max_age_days` (default 1) of wall-clock time. That is a different
137
+ question from how old an orbital *record* may be: `remote_max_age_days` (default 3)
138
+ is the hard ceiling on `|record epoch − observation epoch|`, and
139
+ `cache_reuse_max_age_days` (default 1) is when a cached record is close enough to
140
+ skip a request. Set `remote_max_age_days: 1` for the old ±1-day acceptance bound.
141
+ Three days is a broader window, not a promise of three-day accuracy.
142
+
143
+ A broad name is expensive: SatChecker serves one record per request, so
144
+ `sat_names: [starlink]` is tens of thousands of requests and as many propagations.
145
+ `tab-sim` warns above 500 satellites and never truncates silently.
146
+
147
+ #### Caches, offline runs and frozen replay
148
+
149
+ Records are cached per satellite under the platform user-cache directory
150
+ (`~/.cache/orbit-cache`, `~/Library/Caches/orbit-cache`); set `ORBIT_CACHE_DIR` to
151
+ put it elsewhere. Catalogue searches are cached in the same directory. Cached rows
152
+ are the validated copy of what came back, so each states its `RECORD_KIND` and, for
153
+ a TLE, that its checksum digits were verified — no reader has to re-infer either.
154
+ The file scheme is unchanged and the cache is shared with other applications using
155
+ the same client, including older versions of it, so there is nothing to migrate and
156
+ nothing to purge. Records nothing has verified still never enter it.
157
+
158
+ Where several records are held for one satellite — the normal state of a cache that
159
+ has been used — the one selected is the nearest to the observation epoch **that this
160
+ run can use**, not the nearest of all of them. A record the run's checksum policy
161
+ refuses is not a candidate at all, so a nearer unusable row neither displaces a
162
+ usable one nor provokes a request the configuration does not ask for: what is chosen
163
+ is still inside `remote_max_age_days`, and if it is also inside
164
+ `cache_reuse_max_age_days` no request is sent. It is also what lets `--offline`
165
+ resolve from an acceptable record you hold instead of failing because a nearer row
166
+ happens to be unreadable. Earlier versions looked at the nearest record first and
167
+ went to SatChecker when it turned out to be unusable, which could fetch a closer
168
+ record — and, offline, could fail with an acceptable one in hand.
169
+
170
+ ```bash
171
+ sim-vis -c observation.yaml # online
172
+ sim-vis -c observation.yaml --offline # no requests at all
173
+ sim-vis -c observation.yaml --replay-orbit-dir previous/input_data # frozen replay
174
+ ```
175
+
176
+ `--offline` forbids every SatChecker request. Cached searches are reused whatever
177
+ their age, with a warning saying how old they are; cached orbit records still have
178
+ to satisfy `remote_max_age_days`, because offline is about what can be reached and
179
+ not about what an acceptable record is. A satellite with no acceptable local record
180
+ stops the run saying so — it never claims SatChecker has no record for it.
181
+
182
+ Every simulation writes its final selection to `input_data/norad_ids.yaml` and the
183
+ records it propagated to `input_data/used_orbits.json`.
184
+ `--replay-orbit-dir <run>/input_data` makes those two files authoritative: the saved
185
+ IDs *are* the satellites, with no catalogue search, no cache, no request, no
186
+ visibility reselection and no `max_n_sat`. Anything missing or ambiguous in them
187
+ stops the run rather than being resolved from somewhere else. Reproducing a run's
188
+ visibilities exactly also assumes the same observation, spectral models, random
189
+ seeds and numerical environment; what replay freezes is the orbital input.
190
+
191
+ Replay directories are readable in both directions across this change: a pair
192
+ written before it replays now, and a pair written now replays with the earlier
193
+ code. The two files are the same two files, holding the same identities, the same
194
+ retained values and the same checksum provenance — the contract is what reads back,
195
+ not that the bytes match.
196
+
197
+ `extra_orbit_dir` (`--extra-orbit-dir`, also `-eod`) is a different thing: ordinary
198
+ per-NORAD-ID source precedence, searched ahead of the cache and SatChecker, while
199
+ the run still chooses its own satellites from its own names, IDs, visibility cuts
200
+ and `max_n_sat`. Use it to supply your own records — convert TLE text files with
201
+ `tabsim-import-tles` and point it at the output directory. Every `*.json` in that
202
+ directory must be a readable orbit table: one that is not stops the run naming
203
+ itself, rather than letting the run quietly fall back to records you said not to
204
+ use. `replay_orbit_dir` and `extra_orbit_dir` cannot be combined.
205
+
206
+ #### Checksums
207
+
208
+ A TLE line carries a checksum digit, and `tab-sim` rejects a line whose checksum
209
+ does not verify — always, under every setting. Some of SatChecker's historical
210
+ archive (roughly 2001–2018) reached it without that digit at all; those lines are
211
+ rejected by default, which can leave no record for those dates.
212
+ `allow_missing_checksum: true` (or `--allow-missing-checksum`) accepts them and
213
+ marks each record `unverified_missing_checksum` for the rest of its life: the
214
+ status is provenance, so it survives a save and a reload and is never upgraded
215
+ because the lines now parse. The same setting applies to remote records, to
216
+ `extra_orbit_dir` and to replay, so a permissive run cannot be laundered into a
217
+ strict one by saving it. Such records are deliberately kept out of the shared orbit
218
+ cache, which other tools and older client versions read, so the saved run records
219
+ are the only way to reproduce them:
220
+
221
+ ```bash
222
+ sim-vis -c observation.yaml --replay-orbit-dir previous/input_data \
223
+ --allow-missing-checksum
224
+ ```
225
+
226
+ #### Migrating from the Space-Track backend
227
+
228
+ `rfi_sources.tle_satellite.tle_dir` and `spacetrack_path` are rejected on sight —
229
+ they used to decide where records came from, and a run that kept one would
230
+ otherwise succeed while quietly ignoring it. Use `extra_orbit_dir` for existing
231
+ orbit JSON files and `ORBIT_CACHE_DIR` to relocate the managed cache; delete
232
+ `spacetrack_path`, along with the login file and the `-st` flag.
233
+
234
+ Historical completeness is bounded by what SatChecker holds: it cannot reconstruct
235
+ records it does not have, missing checksum evidence, or the catalogue's name
236
+ membership as it stood at a past epoch. Those limits surface as explicit
237
+ diagnostics rather than as a successful simulation with satellites missing from it.
238
+
102
239
  ### Running a simulation
103
240
 
104
241
  To run a simulation of a target field with 100 randomly distributed point sources and some GPS satellites simply run
105
242
 
106
243
  ```bash
107
- sim-vis -c sim_target_16A.yaml -st spacetrack_login.yaml
244
+ sim-vis -c sim_target_16A.yaml
108
245
  ```
109
246
 
110
247
  You can run the help function to see what other command line options there are.
@@ -113,6 +250,15 @@ You can run the help function to see what other command line options there are.
113
250
  sim-vis -h
114
251
  ```
115
252
 
253
+ ### Staged output and memory controls
254
+
255
+ Simulation output now stages components into one Zarr store before composing
256
+ observed visibilities. This is the default for CLI and Python writers. Large
257
+ `rfi_*_A` diagnostics and signal-statistic reductions are omitted by default;
258
+ output arrays, compute chunk size, component concurrency and resource guards are
259
+ configurable. See [staged output](docs/staged-output.md) for CLI, YAML and Python
260
+ examples and the distinction between task-boundary memory guards and hard caps.
261
+
116
262
  ### Config File Definition
117
263
 
118
264
  The simulation configuration file has many options with set defaults such that minimal configurations can be set unless more is required.
@@ -230,9 +376,12 @@ The structure of the `.zarr` file is as follows:
230
376
  | `n_stat_src` | Number of stationary RFI sources |
231
377
  | `n_time` | Number of time steps |
232
378
  | `n_time_fine` | Number of fine grained time steps |
233
- | `target_dec` | Declination of the target direction {deg} |
379
+ | `geometry_time_scale` | Time scale the epochs behind the geometry are read on: `utc`. See [Phase tracking](docs/phase-tracking.md) |
380
+ | `phase_tracking` | Direction the visibilities were fringe-stopped to: `apparent` is the target's apparent place of date. See [Phase tracking](docs/phase-tracking.md) |
381
+ | `target_dec` | Declination of the ICRS target direction {deg} |
234
382
  | `target_name` | Name of the target |
235
- | `target_ra` | Right Ascension of the target direction {deg} |
383
+ | `target_ra` | Right Ascension of the ICRS target direction {deg} |
384
+ | `target_frame` | Reference frame of the target coordinates (`ICRS`) |
236
385
  | `tel_elevation` | Telescope elevation {m} |
237
386
  | `tel_latitude` | Telescope latitude {deg} |
238
387
  | `tel_longitude` | Telescope logitude {deg} |