These instructions apply to the entire repository and are the baseline quality bar for AI agents and automated helpers.
SCEPTer (Satellite Constellation Emission Pattern simulator for radio Telescopes) models EPFD impact from non-GSO satellite systems on radio-astronomy stations. The toolkit covers TLE/orbit generation, antenna patterns (ITU-R S.1528), propagation, GPU-accelerated EPFD computation, and post-processing/visualisation.
| Entry point | Purpose |
|---|---|
gui.py |
Desktop GUI launcher (PySide6) — bootstrap, splash, then scepter_GUI |
scepter/scepter_GUI.py |
Main GUI window — constellation editor, RAS geometry, grid analyser, 3-D viewer |
Jupyter notebooks (xxx_*.ipynb, $4RSA_*.ipynb) |
Analysis / batch simulation workflows |
scepter/)| Module | Responsibility |
|---|---|
angle_sampler.py |
Sky-grid angle sampling (S.1586) |
antenna.py |
Satellite & RAS antenna patterns (S.1528 Rec 1.2 / Rec 1.4, M.2101, S.672, RA.1631), optional Numba |
custom_antenna.py |
User-supplied LUT patterns, schema v1 (authoritative format in the module docstring) — loader, CPU evaluators, dump/inspect CLI |
analytical_fixtures.py |
Helpers that sample any analytical pattern onto a user-chosen grid and produce a CustomAntennaPattern — used by tests and by users who want an LUT from an ITU formula |
custom_antenna_preview.py |
Pure-matplotlib figure factory for loaded custom patterns (polar 1-D, heatmap + principal-plane cuts 2-D) |
earthgrid.py |
Hex-grid generation, footprint geometry, optional Numba |
gpu_accel.py |
GPU-accelerated SGP4, angular distance, CuPy kernels |
nbeam.py |
Beam-cap sizing with visibility-aware pooling policies |
obs.py |
Observation simulation (propagation + antenna → RFI) |
satsim.py |
Link-selection helpers (CPU/GPU variants) |
scenario.py |
Scenario orchestration, HDF5 streaming I/O, integration |
skynet.py |
Sky-grid generation (S.1586 implementation) |
tleforger.py |
Synthetic TLE generation from belt definitions |
tlefinder.py |
TLE archive discovery and catalog helpers |
visualise.py |
CDF/CCDF, hemispheres, sky maps, animation export |
postprocess_recipes.py |
HDF5 inspection and plot recipes |
gui_bootstrap.py |
App identity, splash screen (SVG icon via QSvgRenderer, rounded corners, fade-out, non-blocking), icon loading (lightweight; depends on PySide6.QtSvg) |
scepter_GUI.py |
Full desktop GUI (~23 k lines) |
appinfo.py |
Shared version/branding metadata |
scepter/data/)satellite_app_icon.svg / .ico — application icon (SVG is the
high-fidelity source rendered via QSvgRenderer in the splash screen;
ICO is for Windows native taskbar)scepter_brand_mark.svg — brand markearth_texture_nasa_flat_earth_8192.jpg — 3-D viewer Earth texturene_10m_coastline.geojson, ne_10m_land.geojson — Natural Earth 10 m data| Environment | Python | Scope |
|---|---|---|
scepter-dev |
3.10 | Core simulation + GPU (CuPy, Numba); preferred for benchmarking |
scepter-dev-full |
3.13 | Full stack: PySide6, PyVista, Numba, CuPy (may have issues) |
conda env create -f environment.yml # lite
conda env create -f environment-full.yml # full
conda activate scepter-dev-full
python -m pytest --basetemp=.pytest-tmp -v # run tests
python gui.py # launch the GUI
Tests live in scepter/tests/. GUI tests use QT_QPA_PLATFORM=offscreen.
No pytest-qt — event-loop management is manual via QEventLoop/QTimer.
conftest.py forces matplotlib.use("Agg") for headless plot rendering.
astropy (units/time), pycraf (ITU propagation), cysgp4 (SGP4/TLE), shapely (geometry), PySide6 (Qt GUI), PyVista/PyVistaQt (3-D), h5py (HDF5), Numba (optional JIT), CuPy (optional GPU arrays).
.github/copilot-instructions.md before proposing structural or code
changes.git diff and remove no-op hunks (especially
remove/add pairs with identical content).scepter-dev (lite) and
scepter-dev-full (full).Parameters,
Returns, Raises, and Notes (plus Examples when practical).environment.yml (lite) and
environment-full.yml (full), unless the dependency is intentionally full-only.environment-full.yml unless they
are strictly required for lite workflows.When optimizing gpu_accel.py or scenario.py, follow these guidelines:
Run python run_benchmark.py 20 for quick 20-batch tests; use
python run_benchmark.py full for production validation. Results go to
benchmark_log.txt. Batch 0 includes JIT warmup — use “steady state (skip 2
warmup)” avg for meaningful comparisons. Two configs are provided:
benchmark_config.json (standard, no boresight) and
benchmark_boresight_config.json (boresight avoidance).
Set gpu_memory_budget_gb and host_memory_budget_gb in config files.
8 GB VRAM machines: use 6/6 GB. 16 GB VRAM machines: 12/12 GB.
Per-batch pipeline: propagate → derive_from_eci → link_library.add_chunk →
beam_finalize → power → export_copy → write_enqueue.
Dominant stages (non-boresight): power (~36%), export_copy (~33%),
finalize (~14%). See CLAUDE.md for exact timings and line references.
(T=49, S≈180, B≈30).
Python dispatch + CUDA launch overhead (3-10 ms/op) dominates, not bandwidth.cp.ElementwiseKernel or cp.RawKernel..astype(cp.float32, copy=False) on float32 arrays.cp.nonzero + fancy indexing for filtering — use cp.where with
sentinel values when possible (fewer kernel launches).out= parameter for in-place operations (cp.clip, etc.).profile_stages=True in the benchmark config to get per-stage
timing breakdowns.| Function | File | Role |
|---|---|---|
_accumulate_ras_power_cp |
gpu_accel.py:~9852 |
Power computation (biggest single stage) |
_evaluate_s1528_pattern_cp |
gpu_accel.py:~9695 |
Transmit pattern (uses LUT for phi=None) |
_evaluate_ras_pattern_cp |
gpu_accel.py:~9667 |
Receive pattern (fused ElementwiseKernel) |
_compute_gpu_direct_epfd_batch_device |
scenario.py:~6843 |
Batch orchestration |
run_gpu_direct_epfd |
scenario.py:~7881 |
Top-level iteration driver |
GpuScepterSession |
gpu_accel.py:~11432 |
Session lifecycle, caches, activation |
prepare_peak_pfd_lut_context |
gpu_accel.py |
Builds the surface-PFD cap per-beam LUT. 1-D K(β, shell) for every pattern whose asymmetry rotates with the beam (S.1528 Rec 1.2, S.1528 Rec 1.4 both symmetric and asymmetric, Custom-1D, Custom-2D, isotropic); 2-D K(α, β, shell) for M.2101 whose element pattern is fixed in the sat body frame. |
_compute_aggregate_surface_pfd_cap_cp |
gpu_accel.py |
Loop-free GPU-native per-satellite aggregate cap helper; supports the 3-D and 4-D paths and every TX-pattern family (S.1528 / M.2101 / Custom-1D / Custom-2D). |
Optional feature that bounds the peak PFD any beam (or any satellite in
aggregate) can deposit on Earth’s surface. Enabled from the GUI
Service & Demand tab. When editing or extending it, keep these invariants:
test_aggregate_cap_single_beam_matches_per_beam_analytic.include_atmosphere setting. The LUT
builder queries the same atmosphere LUT the hot power path uses.precomputed_cap_factor_cp. Do not
re-run the helpers inside a per-slab loop. per_satellite in the
4-D path is supported only via the hoisted precomputed factor;
direct kernel callers without the hoisted tensor get a clear error.K_act = 32 for
memory / compute reasons (physically exact for narrow-beam
constellations). If you relax this, update
test_aggregate_cap_large_k_skips_pair_midpoints.K(β) LUT (GpuPeakPfdLutContext.is_2d == False) is used
for every transmit pattern whose asymmetry rotates with the beam
boresight — the full-360° observation sweep of the builder then
renders the result α-invariant. This covers S.1528 Rec 1.2,
S.1528 Rec 1.4 (both symmetric and asymmetric), Custom-1D,
Custom-2D, and isotropic. The asymmetric S.1528 and Custom-2D
builders use a 2-D (ψ, φ) observation sweep so the φ-dependent
main-lobe peak is captured correctly; the output is still 1-D.K(α, β) LUT (is_2d == True) is only used for M.2101 —
its element pattern is fixed in the sat body frame, so K genuinely
depends on the beam’s steering azimuth.