Skip to content

Satellite suite: the moving constellation (leo-walker)

Regime: a time-varying LEO Walker shell — satellites move, ground stations hand over, ISLs fail and recover. The static ISL torus that came first is isl-grid.md; why satellites need their own suite at all is network-regimes.md. The substrate decision is ADR-0022.

← Benchmark index · Metrics · Methodology

Provenance — measured at 0b5a65ad. Seventeen satellite-benchmark.yml dispatches with harness=leo-walker, one arm per dispatch, on ghcr.io/danieljoppi/ns3:3.48 (default profile), 20 seeds each. Every cell carries its run id on its first line and its full ##CONFIG## line, and every cell passes scenario_check.py (the #297 handover-clock and outage-accounting rules) as committed.

cell anthocnet aodv olsr geo-greedy oracle oracle-delay
walker16 37827387100 37827390737 37827395914 37827410383 37827401516 37827405771
starlink1 37827426356 37827430778 37827443695 37827433965 37827439616
starlink1-storm 37827449496 37827453949 37827467035 37827458721 37827462965
hypatia calibration 37827470132

Cells: results/cells/leo-<cell>-<arm>.txt. Every leo-walker number measured before 0b5a65ad is superseded — see the pairing bug.

Re-measured for v2.2.0 (2026-10-16). The v2.2.0 campaign re-ran every cell on this page as its control generation, at a8a16991, with the shipped defaults and the same 20 seeds. Every ##RUN## row is byte-identical to the cells this page cites except AODV's, which differ through stock AODV's pointer-keyed socket map on multi-interface nodes (#362); its paired deltas are within noise. No default moved, so the numbers here stand as v2.2.0's. The control cells are results/cells/leo-*-v220.txt. What the campaign's four knobs do on this family is in network-regimes.md §7.

On this suite the campaign's sinr arm is not a no-op. The ISLs have no PHY SINR, but the ground-to-satellite access links are wireless and report it. On starlink1 it buys +0.7 pp of PDR for +49 NRL. The faster hello (hello500) gives the campaign's largest PDR gain, +2.6 to +6.5 pp, at NRL +76 to +550, which roughly doubles the routing load. repair200 never binds (byte-identical). reconv1s is noise on walker16; on the Starlink shells it trades about +100 to +120 ms of delay99 for at most +0.8 pp.

The cells

All cells: 4 ground-to-ground CBR flows (2 kbit/s each) between six cities (New York, London, São Paulo, Johannesburg, Tokyo, Sydney), 300 s, minimum elevation 25°, link delays re-read from ECEF distance every 0.1 s, ISL failure overlay at 1 % (mean outage 2 s), 10 Mbps links. The handover clock fires at second 12 of every 15 s — the Starlink reconfiguration interval measured by Tanveer et al. (WWW 2024), and the phase the LENS trace below confirms.

cell shell what it adds
walker16 16 planes × 16 sats, 1150 km, 53°, phasing 1 a small Walker-delta shell: 256 satellites, long hops
starlink1 72 × 22, 550 km, 53°, phasing 39 — Starlink shell 1 the real shell size: 1584 satellites, ~25-hop paths
starlink1-storm as starlink1 a 10 % mass satellite failure at t = 150 s

