Revisit TimeLEO constellation coverage calculator

Validation

Checking the model against something other than itself

A tool that agrees with itself has proved nothing. Every quantity below is computed twice, by methods that share no code, and the two answers are put side by side. The comparison runs on every build and fails it on disagreement.

The independent implementations live in a separate module that imports nothing from the engine, restates the physical constants rather than sharing them, and reaches each answer by a different route: closed forms replaced by numerical solutions, composed rotations replaced by explicit matrices, a fixed time grid replaced by random sampling. Where an external fact exists, such as a published orbital period, the check is anchored to that instead.

This is not a comparison against full-fidelity tooling. It shows the arithmetic is right, not that the model is complete: there is still no drag, no beam patterns and no capacity, and the method page lists what is left out. Two of those simplifications are no longer only listed but measured, in section 7, and the one that turns out to matter was not on the list at all. Section 8 goes further and replaces the assumption underneath it with a measurement, taken from a public catalogue rather than chosen here.

1. Orbital period, against published values

The engine works in mean motion. This computes the period from Kepler's third law and compares it with the figures published for circular orbits at these altitudes.

AltitudeKeplerPublishedDifference
420 km92.82 min92.8 min0.02 min
550 km95.50 min95.6 min0.10 min
780 km100.30 min100.4 min0.10 min
1200 km109.27 min109.4 min0.13 min

2. Coverage half-angle, algebra against arithmetic

The engine uses a closed form. This solves the same question by placing a satellite and a site as three-dimensional vectors, computing the elevation between them directly, and bisecting on the separation until the elevation equals the mask. No half-angle formula is involved.

CaseClosed formBisectionDifference
400 km, mask 10°12.084592°12.084592°below 1e-6°
520 km, mask 25°8.079178°8.079178°below 1e-6°
780 km, mask 25°11.152305°11.152305°below 1e-6°
1200 km, mask 40°9.862304°9.862304°below 1e-6°
1500 km, mask 5°31.259466°31.259466°below 1e-6°

3. The geometric horizon

With a zero mask the coverage circle reduces to the geometric horizon, where the line of sight is tangent to the sphere. That has its own short closed form, which the general expression has to reproduce exactly.

AltitudeGeneral formula at 0°Tangent horizonDifference
420 km20.256273°20.256273°exact
520 km22.401019°22.401019°exact
800 km27.322189°27.322189°exact
1400 km34.930919°34.930919°exact

4. Coverage fraction, fixed steps against random sampling

The engine measures coverage on a fixed grid of times, and a fixed grid can alias against a periodic system. This estimates the same quantity from 20,000 times and longitudes drawn at random, using the independent propagator. Agreement inside the sampling error means the step is not biasing the answer.

ConfigurationEngineRandom samplingDifferenceSampling error
12 sats, 520 km, 53°, lat 50°13.70%13.69%0.00 pp±0.48 pp
48 sats, 520 km, 53°, lat 50°54.07%54.27%0.20 pp±0.69 pp
22 sats, 550 km, 97.5°, lat 60°30.57%30.70%0.13 pp±0.64 pp

Sampling error is the 95% confidence half-width for a proportion at this sample size. Random draws use a seeded generator, so the figures are reproducible.

5. Satellite positions

The engine composes its rotation inline; the independent version multiplies named matrices in the standard sequence. Across 144 sampled positions spanning three altitudes, four planes and a range of times, the largest disagreement is below one nanometre, which is floating-point noise rather than a difference.

6. How much the time step costs

Not a cross-check but a measurement. The site quotes figures sampled at 20 second steps and states they are stable to roughly one step. This computes a reference at 5 seconds and shows what each coarser step actually does to the answer, so that claim is measured rather than asserted.

FleetStepWorst outageError against 5 s reference
12 sats10 s1750 s+5 s
20 s1760 s+15 s
40 s1760 s+15 s
60 s1800 s+55 s
48 sats10 s340 snone
20 s340 snone
40 s360 s+20 s
60 s360 s+20 s

The highlighted rows are the step the site publishes.

7. What the simplifications cost

Naming a simplification is not the same as knowing its size. The secular part of J2, the Earth's equatorial bulge, is closed form, so that figure can be measured here rather than waited for. The implementation is anchored to something outside this codebase: a sun-synchronous orbit is defined by its nodal regression matching the Earth's progress around the Sun, 0.9856 degrees a day, and fed a published sun-synchronous inclination this returns 0.9879.

The answer is not the one the page has been implying. J2 costs almost nothing. Every plane at the same altitude and inclination regresses at the same rate, so the shell turns as a body and its internal geometry is untouched, and these figures are worst-cased across longitudes anyway. At 520 km and 53 degrees the plane regresses -4.56 degrees a day and the answer does not move.

What does move it is an assumption stated nowhere until now: that every satellite sits at exactly the same altitude. Satellites at different altitudes have different orbital periods, drift out of their designed phasing, and the effect is large.

ConfigurationEngineWith J2J2 costs±5 km spread costs
12 satellites, 520 km, 53°29.3 min29.3 min0%0%
48 satellites, 520 km, 53°5.7 min5.7 min0%112%
90 satellites, 550 km, 53°60 s60 s0%367%

The last column holds J2 out entirely and varies only how tightly the planes share an altitude, so the two causes are not confused with each other.

Sensitivity rises with how good the answer is, which is the uncomfortable part. A small fleet does not notice, because a few degrees of relative drift is lost inside gaps that are already long. A dense shell loses continuous service outright, because continuity is a property of a seam that is exactly closed, and nothing exactly closed survives being nudged.

