Configuration¶
LEOPath uses YAML configuration files under leopath/config/.
Core settings¶
constellation: orbital layout and TLE generationsimulation: time horizon, time step, and routing algorithmsatellite: altitude and antenna coneground_stations: list of ground stationsnetwork: interface counts and bandwidthlogging: log output settings
Example¶
constellation:
name: "Starlink-550"
num_orbits: 72
num_sats_per_orbit: 22
phase_diff: true
inclination_degree: 53
eccentricity: 0.0000001
arg_of_perigee_degree: 0.0
mean_motion_rev_per_day: 15.05
tle_output_filename: "tles_starlink_550_sgp.txt"
simulation:
dynamic_state_algorithm: shortest_path_link_state
end_time_hours: 24
time_step_minutes: 10
offset_ns: 0
satellite:
altitude_m: 550000
cone_angle_degrees: 25.0
ground_stations:
- name: "London"
latitude: 51.5074
longitude: -0.1278
elevation_m: 30.0
network:
gsl_interfaces:
number_of_interfaces: 1
aggregate_max_bandwidth: 1.0
logging:
is_debug: false
file_name: "simulation.log"
Constellation layout¶
A shell is a Walker constellation, and three things about it are easy to get wrong without any error message.
Planes versus satellites per plane. num_orbits counts orbital planes; num_sats_per_orbit counts the satellites in each one. Swap them and the logical graph stays the same torus, so nothing complains, but the physics flips:
72 planes x 22 sats (Starlink, FCC 21-48) 22 planes x 72 sats (swapped)
planes 5.0 deg apart planes 16.4 deg apart
in-plane links ~1 960 km in-plane links ~600 km
cross-plane links 619 - 1 426 km cross-plane links 881 - 2 153 km
Since the pivot estimator exists to exploit ISL geometry, a swapped shell measures a different network.
Mean motion and altitude have to agree. SGP4 flies mean_motion_rev_per_day; satellite.altitude_m never reaches the propagator. It sets the ground-link range and the ISL length limit instead, so when the two disagree, visibility gets computed for an orbit the satellites aren't on. The shipped configs pair them like this, measured by propagating each shell:
| config | planes x sats | mean motion | SGP4 flies | layout from |
|---|---|---|---|---|
starlink.yaml |
72 x 22 | 15.05 | 551 km | FCC 21-48 |
kuiper.yaml |
34 x 34 | 14.80 | 629 km | FCC 20-102 |
oneweb.yaml |
12 x 49 | 13.16 | 1 202 km | FCC DA 23-362 |
telesat.yaml |
27 x 13 | 13.66 | 1 015 km | Hypatia |
Hypatia's Starlink value, 15.19, flies about 508 km despite its "~550 km" label; live Starlink satellites between 550 and 559 km show a median of 15.03.
Delta or star. raan_spread_degree (default 360) sets the arc the ascending nodes cover:
WALKER DELTA, 360 deg WALKER STAR, 180 deg
Starlink, Kuiper, Telesat OneWeb
last plane --> wrap --> plane 0 last plane ==><== plane 0
same direction opposite directions
wrap link: ordinary length wrap link: up to 15 000 km
Near-polar shells fly as stars, because at about 88 degrees a node at RAAN and one at RAAN + 180 trace nearly the same ground track in opposite directions. The wrap between the last and first plane then joins satellites up to half an orbit apart, so the grid scenario builds the cylinder on a star shell, exactly as grid_seam does. On a delta shell the wrap is an ordinary co-rotating link, and grid_seam becomes a stress test for losing it. Run metadata records which one happened as isl_seam_wrap.
Example configs¶
leopath/config/ether_simple.yamlleopath/config/starlink.yamlleopath/config/kuiper.yamlleopath/config/oneweb.yamlleopath/config/telesat.yamlleopath/config/dense_synthetic.yamlleopath/config/shells/*.yaml, one per real shell, see below
Per-shell configs¶
scripts/make_shell_configs.py writes one config per shell of the multi-shell constellations, with every parameter taken from an FCC order: Starlink Gen1 from FCC 21-48 para 4, Kuiper from FCC 20-102 footnote 3, Starlink Gen2 from FCC 22-91 para 7. Edit the script, not the generated files.
| config | planes x satellites | altitude, inclination |
|---|---|---|
starlink_gen1_550, starlink_gen1_540 |
72 x 22 | 550 km 53°, 540 km 53.2° |
starlink_gen1_570 |
36 x 20 | 570 km, 70° |
starlink_gen1_560a, starlink_gen1_560b |
6 x 58, 4 x 43 | 560 km, 97.6° |
kuiper_590, kuiper_610, kuiper_630 |
28 x 28, 36 x 36, 34 x 34 | 590 km 33°, 610 km 42°, 630 km 51.9° |
starlink_gen2_525, _530, _535 |
28 x 120 | 525 km 53°, 530 km 43°, 535 km 33° |
starlink_gen2_360 |
30 x 120 | 360 km, 96.9°, the largest shell filed |
FCC 20-102 gives Kuiper's 610 km shell as "42 orbital planes with 36 satellites per plane for a total of 1296". That can't be right, since 42 × 36 is 1 512, and the order totals 98 planes elsewhere; the config uses 36 × 36. Gen2's three authorised shells hold 10 080 slots of which the FCC authorised 7 500, so those configs carry the filed geometry, not the authorised count. Shells above 80° inclination are laid out as Walker stars, following the rule in generate_tles_from_scratch; the filings don't give the node spread.
The script also checks each shell's longest cross-plane link against line of sight. starlink_gen1_560b fails it: four planes spread over 180° are 45° apart, and the longest rung, 5 390 km, exceeds the 5 072 km limit, so that shell can't form a cross-plane grid at all.