Arms:

  • anthocnet — this repository, defaults.
  • aodv — stock ns-3 AODV, the reactive baseline every other suite uses. A MANET protocol here, so it leaves the LEO cells from the next campaign (ADR-0026); its v2.3.0 rows stay as measured.
  • olsr — retired from v2.3.0 on. Stock ns-3 OLSR, the proactive link-state baseline, ran on walker16 only; its v2.2.0 measurement is kept in the dated tables below. It was a terrestrial sanity row, which AODV and the two oracles already provide (satellite-routing-prior-art.md §2.2), and at 20 seeds its walker16 run no longer fits the 6 h job limit. The proactive link-state slot goes to the comparator the field actually proposes: predictability-aware link state (OPSPF, #707; the opspf arm below).
  • geo-greedy — an idealised greedy geographic comparator: each hop goes to the neighbour nearest the destination's serving satellite, with perfect global position knowledge and no control traffic. It stands for the position-based family the satellite literature proposes, at its best case.
  • oracle — the precomputed shortest-hop control (#196), re-solved every 1 s (the oracle module's default RecomputeInterval). A delivery bound, not a delay bound: one hop count covers links of very different lengths.
  • oracle-delay — the same oracle with Metric=delay: Dijkstra on current channel delays, re-solved every 0.1 s on walker16 (oracleInterval=0 follows --delayUpdate) and every 1 s on S1 to keep CI time bounded. This is the latency bound, and the Hypatia-style control.

OLSR ran on walker16 only, through v2.2.0. On S1's 1584 nodes its topology flooding is the practical limit of the harness, not of the substrate (ADR-0022, criterion 5): even the 256-node walker16 cell took 5 h 54 min of CI for 20 seeds, and its v2.3.0 re-run exceeded the 6 h limit twice.

Restated at v2.4.0: family-native baselines (ADR-0026)

Current numbers: v2.4.0. From v2.4.0 every family is compared against its own protocols plus its oracle (ADR-0026). These cells are the v2.4.0 control campaign at the shipped defaults (no default moved), 20 seeds, mean ± 95 % t-interval. AntHocNet's rows and every kept baseline row are byte-identical to v2.3.0's. The blocks below this one are the dated record of their campaigns; their migrant rows are retired, not deleted.

Native set: AntHocNet, OPSPF (#707), geo-greedy, the hop oracle and the delay oracle. Retired: AODV (OLSR was retired at v2.3.0).

walker16

protocol PDR % ± 95 % CI delay99 ms NRL
AntHocNet 92.74 ± 0.39 141.6 97.66
OPSPF 98.90 ± 1.12 123.5 498.69
geo-greedy 41.35 ± 0.19 417.3 0.00
oracle 98.35 ± 0.12 123.2 0.00
oracle-delay 99.79 ± 0.03 103.2 0.00

AntHocNet − oracle -5.62 [-6.01, -5.22] pp; against the best native baseline (opspf, 98.90 %): dPDR -6.17 [-7.42, -4.91] pp, d delay99 +18.10 [+9.25, +27.60] ms, d NRL -401.03 [-411.70, -390.35] (PAIRED-MIXED).

protocol PDR % ± 95 % CI delay99 ms NRL
AntHocNet 79.34 ± 0.53 214.1 973.56
OPSPF 96.82 ± 1.26 132.2 19399.35
geo-greedy 3.81 ± 0.01 27.9 0.00
oracle 95.27 ± 0.34 132.1 0.00
oracle-delay 94.92 ± 0.22 117.5 0.00

AntHocNet − oracle -15.94 [-16.64, -15.23] pp; against the best native baseline (opspf, 96.82 %): dPDR -17.48 [-19.08, -15.88] pp, d delay99 +81.95 [+75.50, +88.25] ms, d NRL -18425.79 [-18691.83, -18159.75] (PAIRED-MIXED).

protocol PDR % ± 95 % CI delay99 ms NRL
AntHocNet 79.17 ± 0.97 209.5 934.18
OPSPF 96.12 ± 2.43 134.8 16356.53
geo-greedy 3.60 ± 0.48 27.4 0.00
oracle 95.08 ± 0.29 134.2 0.00
oracle-delay 94.95 ± 0.26 126.8 0.00

AntHocNet − oracle -15.91 [-16.80, -15.01] pp; against the best native baseline (opspf, 96.12 %): dPDR -16.94 [-19.27, -14.61] pp, d delay99 +74.75 [+70.35, +79.70] ms, d NRL -15422.35 [-15955.05, -14889.66] (PAIRED-MIXED).

The headline satellite comparison is AntHocNet − OPSPF, and it goes against AntHocNet: −6.17 pp on walker16, −17.48 and −16.94 pp on Starlink S1 and its storm. OPSPF pays 5–20× AntHocNet's control for it (NRL 499 against 98; 16 357–19 399 against 934–974). Its LSA floods are also accounted, not transmitted. The opspf section lists that and its other simplifications. OPSPF also sits at or above the 1 s hop oracle (+0.55, +1.54 and +1.04 pp), so on these failure cells the oracle is not a delivery bound (#720).

Restated at v2.3.0 defaults (quiet mode on)

Dated record: v2.3.0 defaults (superseded by the v2.4.0 block above). v2.3.0 turns quiet mode on by default (#571, the v2.3.0 campaign). AntHocNet's rows below are the campaign's quiet arm. Every baseline row is the campaign's control, and the attribution control found those identical to the v2.2.0 corpus. 20 seeds, paired per seed; a bold difference is one whose 95 % t-interval excludes zero.

Quiet mode's effect here, paired against v2.2.0's AntHocNet: noise on PDR, delay99 and NRL on all three shells.

Provenance. The campaign's runs at main, 54cdfae7..cc230126. Cells: the per-arm cells results/cells/leo-<shell>-<arm>-v230.txt, with AntHocNet from leo-<shell>-anthocnet-v230-quiet.txt. Tables recomputed from the cells' ##RUN## rows (t-intervals; delay99 by bootstrap). Stock AODV's rows are the v2.3.0 control's. On these multi-interface nodes they differ from v2.2.0's in iteration order only (#362, #675), and the paired difference is within noise. OLSR is no longer a LEO arm (decision 2026-10-18, see the arm list); its last measurement, v2.2.0's walker16 cell, stays in the dated tables below.

walker16

protocol PDR % delay99 (ms, bootstrap) NRL
anthocnet 92.74 ± 0.39 141.6 [132.8, 150.9] 97.66 ± 1.79
aodv 78.40 ± 2.14 125.0 [121.2, 128.9] 91.57 ± 2.54
geo-greedy 41.35 ± 0.19 417.3 [396.9, 432.2] 0.00 ± 0.00
oracle 98.35 ± 0.12 123.2 [123.0, 123.5] 0.00 ± 0.00
oracle-delay 99.79 ± 0.03 103.2 [103.0, 103.6] 0.00 ± 0.00
AntHocNet minus ΔPDR (pp) Δdelay99 (ms) ΔNRL
aodv +14.34 [+12.11, +16.57] +16.6 [+5.5, +27.6] +6.10 [+2.99, +9.20]
geo-greedy +51.39 [+51.00, +51.78] -275.8 [-298.3, -253.2] +97.66 [+95.87, +99.45]

Starlink S1

protocol PDR % delay99 (ms, bootstrap) NRL
anthocnet 79.34 ± 0.53 214.1 [207.7, 220.4] 973.56 ± 30.75
aodv 52.77 ± 1.29 1653.0 [1428.8, 1855.0] 877.62 ± 21.56
geo-greedy 3.81 ± 0.01 27.9 [27.0, 28.9] 0.00 ± 0.00
oracle 95.27 ± 0.34 132.1 [132.0, 132.2] 0.00 ± 0.00
oracle-delay 94.92 ± 0.22 117.5 [116.7, 118.5] 0.00 ± 0.00
AntHocNet minus ΔPDR (pp) Δdelay99 (ms) ΔNRL
aodv +26.57 [+25.09, +28.05] -1438.9 [-1673.0, -1204.8] +95.93 [+56.79, +135.08]
geo-greedy +75.53 [+75.00, +76.06] +186.2 [+178.7, +193.7] +973.56 [+942.80, +1004.31]

Starlink S1 + 10 % mass failure

protocol PDR % delay99 (ms, bootstrap) NRL
anthocnet 79.17 ± 0.97 209.5 [205.3, 214.2] 934.18 ± 29.10
aodv 47.03 ± 1.94 1846.5 [1626.8, 2045.3] 1002.21 ± 48.29
geo-greedy 3.60 ± 0.48 27.4 [26.6, 28.4] 0.00 ± 0.00
oracle 95.08 ± 0.29 134.2 [133.2, 135.7] 0.00 ± 0.00
oracle-delay 94.95 ± 0.26 126.8 [125.3, 128.4] 0.00 ± 0.00
AntHocNet minus ΔPDR (pp) Δdelay99 (ms) ΔNRL
aodv +32.14 [+30.21, +34.07] -1637.0 [-1865.7, -1408.4] -68.03 [-125.88, -10.18]
geo-greedy +75.57 [+74.45, +76.69] +182.1 [+176.9, +187.2] +934.18 [+905.08, +963.28]

The blocks below are the dated v2.2.0 record (quiet mode off). Their baseline rows are unchanged; where AntHocNet's numbers disagree with this block, this block is current. Diagnostic tables below (drop causes, paths, handovers, churn) describe the v2.2.0 runs.

Headline

Moving LEO constellation: PDR, packets lost per scheduled handover and delay99 per arm, with the delay oracle as a reference line

Reproduce: make reproduce FIG=constellation re-renders it from the committed data and diffs the PNG; MODE=rerun NS3DIR=… re-measures a cell first (#621).

  • AntHocNet beats AODV on every cell, by more as the shell grows: +15.5 pp PDR on walker16, +28.2 pp on Starlink S1, +32.3 pp under the S1 storm — all 20 of 20 seeds, Wilcoxon p = 1.9e-06 — with lower mean delay on every cell.
  • The handover is where the difference lives. AntHocNet loses 9.4–11.2 packets per scheduled handover; AODV 34.5–57.7. The oracles, which re-solve on the new topology instantly, lose ~0.
  • The gap to the bound grows with path length. AntHocNet is 7.0 pp under the delay oracle on walker16 (~9 hops) and 15.5 pp under it on S1 (~25 hops). That is the residual a 25-hop ant has to cover between two 15 s reconfigurations.
  • Greedy geographic forwarding is not a viable comparator on a Walker grid, even idealised — see the geo-greedy verdict.

Tables below are the output of python3 ns3/tools/leo-summary.py walker16 starlink1 starlink1-storm, pasted unedited; rerun it on the committed cells to reproduce every number.

walker16

arm seeds PDR % mean delay (ms) delay99 (ms, boot) NRL refused at source %
anthocnet 20 92.84 ± 0.40 67.33 ± 1.18 140.5 [131.4, 150.9] 97.63 ± 1.69 0.00
aodv 20 77.34 ± 2.58 71.97 ± 0.95 125.4 [123.0, 128.6] 92.84 ± 3.24 0.00
olsr 20 83.88 ± 0.53 67.37 ± 0.47 124.7 [123.5, 125.8] 306.59 ± 1.87 2.91
geo-greedy 20 41.35 ± 0.19 76.91 ± 2.07 417.3 [396.9, 432.2] 0.00 ± 0.00 0.00
oracle (bound) 20 98.35 ± 0.12 69.85 ± 0.09 123.2 [123.0, 123.5] 0.00 ± 0.00 0.00
oracle-delay (bound) 20 99.79 ± 0.03 61.56 ± 0.05 103.2 [103.0, 103.6] 0.00 ± 0.00 0.00

AntHocNet − arm, paired per seed (95 % t-CI, Wilcoxon p):

vs n Δ PDR (pp) Δ mean delay (ms)
aodv 20 +15.50 [+12.91, +18.09], p=1.9e-06 -4.64 [-6.20, -3.07], p=1.3e-05
olsr 20 +8.96 [+8.36, +9.56], p=9.6e-05 -0.04 [-1.34, +1.26], p=0.94
geo-greedy 20 +51.50 [+51.06, +51.94], p=9.6e-05 -9.57 [-12.09, -7.05], p=3.8e-06

Handover metric family (sums over seeds; outage = ≥ 3 consecutive lost send attempts):

arm startup n / lost scheduled n / lost unplanned n / lost massfail n / lost other n / lost lost per scheduled handover median outage, scheduled (s) median outage, unplanned (s)
anthocnet 1 / 3 257 / 2062 649 / 4329 0 / 0 10 / 74 9.37 2.00 1.75
aodv 2 / 17 232 / 12695 509 / 7256 0 / 0 106 / 1223 57.70 3.25 2.25
olsr 80 / 2789 259 / 6009 551 / 5685 0 / 0 55 / 543 27.31 5.25 2.12
geo-greedy 80 / 41274 41 / 13236 75 / 998 0 / 0 0 / 0 60.16 92.00 2.12
oracle 0 / 0 1 / 4 252 / 830 0 / 0 4 / 15 0.02 1.00 0.75
oracle-delay 0 / 0 0 / 0 0 / 0 0 / 0 0 / 0 0.00 — —
arm hop changes / flow / min mean hops (delivered)
anthocnet 2.07 9.29
aodv 0.76 9.05
olsr 0.83 8.65
geo-greedy 0.84 6.33
oracle 1.02 8.72
oracle-delay 1.08 8.76

AODV's and OLSR's lower delay99 on walker16 (125 vs 140 ms) is survivorship: they deliver 15.5 and 9.0 pp fewer packets, and the ones they drop are the ones a detour would have made late.

OLSR, the proactive baseline, sits between AODV and AntHocNet. AntHocNet delivers +8.96 pp more (20 of 20 seeds, p = 9.6e-05) at the same mean delay (−0.04 ms, p = 0.94), with a third of OLSR's routing overhead (NRL 97.6 vs 306.6). OLSR loses 27.3 packets per scheduled handover — half of AODV's 57.7, three times AntHocNet's 9.4 — consistent with its topology view lagging each 15 s reconfiguration by its TC interval. It is also the only arm that refuses packets at the source (2.91 %: no route yet), and every one of its 80 flows (4 × 20 seeds) opens with a startup outage while the first topology floods converge.

starlink1

arm seeds PDR % mean delay (ms) delay99 (ms, boot) NRL refused at source %
anthocnet 20 79.43 ± 0.41 84.61 ± 1.18 210.2 [205.1, 215.7] 955.45 ± 17.11 0.00
aodv 20 51.23 ± 1.18 108.48 ± 5.40 1644.1 [1461.1, 1785.0] 904.16 ± 24.48 0.00
geo-greedy 20 3.81 ± 0.01 25.29 ± 0.45 27.9 [27.0, 28.9] 0.00 ± 0.00 0.00
oracle (bound) 20 95.27 ± 0.34 78.66 ± 0.11 132.1 [132.0, 132.2] 0.00 ± 0.00 0.00
oracle-delay (bound) 20 94.92 ± 0.22 74.89 ± 0.07 117.5 [116.7, 118.5] 0.00 ± 0.00 0.00

AntHocNet − arm, paired per seed (95 % t-CI, Wilcoxon p):

vs n Δ PDR (pp) Δ mean delay (ms)
aodv 20 +28.20 [+26.90, +29.50], p=1.9e-06 -23.86 [-28.97, -18.75], p=1.9e-06
geo-greedy 20 +75.62 [+75.21, +76.03], p=9.6e-05 +59.32 [+57.97, +60.68], p=1.9e-06

Handover metric family (sums over seeds; outage = ≥ 3 consecutive lost send attempts):

arm startup n / lost scheduled n / lost unplanned n / lost massfail n / lost other n / lost lost per scheduled handover median outage, scheduled (s) median outage, unplanned (s)
anthocnet 0 / 0 592 / 5958 1573 / 11779 0 / 0 100 / 538 9.93 2.25 1.75
aodv 6 / 820 399 / 20705 874 / 16701 0 / 0 276 / 7453 34.51 5.12 2.25
geo-greedy 80 / 83762 40 / 7353 0 / 0 0 / 0 0 / 0 12.26 47.00 —
oracle 2 / 7 7 / 27 693 / 2354 0 / 0 17 / 66 0.04 1.00 0.75
oracle-delay 1 / 4 7 / 25 782 / 2671 0 / 0 22 / 77 0.04 1.00 0.75
arm hop changes / flow / min mean hops (delivered)
anthocnet 8.71 25.60
aodv 1.86 20.70
geo-greedy 1.37 4.14
oracle 2.82 23.75
oracle-delay 2.83 24.23

On S1, AntHocNet's control overhead (NRL 955) is in the same range as AODV's (904), on a 1584-node shell — the cost question #207 asked. It pays for that with 4.7× the hop-change rate of AODV: ants keep moving traffic to the currently-shortest path, which is what the delay result shows. The oracle arms' "unplanned" outages are the 1 s re-solve interval on S1 meeting the 1 % ISL failure overlay — a cost of the bound's sampling, not of routing.

starlink1-storm

arm seeds PDR % mean delay (ms) delay99 (ms, boot) NRL refused at source %
anthocnet 20 78.79 ± 1.04 90.04 ± 1.95 212.8 [208.1, 217.4] 957.42 ± 63.39 0.00
aodv 20 46.49 ± 2.39 111.82 ± 5.71 1704.0 [1546.8, 1841.3] 1018.31 ± 63.81 0.00
geo-greedy 20 3.60 ± 0.48 25.48 ± 0.63 27.4 [26.6, 28.4] 0.00 ± 0.00 0.00
oracle (bound) 20 95.08 ± 0.29 81.75 ± 1.34 134.2 [133.2, 135.7] 0.00 ± 0.00 0.10
oracle-delay (bound) 20 94.95 ± 0.26 78.51 ± 1.29 126.8 [125.3, 128.4] 0.00 ± 0.00 0.10

AntHocNet − arm, paired per seed (95 % t-CI, Wilcoxon p):

vs n Δ PDR (pp) Δ mean delay (ms)
aodv 20 +32.30 [+30.03, +34.57], p=1.9e-06 -21.78 [-27.29, -16.27], p=1.9e-06
geo-greedy 20 +75.19 [+73.94, +76.44], p=1.9e-06 +64.56 [+62.35, +66.77], p=1.9e-06

Handover metric family (sums over seeds; outage = ≥ 3 consecutive lost send attempts):

arm startup n / lost scheduled n / lost unplanned n / lost massfail n / lost other n / lost lost per scheduled handover median outage, scheduled (s) median outage, unplanned (s)
anthocnet 0 / 0 612 / 6610 1528 / 11347 42 / 354 85 / 510 11.15 2.25 1.75
aodv 6 / 410 378 / 21513 716 / 14792 18 / 5404 297 / 8116 36.28 5.62 2.50
geo-greedy 80 / 84112 38 / 7184 2 / 14 0 / 0 0 / 0 12.11 47.00 1.75
oracle 3 / 10 28 / 124 714 / 2428 0 / 0 11 / 42 0.21 1.00 0.75
oracle-delay 1 / 4 23 / 99 776 / 2613 0 / 0 19 / 68 0.17 1.00 0.75
arm hop changes / flow / min mean hops (delivered)
anthocnet 9.22 26.64
aodv 1.79 20.37
geo-greedy 0.94 5.16
oracle 3.39 24.47
oracle-delay 3.59 24.81

The mass failure costs AntHocNet 0.6 pp of PDR and costs AODV 4.7 pp. The outage table says why: AntHocNet's 42 mass-failure outages lost 354 packets in total (≈ 8 each, a few seconds of traffic); AODV's 18 lost 5404 (≈ 300 each — the flow stays black until a route error finally reaches the source and a fresh discovery succeeds).

Calibration

The model is circular orbits on a spherical Earth with propagation-only links (ADR-0022), so calibration asks one thing: are the delays and the reconfiguration timing the right size? Two external references answer it.

Hypatia (Kassing et al., IMC 2020, Fig. 13) publishes the Paris–Luanda RTT over 200 s on Starlink S1 (72 × 22, 550 km, 53°, minimum elevation 25°, path re-solved every 100 ms): 85–117 ms. The hypatia cell reproduces that setup — --pairs=hypatia, the delay oracle re-solved every 0.1 s, 1000 s, one flow.

Paris–Luanda RTT on Starlink S1 under the delay oracle, with Hypatia's published 85–117 ms band

Reproduce: make reproduce FIG=calibration-hypatia re-renders it from the committed data and diffs the PNG (#621).

min p25 p50 p75 p95 max
leo-walker RTT (ms) 54.8 77.2 101.0 140.3 186.4 207.5
  • The median sits inside Hypatia's band (101.0 ms), and 33.5 % of the seconds fall inside 85–117 ms. Mean path length is 20.8 hops.
  • The spread is wider than Hypatia's, and the cause is known: leo-walker gives each ground station one serving satellite (the highest one) and hands over on the 15 s clock; Hypatia lets the station use any visible satellite and picks whichever minimises the end-to-end path. The plateaus in the chart are exactly the 15 s-to-minutes stretches where the serving satellite on one end is a poor entry point into the grid (high plateaus) or an unusually good one (low plateaus, under Hypatia's floor). The minimum (54.8 ms) stays above the 43.4 ms great-circle-at-c line; nothing is faster than physics.
  • What this means for the routing results: the single-serving-satellite GSL makes every arm's paths longer than an any-visible-satellite model would, by the same geometry for every arm. It moves absolute delays; it does not favour an arm.

LENS (Zhao & Pan, MMSys 2024; dataset CC BY-SA 4.0) publishes raw 10 ms ping traces from Starlink dishes to their point of presence. One full day of the bruhl dish (Brühl → Frankfurt PoP, 2026-10-06, 24 hourly traces, 5.96 M pings) summarised by ns3/tools/lens-calibration.py; derived numbers only, in results/cells/lens-bruhl-20261006.txt:

min p5 p50 p95 p99 loss
LENS bruhl RTT (ms) 11.4 16.0 20.8 33.6 47.2 0.186 %
  • Delay: a dish-to-PoP RTT is one bent-pipe hop (dish → satellite → gateway, and back). At 550 km and 25° minimum elevation the propagation-only floor for that is 7.3–15.0 ms; LENS's 11.4 ms minimum and 20.8 ms median are that floor plus scheduling and queueing that leo-walker does not model. The model's propagation is the right size; its RTTs are lower bounds.
  • Timing — the 15 s clock is confirmed in the wild: binning loss by UTC second mod 15, LENS loss peaks at phases 11 and 12 (0.399 % and 0.230 % against a ~0.16 % baseline on every other phase), and median RTT steps up at phase 12 (21.2 ms against 20.6–20.9). leo-walker's handover clock fires at phase 12 (clock=15 clockOffset=12). The scheduled-handover class in the tables above is placed where the real network puts it.

--protocols=opspf is OPSPF-style link state on the predicted orbit schedule (Pan et al., "OPSPF: Orbit Prediction Shortest Path First Routing for Resilient LEO Satellite Networks", IEEE ICC 2019). It is the comparator the satellite literature actually proposes (satellite-routing-prior-art.md §2.2). It runs on the oracle module's machinery in Knowledge=predicted mode (oracle README).

What it knows, and when. - Predicted changes: the GSL handovers, scheduled or forced. Every node knows them the moment they happen, as the oracle does. The ISL plan never changes. - Unpredicted changes: --islDown failures and repairs, and the mass failure's lost links. These reach a node only when an LSA flood arrives there. Each surviving endpoint of a changed ISL detects it at once (loss of carrier; there is no hello, so no dead interval) and floods one router-LSA. A storm victim's neighbours each flood one LSA listing every link they lost. - Flood timing: the flood travels over the links that are physically up, so a node learns at origin + its fastest path's propagation delay. - Routing view: each node routes on its own view, so nodes the flood has not reached keep routing into the failure.

Its control traffic is the flood. Each node forwards an LSA once on every up link but the one it first heard it on; the originator sends on all of them. Every one of those transmissions counts in NRL. Each is 120 bytes in NRL bytes: one OSPF Link State Update with one router-LSA for 4 ISLs (RFC 2328 §A.3.5, A.4.1, A.4.2).

What is simplified, and which way it leans: - The LSAs are accounted, not sent. They take no ISL capacity. Measured load on S1: 17.9 M LSA transmissions × 120 B over 60 s across 6336 directed ISLs, ~45 kbit/s per 10 Mbit/s link, or 0.45 %. - Detection is instant. - There is no MinLSInterval rate limit (RFC 2328: 5 s). A flapping link floods on every change.

The first two favour the arm. The third inflates its NRL and also makes it learn fastest.

Correctness check (the issue's acceptance). With no unpredicted failure, OPSPF is the hop oracle: it differs only in what it can know. tools/checks/check-leo-walker.sh asserts that the failure-free per-seed ##RUN## rows of oracle and opspf are identical field for field. scenario_check.py results FAILs an opspf run that flooded in a failure-free cell, or one that never flooded in a failure cell.

Measured while building it (ns-3.48, short runs, not the published cells):

cell opspf oracle (1 s) AntHocNet opspf NRL
walker16, islDown=0.01, 120 s, 2 seeds 99.84 / 100.00 % 97.59 / 97.84 % 93.03 / 91.79 % 517 / 522
Starlink S1, islDown=0.01, 60 s, 1 seed 100.00 % 94.59 % — 19 815
S1 storm, 300 s, 1 seed 98.46 % 95.17 % — 15 749

Two readings follow, and both change how this page reads.

  1. The hop oracle is not a delivery bound on failure cells. At its 1 s cadence it routes into a dead ISL for up to a second. The flood reaches every node in milliseconds, so opspf out-delivers it by 2–5 pp. Tracked in #720. Until that is decided, read the oracle's delivery on failure cells as "within its 1 s staleness".
  2. OPSPF pays for that delivery in flooding. Every ISL change floods the whole shell. That is the reconvergence cost the OPSPF paper exists to reduce for predicted changes, and it cannot avoid it for unpredicted ones. The overhead comparison against AntHocNet is therefore as much a part of the headline as delivery.

The 20-seed opspf cells on walker16, Starlink S1 and the storm are measured in the v2.4.0 control campaign. AntHocNet − OPSPF, paired, becomes this page's headline comparison then.

The geo-greedy verdict

Greedy geographic forwarding — the family behind most "position-based LEO routing" proposals — is given every advantage here: perfect global positions, zero control traffic, a forced uplink at the ground station, and the destination's serving satellite (not the city) as the target. It still delivers 41 % on walker16 and under 4 % on Starlink S1.

The failure is structural, not tuning. On a Walker +grid, a satellite's four neighbours sit fore/aft in its own plane and left/right in the adjacent planes. Reaching a destination across planes needs, at some point, a hop that does not reduce Euclidean distance to the target — the grid is a lattice wrapped on a sphere, and the greedy-progress invariant has local minima everywhere planes cross. The 80 "startup" outages per cell are every flow on every seed (4 × 20) beginning inside a local minimum — stuck for a median ~200 s on walker16 and for essentially the whole 300 s run on S1, until satellite motion happens to carry the flow's entry point out of it. Face/perimeter routing, the planar-graph repair that makes greedy forwarding complete in 2-D, has no 3-D equivalent with the same guarantee. geo-greedy's low delay on S1 (25 ms) is survivorship: the only flows it delivers are the short ones.

The cross-plane pairing bug and the superseded runs

The first leo-walker paired each satellite with the same slot in the neighbouring plane. With Walker phasing, the same slot in the next plane is not the nearest satellite there: on S1, same-slot cross links averaged 1470.7 km against 636.9 km for the best constant shift, and the Hypatia calibration came out near 274 ms — almost 3× the published range. An independent Python model of the shell reproduced the 3×, and the fix (0b5a65ad) picks one constant slot shift for the whole shell — the one that minimises the mean cross-link length at t = 0 — with the seam plane pairing to its nearest satellite.

Each cell's # anchor isl line records the choice, and the CI smoke (tools/checks/check-leo-walker.sh) asserts it is no longer on average than same-slot pairing and shorter than the in-plane chord:

shell chosen shift (slots) cross mean same-slot mean in-plane chord
walker16 15 (≡ −1) 1838.4 km 2527.3 km 2934.5 km
starlink1 21 (≡ −1) 636.9 km 1470.7 km 1969.9 km

Every leo-walker cell measured before 0b5a65ad was re-run and is superseded; none was published. The longer cross links had made every arm's paths longer by the same geometry, so the arm ranking did not change — but the absolute delays and the calibration did, and only the re-run numbers are on this page.

Threats to validity

  • Circular orbits, spherical Earth. The stock ns-3.48 model; no J2, no eccentricity, no TLEs. Results are about routing over Walker geometry and delay, not orbital precision (ADR-0022).
  • Propagation-only links. ISL and GSL delays are distance / c; no PHY, no weather, no queueing at the gateway. RTTs are lower bounds (the LENS comparison above).
  • Single serving satellite per station. The main cause of the wider Hypatia spread; it lengthens every arm's paths alike.
  • The oracle bounds are sampled, not continuous. The hop oracle re-solves every 1 s everywhere; the delay oracle every 0.1 s on walker16 and every 1 s on S1, to keep the 20-seed CI run bounded. Their "unplanned" outages are that sampling meeting the ISL failure overlay.
  • AODV is order-dependent in ns-3 (#362): stock AODV keys socket maps on heap addresses. Each arm runs alone in its own dispatch here, so no arm's numbers depend on another's.
  • 10 Mbps links, 2 kbit/s flows. The load is light on purpose — this is a reachability-under-motion test, not a capacity one. Congestion on ISLs is #206's question.
  • No OLSR from v2.3.0, and never on S1. Proactive link-state flooding on 1584 nodes exceeds the CI budget; walker16 alone took 5 h 54 min for 20 seeds in v2.2.0 and exceeded 6 h in v2.3.0. The arm is retired; OPSPF (#707) is the link-state comparator to add.

Reproduce

# one arm of one cell, locally in the 3.48 image (S1, 20 seeds)
./ns3 run "leo-walker --csv --protocols=anthocnet --planes=72 --sats=22 --phasing=39 \
  --altitude=550 --inclination=53 --minElevation=25 --time=300 --runs=20 \
  --islDown=0.01 --oracleInterval=1"

# the tables on this page, from the committed cells
python3 ns3/tools/leo-summary.py walker16 starlink1 starlink1-storm

# the charts
python3 ns3/tools/family-charts.py

In CI: benchmark-cell.yml, harness=leo-walker, version=3.48, one arm per dispatch, with the cell's flags in args (a campaign plan lists them; before v2.5.0 this was satellite-benchmark.yml, #746).