Altitude dispersionWorst outage at 90 satellitesChange
uniform, as modelled60 s0%
±3 m measured, Iridium pattern60 s0%
±42 m measured, Globalstar pattern60 s0%
±500 m80 s33%
±1.0 km1.7 min67%
±2.0 km2.7 min167%
±5.0 km4.7 min367%
±10 km4.7 min367%
±24 km measured, OneWeb pattern4.7 min367%

The effect saturates: once the designed phasing is scrambled, scrambling it further changes nothing. The marked rungs are not assumptions. They are the dispersions of three real shells, measured in section 8 below, dropped onto the same 90 satellite configuration so they can be read against the values this page had been guessing at.

So the honest reading is not that these figures are wrong by a factor of four. It is that the last factor of two or three in a dense shell's headline number depends on station keeping this model does not represent. Treat a continuous result as the claim most in need of checking elsewhere, not the least.

Drag, beam patterns, link budgets and capacity are all still absent, and section 7 measures only two of the simplifications rather than all of them. Real ephemeris is absent from every figure above and from the calculator; where it is not absent, on the measured page, the page says so in its first line.

A comparison against full-fidelity tooling would still be worth having, but section 7 changes what it is worth having for. The plan was to run a benchmark case through GMAT with perturbations enabled and see how far the answer moved. That figure is now known to be 0 to 3%. The case worth running instead is a shell with realistic altitude dispersion and station keeping, which is where the movement actually is. Until then, treat everything here as geometry done correctly rather than as coverage predicted accurately.

8. Dispersion, measured rather than assumed

Section 7 leaves the most consequential row in the error budget resting on numbers I chose. Whether a real shell sits at the bottom of that ladder or off the top of it decides whether the row is a footnote or an alarm, and until now nothing here answered the question.

Element sets answer it without propagating anything. A catalogued object's mean motion gives its semi-major axis through Kepler's third law, and how much that varies across a shell is the quantity the model calls dispersion. Three of the four shapes this site publishes have a counterpart in the public catalogue, so three of them can be measured.

The data is the United States Space Force public catalogue, redistributed by CelesTrak, vendored into this project as a dated snapshot rather than fetched while a page is built. It is not operator data, it is not published or endorsed by anyone flying these satellites, and it records where objects are rather than whose they are or what they sell.

ShellCataloguedIn the shellPlanesMean altitudeSemi-major axis, sdAs ± dispersionPlane spacing
Iridium pattern80676784.8 km2 m±3 mone altitude
Globalstar pattern282481420.7 km24 m±42 mone altitude
OneWeb pattern651647121207.4 km14 km±24 km3.99 km apart

Snapshot 2026-09-08, retrieved 2026-09-08T19:46:53Z. Epoch age is measured against 2026-09-08T11:00:00Z, the instant this snapshot is pinned to, never against the clock of the machine that built the page. The ± column is the half-width whose even spread across planes has the measured standard deviation, which is the form the model takes its parameter in. Membership, in full:

  • Iridium pattern: 67 of 80 objects, excluding 6 semi-major axis far from the shell, 7 in a separate altitude population. Epoch age median 0.10 d, 95th percentile 0.63 d, oldest 0.69 d.
  • Globalstar pattern: 24 of 28 objects, excluding 4 semi-major axis far from the shell. Epoch age median 0.12 d, 95th percentile 0.47 d, oldest 0.68 d.
  • OneWeb pattern: 647 of 651 objects, excluding 1 orbit too eccentric for the shell, 2 semi-major axis far from the shell, 1 in a separate altitude population. Epoch age median 0.11 d, 95th percentile 0.65 d, oldest 1.14 d.

The answer is that it depends on the operator, by a factor of 7200. The Iridium pattern holds every satellite to within 8 m of a common semi-major axis, far below the smallest rung of the ladder, which means the uniform altitude this site assumes is not an approximation for that shell, it is what the shell does. The OneWeb pattern spreads its 12 planes deliberately across 44 km, 3.99 km apart, which is off the top of the ladder entirely.

So the honest reading of the error budget changes shape. The row is not "real shells are dispersed by about this much". It is a question to ask about a particular fleet: a shell that station keeps to one altitude is modelled here faithfully, a shell that separates its planes on purpose is not, and the distance between those two cases is larger than every other simplification on this page put together.

Does the model have the right shape as well as the right size?

Worth asking, because the model spreads altitude plane by plane on an even ramp, and the natural guess is that reality is scatter about a mean, in which case a measured magnitude fed into the wrong shape would be a calibration in name only. For the one dispersed shell here the guess is wrong and the model is right. The variation between planes is 31 times the variation inside them, and the planes sit 3.99 km apart with a spread of 51 m on that spacing. That is the model's ramp, flown.

What the ramp does not carry is the 466 m of scatter left inside each plane. Feeding the measured distribution back one satellite at a time rather than one plane at a time is a small addition to the same model, and what it costs is measured on the measured page. At the margin it moves the answer further, so treat the ramp as a floor rather than the whole story.

Two limits on all of this. A semi-major axis derived from a mean motion is a mean element rather than an instantaneous one, offset by a term of order J2 that is a few kilometres at these altitudes and is shared by every satellite in a shell, so it moves an altitude and leaves a spread alone. And this is one snapshot of three shells on one day: it says what those shells were doing then, not what every shell does, and a shell mid-deployment would look nothing like any of them.