Terminus: rings over twilight

Proposal section 4: the fleet

Ride along with one of our satellites for an orbit. It crests the north pole and falls down the night side, two hours of frozen blackness scrolling below, no lights, no one to serve. Then a thin red line rises over the horizon — the twilight band, glowing with the scattered lamps of every town on the planet — and for a quarter of an hour the satellite is working, relaying a thousand conversations, before the band slides behind it and the day side's empty glare takes over. Around the bottom of the world and back up into darkness. A satellite here spends most of its life commuting and a sliver of it on shift.

This section designs the constellation: how many such commuters we need, and on which roads. The survey gave us the shelf menu; the trap taught us the rings must stay fixed among the stars while the band sweeps past them. Now we lay actual rings in the actual sky — and then, because the RFP demands evidence rather than confidence, we simulate every town on the planet for a full rotation and see whether our first design holds.

It will not. That is what simulations are for.

Before we lay the first ring, here is the fleet we are about to build. Press Play on the model below and watch the six rings wheel slowly past the twilight band, each taking its turn as the ring that serves it. Much of it will not make sense yet — why the rings run over the poles, why so many satellites sit dark, why the fleet flies at 2,200 km and not lower. By the end of this section, every piece of it will have earned its place.

Twilight band Access wheel Duty ring Alpha Centauri A · B Drag to turn the view · scroll to zoom · right-drag to pan
Pole view
Speed 10 min/s
Mission clock T+ 0.00 d
Duty ring
Min visible
Mean visible
Footprint radius
Dwell
Edge latency

The baseline access fleet: six polar rings 30° apart, 72 satellites at 2,200 km. The amber ring is the one on duty, lying closest to the twilight band. Bright satellites are carrying traffic; the dark ones are coasting between shifts.

Open the full console →

Why the rings run pole to pole

The twilight band is the line where day meets night, and that line has a useful property: it passes through both poles. Day and night each claim half the planet, and the frontier between them runs top to bottom, like the seam on a two-colored ball. So a satellite ring that also runs pole to pole — a polar orbit — can lie right along the band. Tilted rings would cross the band at an angle, wasting their arc on day and night country; polar rings can spend their whole circle near the line.

But the lesson of the trap never sleeps: the rings are pinned to the stars, and the line turns with the planet — 32 degrees per day. One perfectly placed polar ring drifts out of alignment within hours. So we do not build one ring. We build a wheel of them: six polar rings, their planes fanned evenly like spokes, 30 degrees apart. At any moment one of them lies closest to the band and carries most of its traffic; call that one the duty ring. As the planet turns, the line rotates from one spoke to the next, and the next ring inherits the duty:

Hold onto the word duty. It is a real and useful idea — one ring does lie closest to the band, and it does carry the most traffic — but we will find, when the simulation is done with us, that it never works alone.

The wheel below is that schedule, running, seen from straight above the north pole. The three-dimensional model near the top of this page holds the same view: scroll back up, press Pole view · North, and the two pictures line up. Nothing in it maneuvers: the six planes are pinned to the stars exactly as the trap insisted they must be, and the planet's own rotation does the scheduling. But watch the misalignment readout through a shift. It is zero only for an instant, in the middle of the shift, when the band lies straight along the duty ring; it climbs from there, and at the moment of handover it stands at its worst — 15 degrees off the ring going off duty and 15 degrees off the one coming on. The shift change and the sourest geometry of the shift are the same event. Remember that; it is where this design breaks.

On duty
Misalignment
Next handover
Mission clockT+ 0.00 d

Six ring planes, fixed among the stars and fanned 30° apart; the band turns past them at 32.14° a day.

The wheel from far above the north pole. Each polar ring runs over both poles, so from here it projects to a straight line through the planet, drawn out to the 2,200 km shelf it actually flies on. Outside the dial, each ring carries the right ascension of its ascending node — its RAAN. They run 0° to 150° and stop: six rings need only half a circle of node angles, because the bare half of the rim is the same six planes seen from behind. The arrows follow the traffic, inward over the pole and outward down the far side, so six inward arrows meet six outward ones at two seams. The rim is the roster: twelve 30° sectors, one 22.4-hour shift each, and every ring stands duty twice in an 11.2-day local year — twice, for the same reason the node angles stop at 150°.

Two rhythms, hours and minutes, and every piece of protocol we design later must dance to both. With 12 satellites spaced around each ring, the fleet is 6 × 12 = 72 spacecraft. That was our seed design, at 1,800 km — the survey's middle ground. It looks right. Most wrong designs do.

Two numbers place a satellite

We have been describing the wheel in words — six rings, fanned like spokes, 30 degrees apart. The simulation needs to know where every one of the seventy-two spacecraft is, at every instant, and it gets that from two numbers per satellite. A third, the inclination — the tilt of the orbit's plane measured from the equator — is already settled: 90 degrees, a plane standing square to the equator and so passing straight over the poles, for the reason the last section gave. What remains is which plane a satellite flies in, and where in that plane it sits.

The first number names the plane. An orbit is a circle in space, and a circle through both poles cuts the equator at exactly two opposite points — one where the satellite is heading north, one where it is heading south. The northbound crossing is the ascending node, and the angle at which it happens is the plane's right ascension of the ascending node: RAAN, the name it carries in every orbital catalog.

Both halves of that phrase are key, so it is worth taking apart. Ascending node is the northbound crossing we have just found, as opposed to the descending one half a circle away — naming the plane by one of its two equator crossings, and picking the northbound one by convention. Right ascension is the astronomer's longitude: an angle measured around the equator like any other, but reckoned against the fixed stars rather than against the ground, from a zero direction the sky agrees on, and no planet can drag with it. Earth's catalogs use the First Point of Aries; ours will use a reference star far beyond this system, and any star will do, so long as it is far enough that no turning of the ground can shift its direction. The right in the name is the old word for straight, from the view of an observer on the equator, where stars rise straight up rather than at a slant; it says nothing about which side the angle is taken on. That distinction is the whole difference between a RAAN and a longitude you could paint on a map. A town's longitude turns with the planet. A plane's right ascension does not, because nothing fastens the plane to the planet: it is a circle in space, and space does not turn.

Give a polar orbit its right ascension, and you have named the plane completely; every satellite in it rides the same circle.

A polar ring · i = 90°

NreferencestarRAANΩ = 60°inclinationi = 90°ascending nodeequatorial planeorbital plane

Its own opposite. Turn the node 180° along the equator and the plane you get is the one you started with, run the other way: the same circle, with its ascending node where the descending one was. Six polar rings therefore divide 180°, not 360° — the PI in k * PI / planes.

Two numbers name a plane. The inclination, i, is the tilt of the orbital plane measured up from the equatorial plane at the ascending node, the crossing where the satellite is heading north; the descending node, where it comes back down, is on the far side of the globe. For the wheel i is 90°, and the ring stands square to the equator. RAAN, the right ascension of the ascending node, Ω, is how far along the equator that crossing sits, reckoned from a zero direction fixed among the stars rather than from any meridian on the ground, which is why it never turns with the planet. The reference star stands for that zero: a star so far beyond this system that no turning of the planet can shift its direction, the same convention Earth's catalogs follow with the First Point of Aries. Drawn for the wheel's third ring, Ω = 60°, at the access shelf's 2,200 km.

So a wheel of six rings needs six values of RAAN, and here the obvious guess is wrong in a way worth slowing down for. Six things spread evenly around a circle sit 60 degrees apart. Our rings sit 30 degrees apart, and their RAANs are 0°, 30°, 60°, 90°, 120°, and 150° — they stop halfway around and never finish the circle. Look at the wheel above, and you can see why: each ring is drawn as a diameter, not a spoke, because a polar orbit passes over both poles and so reaches clean across the planet. The three-dimensional model near the top of this page shows the same thing from Pole view · North: the rings you have been watching as loops flatten into six straight lines through the middle of the world. The plane you would place at 180° is the plane already sitting at 0°. It is the same circle in the same piece of sky, traversed in the opposite direction: in the figure above, its ascending node would sit where the descending one is now. A polar plane is its own opposite, so N polar rings divide 180 degrees rather than 360:

let raan = k as f64 * std::f64::consts::PI / c.planes as f64;   // PI, not 2 * PI

That single PI is the reason the shift schedule has twelve shifts and not six. The band sweeps a full circle in a local year and meets a plane every 30 degrees of that sweep — but there are only six planes, so each one takes duty twice, once as its ascending half swings into the band and once, half a year later, as its descending half does. The roster on the plate's rim carries every ring number twice for exactly this reason.

The second number places the satellite along the ring. Twelve satellites, evenly filed, 30 degrees of arc apart:

let theta0 = j as f64 * 2.0 * std::f64::consts::PI / c.sats_per_plane as f64   // here, the full circle
    + k as f64 * c.interplane_phase;                                             // zero in the seed design

The full circle this time, because a satellite genuinely can be anywhere on the ring and a position and its opposite are two different places. Those 30-degree slots are what set the fast rhythm: the handover cadence we are about to quote is the time it takes the ring to advance by one slot.

The second line is the one to notice. It offsets each ring's slot pattern from its neighbor's by a fixed angle, and the seed design sets that angle to zero, so every ring files its satellites at the same phase. The simulator carries the term anyway, because nothing in this design gets to assume a value for it. A satellite reaches its ring when the launch site swings beneath that plane, and where it lands along the ring depends on the minute the launch left the ground; a campaign that slips a window by a day arrives at a different phase, and nothing later corrects it for free. So the relationship between adjacent rings is not a number the design chooses. It is a number the fleet ends up with, and a coverage claim has to hold for whatever that turns out to be. The simulation tests exactly that before this section is over, dealing every ring an arbitrary phase and asking whether the band still holds.

The arrows on the plate are worth one more look, because they carry a fact we will need twice more before this section is done. Walk around the rim, and you pass six rings running inward — climbing north over the pole — and then six running outward, back down the far side. Between those two runs are two places where a ring's nearest neighbor, 30 degrees away, is flying the other way entirely. These are the wheel's seams. They are unavoidable: the nodes span half a circle, the traffic spans a whole one, and something has to give at the join. When we try to interleave the rings like a honeycomb later on, the seams are what defeat it.

One last consequence, and it closes the loop with the trap. The planes hold their right ascensions forever, because that is what being measured against the stars means; the planet turns underneath at 32.14° a day. In the planet's own frame, then, every node walks steadily backward — node = raan - spin * t, one line in polar_sat_position — and that backward walk is the shift schedule. The wheel does not hand over because anything aboard it decides to. It hands over because the ground keeps moving and the nodes do not.

When the backbone puts a navigation shell above this one, it will spread its nodes over a full 360 degrees instead. Nothing has changed about the arithmetic; those planes are tilted 55 degrees rather than 90, and a tilted plane is not its own opposite — flip it halfway around the planet, and it leans the other way. Only the polar case gets to halve its circle.

Both claims are easier to test than to read. The model below is the construction from the figure above with its two numbers unfixed. Drag RAAN and the ascending node walks along the equator; drag inclination and the plane leans over the node line. Then, with the ring polar, press Turn the node 180°: the ring sweeps half a circle and lands exactly on the dashed ghost of where it started, with its arrows reversed. Set the inclination to the backbone's 55° and press it again. The ring lands somewhere else. Two camera presets make one number each plain: North pole looks straight down the spin axis, where RAAN is an angle on a dial, and Equator looks along the node line, where the inclination is the lean of the ring against the equator.

RAAN Ω
Inclination i
The ring is
Satellite

Ω = 60°, i = 90°: the wheel's third ring, as the figure above draws it. Drag the sliders, then turn the node.

The construction from the figure above with its two numbers unfixed. RAAN walks the ascending node along the equator, measured from the reference star; inclination leans the orbital plane over the node line, and the heavy half of the ring is the one climbing north through that node. Turn the node 180° sweeps the ring half a circle and leaves a dashed ghost where it was. At i = 90° the ring lands on its ghost with the arrows reversed: the same circle, run the other way, which is why six polar rings divide 180°. At the backbone's 55° it lands on a different plane, sharing only the node line, which is why that shell must spread its nodes over the full 360°. Two camera presets make one number each plain: North pole looks down the spin axis, where RAAN is an angle on a dial, and Equator looks along the node line, where the inclination is the lean of the ring against the equator. The satellite rides at the access shelf's 2,200 km and completes an orbit in about thirteen seconds here, against 131.6 minutes in the sky.

The ground stands still

Before the simulation runs, one thing about where it runs. A satellite tracker on Earth keeps two coordinate frames, because two things are simple in two different places. An orbit is simple among the stars: its plane holds still and its motion is Kepler's, so trackers propagate orbits in an Earth-centered inertial frame, ECI, whose axes point at fixed directions in the sky. The ground is simple on the ground: a city, a dish, a GPS fix all have coordinates that never change in an Earth-centered, Earth-fixed frame, ECEF, whose axes turn with the planet. The two frames rotate against each other once a sidereal day, and every pass prediction converts between them: a rotation about the pole by the Earth's rotation angle, with small corrections for precession, nutation, and polar motion when the job is precise. A dish that sits still in ECEF is moving at 465 meters a second in ECI. Neither frame is wrong. The conversion is the price of using each where it is simple.

The terminus simulator keeps only the ground frame. Its axes are the planet's own: z the pole, the red dwarf along -x forever, the terminator the great circle in the x = 0 plane, the band straddling it. In that frame, every ground point is a constant unit vector, the ground_unit the counting loop is handed, and the 216 sample points of the band are built once, from an azimuth around the terminator and an offset toward the night side, and never move again. The orbital planes are what move. They are fixed among the stars, so in the planet's frame they regress at the spin rate, and the conversion Earth applies to the ground is applied here to the planes instead, in one line, the node walking back. Satellite positions come out in the ground frame directly, and counting who can see whom is a dot product against constant vectors.

The figure below draws the same day twice, once in each frame, with the planet's center at the origin of both. The left panel is fixed to the stars, Earth's ECI: one polar ring holds still, and the ghost of the red dwarf shows where the ground and everything on it will have turned to a day later. The right panel is fixed to the ground, Earth's ECEF and the simulator's own: the band and its 216 sample points hold still, and the ghost ring shows where the node will have walked back to. Nothing in the sky differs between the two; only the axes do.

The same day in two planet-centered frames. Both keep the planet's center at the origin; they differ only in what the axes are fixed to. On the left the axes are fixed to the stars, Earth's ECI, and the ring holds while the ground turns. On the right they are fixed to the ground, Earth's ECEF and the simulator's own frame, and the band holds while the ring's node walks back. The ghosts show where things will be one day later.

Planet-centered, fixed to the stars · Earth's ECI

Nto the red dwarfa day laterthe ground turns 32.14° a daytwilight band216 ground_unit pointsthe ring holds

The ring holds. Nothing fastens an orbital plane to the planet, so among the stars it never moves. Everything on the ground does: the twilight band, the 216 points the sweep samples along it, and the red dwarf they all face, turning together 32.14° a day.

Planet-centered, fixed to the ground · Earth's ECEF

Nto the red dwarfthe node walks back 32.14° a daythe band holds216 ground_unit pointsring, a day later

The ground holds. The same day, from the band's own point of view: the ground points are constants, the red dwarf never moves, and the ring is what drifts, its node walking back 32.14° along the equator — node = raan - spin * t, the one conversion the simulator makes.

Earth's trackers keep both frames and convert between them constantly, because the ground frame there pins only the ground: the Sun sweeps round it every day. On a tidally locked planet the ground frame also pins the red dwarf, the terminator and the band, so the whole problem stands still and only the orbital planes move through it. The simulator works in the right-hand picture throughout: every ground_unit is a constant vector, and the planes are moved into its frame by regressing their nodes.

Now the same two frames as an animation, so the still figures above can be watched turning. Both keep the planet's center at the origin and differ only in what the axes are fixed to. Press Play with fixed to the stars selected: the six rings are fixed in space, and the planet moves, its band and its red dwarf sliding round together beneath the rings. Switch to fixed to the ground, and you have the planet's own perspective: the red dwarf and the ground stand still, and the constellation and the stars are what move, the rings drifting the other way while the star field sweeps past behind them. Then press North in the pole view. Fixed to the stars, the six rings flatten into six fixed diameters, and the green band sweeps round like the hand of a clock; fixed to the ground, the band stands still and the six diameters wheel past it, which is the duty plate from earlier in this section, now in three dimensions. The green points on the band are the sweep's 216 sample points. Nothing in the sky differs between the two views; only the axes do.

Twilight band Access wheel Duty ring Alpha Centauri A · B Drag to turn the view · scroll to zoom · right-drag to pan
Pole view
Planet-centered frame
Speed 10 min/s
Mission clock T+ 0.00 d
Duty ring
Min visible
Mean visible
Footprint radius
Dwell
Edge latency

The same day, both ways, from the planet's center. Fixed to the stars, the rings hold and the ground turns; fixed to the ground, the band holds and the rings drift. The green dots are the 216 ground points the sweep samples. One turn of the planet takes 11.2 days.

Open the full console →

One thing in that view looks like a mistake and is not: fixed to the stars, the star field holds still and Proxima Centauri does not. The distant stars are, for every purpose here, infinitely far away, so the planet's motion along its orbit does not change their directions. Proxima Centauri is close, and the planet circles it once every 11.2 days, so from a frame pinned to the planet's center the star's direction sweeps a full circle in that time. The tidal lock makes that sweep match the spin exactly, which is why the globe, its band, and the red dwarf turn as one rigid thing in the first view and stand still together in the second.

That is also why one frame is enough. The conversion is the same arithmetic on both worlds, a rotation about the pole by the spin rate times the time; what differs is what the ground frame fixes. Earth's ECEF pins the ground and nothing else: the Sun sweeps round it once a day, and even in ECI its direction drifts a degree a day, which is what a sun-synchronous orbit is built to follow. On a tidally locked planet the frame that pins the ground also pins the red dwarf, the day side, the terminator, and the band. The whole problem stands still, and only the wheel moves through it. The zero direction the last section reckoned RAAN from is, in the code, simply the planet's own +x axis as it stood at time zero, the direction straight away from the red dwarf at that instant, fixed among the stars from then on.

The interrogation

We put the whole planet under simulation: sample the inhabited band — its center line and both edges, all the way around — and every 30 seconds for a full 11.2-day rotation, count how many satellites each sample point can actually use, meaning satellites at least 25 degrees above that point's horizon. The counting core, visible_count in the simulator's constellation module, is plain brute force:

/// Number of the constellation's satellites at or above `min_elevation`
/// (rad) as seen from `ground_unit` at time `t`.
pub fn visible_count(
    body: &CentralBody,
    c: &PolarConstellation,
    ground_unit: [f64; 3],
    min_elevation: f64,
    t: f64,
) -> usize {
    let mut count = 0;
    for k in 0..c.planes {
        let raan = k as f64 * std::f64::consts::PI / c.planes as f64;
        for j in 0..c.sats_per_plane {
            let theta0 = j as f64 * 2.0 * std::f64::consts::PI / c.sats_per_plane as f64
                + k as f64 * c.interplane_phase;
            let sat = polar_sat_position(body, c.altitude, raan, theta0, t);
            if elevation(body, ground_unit, sat) >= min_elevation {
                count += 1;
            }
        }
    }
    count
}

Two things about that signature. ground_unit is not a place on a map but a direction: a unit vector from the planet's center, in the frame of the last section, that the sampler builds from an azimuth around the terminator and an offset toward the night side. It is the analogue of an ECEF direction on Earth, minus the ellipsoid. The planet is a sphere here, the radius is applied inside elevation, and every terminal is taken to sit on the surface, a simplification that costs nothing at 2,200 km but would need revisiting for a terminal on a mountain. And the function answers for one point at one instant. The sweep, band_coverage in the same file, calls it for every one of the 216 sample points at every 30-second step of the 11.2-day rotation, about seven million calls, each walking all seventy-two satellites, which is where the hundreds of millions of geometry evaluations the closing section warns about come from. The access_constellation example that prints the tables below is what drives that sweep.

One caution about what the count means. It is what a town can see: every satellite above the mask is counted, whether or not it is radiating. What a town needs is a different and much smaller number, and a later section will use this same machinery to find it.

The number that matters is the minimum — the worst moment of the worst town's whole year. An average means nothing to a village whose connection died at shift change. And for the 72-satellite seed at 1,800 km, the minimum is zero.

Somewhere in the band, sometime in the rotation, a town looks up and finds no usable satellite at all.

Anatomy of a silence

The gap lives at the seams of the wheel. When the twilight band lies midway between two rings, a town at the band's center sits about 15 degrees off either ring's track. From that far aside, a passing satellite stands high enough to use for only a short stretch of its arc — about ±13 degrees of along-track travel. But the satellites in a ring are spaced 30 degrees apart. 13-degree windows, 30-degree spacing: between one satellite's setting and the next one's rising there is a sliver of silence, sweeping the mid-seam country like a slow blade, several times an orbit, every 22.4 hours when the geometry sours.

Both flanking rings are in the same predicament at the same moment, and — in this first design — in the same rhythm: every ring files its satellites at identical phase, so their windows open and shut in unison and their gaps line up perfectly. The silence is not one ring's failure. It is all of them blinking together. Nor is it only the mid-seam towns that suffer: the band reaches 20 degrees to either side of the terminator, and its two edges lie farther from every ring than its center does, so the outer margins of the inhabited zone go dark slightly more often than the middle.

Our first coverage sweep was under-sampled in time, and the error it produced is worth recording. At a two-minute step, staggering the rings' phasing appeared to close the gap, and we nearly filed that as the fix. At a 30-second step the gap reappeared through every stagger we tried: the silences were narrower than two minutes and had slipped between the samples. Coverage claims in this proposal are therefore made only at fine sampling, as a standing rule.

Buying coverage with altitude instead of hardware

Two honest fixes exist. The brute-force one: more satellites, tightening the spacing until the windows overlap — at 1,800 km that takes 84. The elegant one: raise the whole fleet, which widens every satellite's footprint, stretching the along-track window until 12 per ring suffice again. The simulation prices both. The example takes several minutes to run, because it sweeps the whole rotation at 30-second steps for every row of the table, and it needs --release:

cargo run --release -p terminus-orbits --example access_constellation

  alt (km)   rings  sats/ring   total   min visible   mean visible
      1800       6         12      72             0           2.68
      1800       6         14      84             1           3.13
      1800       7         12      84             1           3.13
      2000       6         12      72             1           3.07
      2200       6         12      72             1           3.45
      2400       6         12      72             1           3.81

Climbing 200 km closes the gap with the same 72 spacecraft — but at 2,000 km the along-track window beats the spacing by under three percent, a margin we decline to bet a civilization on. At 2,200 km the margin is real, the average town sees about three and a half satellites, and the edge-of-footprint latency has crept only from about ten to about twelve milliseconds — still comfortably inside the RFP's budget. The evaluation rule in TER-REQ-016 makes the choice for us: altitude is free forever; twelve extra spacecraft are not.

The baseline access fleet, recorded as ADR-0003: six polar rings, twelve satellites each, at 2,200 km. The old 1,800 km label is retired from the reference architecture.

With the altitude settled, the fast rhythm gets its exact value — and it is worth a sentence of its own, because the obvious guess is wrong. A satellite up here could serve a town for 16.6 minutes: that is the pass the survey measured, the whole arc of a satellite that happens to cross straight overhead. But a town is never given a whole pass. The next satellite in the ring rises behind it, climbs higher in the sky, and the link moves. One orbit, 131.6 minutes, divided by twelve satellites, is 11 minutes — and that, not the pass, is the nominal interval every later protocol has to survive. Handover cadence is the ring's spacing, not the satellite's pass duration. Treat 11 minutes as a ceiling rather than a promise: as the next section shows, the satellite that rises next is not always in the same ring, and a link that moves between rings moves sooner. It is also a lever: a satellite added to a ring buys coverage and costs handovers in exact proportion; a ring added to the wheel buys coverage and costs none.

One debt is flagged in the ADR rather than hidden: a minimum of one visible satellite is coverage without redundancy. Lose that satellite — or ask, as we soon will, for a second one to be visible before the first lets go of a conversation — and the count must rise. The simulation has already priced it: eighteen satellites per ring instead of twelve, 108 instead of seventy-two, buys a minimum of two. We will pay that bill when we design the handover machinery, and pay it knowingly.

Who is actually on duty

We have been saying "the duty ring" as though the wheel takes turns, one ring working while five coast. The simulation has been quietly telling us otherwise the whole time, because it never counted rings — it counted satellites, wherever they were. So ask it directly: when a town looks up, how many rings can it actually see?

The answer depends on where the town stands, and the reason is a piece of geometry we have been using all along without following it through. Polar rings are 30 degrees apart at the equator, but every one of them passes over both poles. They are not parallel; they are a fan, splayed at the waist and pinched at the ends. One example answers this question and the two that follow it. Its output comes in three parts, A, B, and C, each quoted on this page where its result is used, and it takes several minutes to run, nearly all of them in part C:

cargo run --release -p terminus-orbits --example duty_ring_trade

The example is duty_ring_trade.rs; the reach and ring-count functions it calls live in duty.rs.

Part B, condensed to the columns the argument needs:

Town's latitudeRings within reachDuty ring's share of its sky
0–15°1–249%
15–30°1–244%
30–50°1–337%
50–70°2–619%
70–90°all 617%

The model below opens at the moment of a shift change, 0.47 days in. Two rings sit 15 degrees off the band, one falling away from it and one arriving, and the amber duty label has just passed to the arriving one, still 15 degrees short and closing. Every ring's footprints are drawn, and the band is covered end to end all the same, largely by satellites the new duty ring does not own. Press Play and watch the amber ring settle onto the band over the next half day; press North in the pole view and the fan is plain to see, six diameters pinched together at the poles and splayed at the equator, which is why a town near the pole has every ring overhead and a town near the equator has two.

Twilight band Access wheel Duty ring Alpha Centauri A · B Drag to turn the view · scroll to zoom · right-drag to pan
Pole view
Speed 10 min/s
Mission clock T+ 0.00 d
Duty ring
Min visible
Mean visible
Footprint radius
Dwell
Edge latency

The moment of a shift change, 0.47 days in: the duty has just passed to the amber ring, still 15° short of the band, while the ring it replaces is 15° past it — yet the band is covered all the same, by its neighbors. Note how few satellites are lit.

Open the full console →

Near the equator — where most of the band's area lies, and most of its towns — the wheel is a two-ring affair. The duty ring is genuinely the workhorse there, supplying about half of everything the town can see, with a single neighbor covering the rest and, crucially, covering the moments when the duty ring's own satellites are between slots. Walk toward a pole, and the rings crowd together until all six are overhead at once, and "duty" stops meaning anything: the on-duty ring's share falls to 17 percent, which is exactly the one-sixth a town would get if the label were assigned at random.

So the duty ring survives as an idea, and the shift schedule is real. What does not survive is the notion that it works alone. A town at the band's edge, at the sourest moment of the shift, can be 35 degrees of arc from the duty ring's track — and a satellite at 2,200 km can reach only about 23 degrees. That town is not underserved by the duty ring; it is outside it altogether. No number of satellites added to that ring would reach it, because the failure is one of reach, not of spacing. We priced the alternative honestly: a fleet where the duty ring really did serve alone would have to fly at 7,300 km, nearly tripling edge latency from 12 to 32 milliseconds, or drop its elevation mask to 5 degrees and surrender the rain margin this cloudy, wind-driven band cannot spare. That is part A of the example's output, first as geometry and then as a simulation to confirm it:

A. Could one ring serve the band alone?

 alt (km)     mask  lambda (deg)    reach needed   sats/ring needed
     2200      25d        22.65d          35.00d impossible at any count
     3000      25d        26.96d          35.00d impossible at any count
     4000      25d        31.17d          35.00d impossible at any count
     5200      25d        35.07d          35.00d                 79
     6000      25d        37.18d          35.00d                 14
     7300      25d        40.02d          35.00d                  9
     2200      10d        32.94d          35.00d impossible at any count
     2200       5d        37.23d          35.00d                 14

   Simulated, to confirm the analysis:

 alt (km)     mask sats/ring   total  min (duty only)     edge 1-way
     2200      25d       12      72                0        12.1 ms
     2200      25d       24     144                0        12.1 ms
     2200      25d       48     288                0        12.1 ms
     2200       5d       14      84                1        17.4 ms
     6000      25d       14      84                1        27.5 ms
     7300      25d        9      54                1        32.4 ms

Read the first three simulated rows together. Doubling the duty ring to 24 satellites, and doubling it again to 48, leaves the minimum at zero, which is the reach wall in numbers: a ring that cannot see the town does not start seeing it by getting crowded.

There is a gift hidden in this, and it is worth more than the tidiness we gave up. We re-ran the whole rotation 64 times per design, each time dealing every ring a different, arbitrary starting phase — the sort of scramble a real launch campaign produces when windows slip. The minimum never moved. Not once in 256 attempts did a scrambled wheel cover the band worse than a perfectly synchronized one. That is part C of the example's output, and a warning before you run it: those 256 attempts are each a full rotation at 30-second steps, so this part is where nearly all of the example's minutes go, even spread across every core the machine has.

C. Does coverage depend on inter-ring phasing?

 alt (km)   rings sats/ring   total   phase-locked min   worst random min    failures
     2200       6        12      72                  1                  1        0/64
     2200       6        18     108                  2                  2        0/64
     2400       6        12      72                  1                  1        0/64
     2200       8        12      96                  1                  1        0/64

The second row is also the receipt for a bill mentioned earlier: eighteen satellites per ring, 108 in all, holds a minimum of two under every scramble as well.

We should say precisely what that does and does not prove, because we tested one more idea and it failed. A cellular network covers a city by offsetting each ring of towers half a step from the last, so their cells interlock like a honeycomb — the densest way known to tile a flat plane with circles. Try the same trick here, giving each ring half a satellite's head start on its neighbor, and the wheel gets worse: at our twelve satellites per ring, it opens a gap that the plainly aligned wheel does not have — --example phasing_options walks the whole profile, and the result is not even monotone in satellite count, because "half a slot" means 360°/satellites and so changes meaning every time you add one. The honeycomb does not survive the trip from a plane to a sphere. Every ring runs over both poles, so the cells that were tidy hexagons at the equator pinch shut at the caps; and because a ring is a closed circle, it serves the band at two opposite longitudes at once, climbing north on one side while falling south on the other, so there is no single offset to tune. Where the wheel closes on itself, neighboring rings run in opposite directions outright.

So the freedom we have is narrower than it first looked, and worth stating honestly: the fleet is free to ignore inter-ring phase, not free to choose it badly. Uncoordinated is safe. Clever is not.

That matters enormously for how this fleet gets built. A launch reaches a given ring only when the launch site swings beneath its plane, and on a world that turns once every 11.2 days, those windows are precious — one per ring per rotation, each ring's window 22.4 hours after its neighbor's. Had coverage depended on the rings staying in step, every launch would have had to hit not just its window but a particular minute inside it, and then the fleet would have had to hold that arrangement forever: a satellite injected just 100 meters off its intended altitude drifts a full slot within about fourteen months. It costs almost nothing to correct — a nudge of about forty millimeters per second — but it is a nudge that never stops being due, on all seventy-two spacecraft, for as long as the constellation flies.

We owe none of it. The rings may be launched in any order, in any year, at any phase, and a slipped window costs exactly one late satellite and not one second of anyone's coverage.

Most of the fleet is asleep

Look again at that latitude table, and something should nag. A town near the pole has all six rings overhead, half a dozen satellites for one town with one sky's worth of demand, and the fleet keeps every one of its seventy-two transmitters radiating to achieve that. Why run the whole fleet to serve a band that never needs more than a handful of satellites over any one place?

We would not. Visible and needed are different questions, and we have been answering the wrong one. So ask the right one: at each instant, what is the smallest set of satellites that still serves every point of the band? That is a classic problem — minimum set cover — and our instances are small enough that we do not have to guess at the answer. We can prove it.

One definition first, because the rest of the series depends on it. A satellite is lit when its access radio is on and serving the band, and dark, or asleep in this section's language, when that radio is off. Dark is not powered down. The spacecraft's bus, its attitude control, and its laser terminals stay up, and a dark satellite goes on relaying traffic along its ring; a later section leans on exactly that when it routes conversations to the anchors through satellites whose radios are off. Everything switched, counted, and saved below is the radio.

cargo run --release -p terminus-orbits --example activation_plan
PolicyMean litPeakDuty cycleSwitches/hourCoverage
everything on7272100%0complete
duty ring only121217%0fails always
duty ring, patch holes, then prune23.13032%253complete
greedy set cover22.43031%373complete
proved minimum21.42830%423complete

Read the second row as an epitaph. Lighting only the duty ring — the design we spent this section dismantling — leaves the band uncovered at every single instant we sampled. It never works, not once in eleven days.

Read the last row as the prize. Twenty-one satellites, on average, can hold the entire twilight band. Roughly seventy percent of the fleet can be dark at any given moment. That is not an estimate; a branch-and-bound search — the kind that discards whole families of plans at once until only the provably best remains — closed on the true optimum at all 16,128 instants we tested (the same 11.2-day rotation, planned at a one-minute step rather than the interrogation's 30 seconds), so the number is a floor and not a hopeful heuristic.

But we are not going to fly the optimum, and the reason is instructive. The optimal plan is free to reshuffle its entire active set every minute, and it uses that freedom: it switches satellites on and off 423 times an hour, against 253 for the duty-first policy in the third row, and every switch is a thermal cycle and a power-electronics cycle on a radio we would like to last decades. What it buys for all that churn is under two satellites.

So we take the policy in the third row, which an operator can still state in one sentence: light the duty ring, add satellites from the neighboring rings wherever a hole remains, then switch off anything that turns out to be serving no one. The duty ring keeps its meaning — it is genuinely the block that goes on first — and the wheel fills in behind it.

Here is the whole algorithm — first as a shape, then as something you could implement from. There is nothing clever in either, and that is the point: every step is one a ground controller could check by hand.

flowchart TD accTitle: The access-radio activation planning algorithm, from duty ring to uploaded timetable accDescr: The planner decides only which access radios are on; every satellite's laser links stay up throughout. For each instant in the timetable, the planner finds the duty ring, the orbital plane lying closest to the terminator, and switches on the radios of all twelve of its satellites as a block. It then samples the inhabited band at 216 ground points and, for each point, lists the satellites standing at least twenty-five degrees above its horizon. It checks whether any point has no satellite with its radio on. If some point is unserved, it switches on the radio of the dark satellite that would cover the most unserved points, breaking ties toward a satellite whose radio was already on at the previous instant, and repeats the check. When no point is unserved, the planner makes one last pass over every radio it has switched on and switches off any whose every point would remain covered without it, which is how radios made redundant by later picks are recovered. Then the instant is done, and the planner moves to the next. Once every instant has a plan, the schedule is smoothed across time in two passes: a lazy-off pass that keeps a radio on through any gap shorter than 180 seconds because it will be needed again, and a warm-start pass that switches each radio on ahead of its first use so it is never handed a link cold. The finished radio timetable is uploaded to the fleet. START(["For each instant t"]) --> DUTY["Find the duty ring"] DUTY --> BLOCK["Radios on as a block<br/>(12 satellites)"] BLOCK --> REACH["Sample 216 band points;<br/>list what reaches each<br/>at ≥ 25°"] REACH --> CHECK{"Any point<br/>unserved?"} CHECK -->|"yes"| PICK["Switch on the dark radio<br/>closing the most holes<br/>(ties: one on last instant)"] PICK --> CHECK CHECK -->|"no"| PRUNE["<b>Prune:</b><br/>switch off any radio<br/>whose every point stays<br/>covered without it"] PRUNE --> NEXT{"More instants?"} NEXT -->|"yes"| START NEXT -->|"no"| LAZY["<b>Lazy off:</b><br/>needed again within<br/>180 s? Keep the radio on"] LAZY --> WARM["<b>Warm start:</b><br/>radio on before first use"] WARM --> UPLOAD(["Upload the radio timetable"])
The activation planner, for the access radios only. Each instant: the duty ring's radios on as a block, then dark radios switched on one at a time — always whichever closes the most remaining holes — until nothing is unserved, and finally a prune pass that switches off whatever the later picks made redundant. The finished per-instant plans are then smoothed across time. The laser links are never part of it; they stay up on every satellite.

The flowchart shows the shape; the loop at its heart deserves stating exactly, because everything turns on how the next satellite is chosen:

# lit = access radio on, dark = access radio off. Laser links stay up on
# every satellite regardless; the planner never touches them.

plan(t):
    duty  ← ring whose plane lies closest to the terminator at t
    lit   ← every satellite in duty                  # radios on as a block, before measuring
    for each of the 216 band points p:
        reach[p] ← satellites ≥ 25° above p at t

    while some p has no satellite in lit:
        best ← argmax over dark satellites s of
                   |{ p : reach[p] ∩ lit = ∅ and s ∈ reach[p] }|
               ties broken toward s already lit at t−1
        lit ← lit ∪ {best}                           # radio on

    for s in lit, dark-last-instant first, then least useful first:
        if every p reached by s has another satellite in lit:
            lit ← lit \ {s}                          # radio off: prune
    return lit

smooth(plans):                                       # across time, not within
    for each satellite s:
        close every off-gap shorter than 180 s       # lazy off: keep the radio on
        extend every on-run backward by the warm-up  # warm start: radio on early

The Rust this is drawn from is crates/orbits/src/activation.rsduty_first_activation is the planner, smooth_schedule the pass over time, and exact_activation the branch-and-bound that produced the proved minimum we decided not to fly.

Five things in there are doing real work.

The duty ring goes on as a block, before anything is measured. That is not an optimization — it is what keeps the plan explainable. The ring nearest the band will carry most of the traffic anyway, so starting from it costs little and means the answer always has a shape a human recognizes. The prune pass may hand one or two of them back at the end, but the block is where every plan starts.

The score counts holes, not coverage. Read the argmax again: a dark satellite is judged purely on how many currently unserved points it would rescue — not on how much sky it can see, and not on how much it would reinforce places already covered. A satellite hanging over a stretch of band that three others already serve scores zero and stays dark, however good its view.

The last pass undoes the greedy loop's own mistakes. Each pick is the best one available at the moment it is made, and that is exactly the weakness: a satellite lit to close hole A often also covers hole C, which a later pick made for hole B then covers again. The early pick is now carrying nothing the plan needs, and nothing in a forward-only loop will ever notice. So the planner finishes by walking everything it has lit and switching off any satellite whose every point would remain covered without it. It is worth 1.1 satellites — and the surplus it removes is not spread evenly. It piles up over the poles, where all six planes converge, and one patch of ground ends up served several times over by satellites each chosen for a different hole. That is precisely where this section began, and precisely the over-serving it set out to disprove.

Ties go to whoever was already awake. When two satellites would close the same number of holes, the one lit a moment ago wins. It costs nothing, and it stops the plan from reshuffling itself for no reason — the churn that made the theoretically optimal policy so expensive two paragraphs ago.

The smoothing happens across time, not within an instant. plan(t) sees one moment and nothing else, which is exactly what makes a satellite flicker: needed, not needed, needed again, in the space of a couple of minutes. Planned that way, a satellite blinks on and off 787 times a day for no gain at all. The lazy-off rule kills every one of those for about one and a half extra satellites lit, and it can be exact rather than a guess, because the geometry is known years ahead — "will this be needed again shortly?" has a real answer.

That last point is what makes the whole scheme practical. This is a timetable, not a negotiation. Nothing in the diagram runs on a spacecraft; it runs on the ground, weeks early, and each satellite is told when to switch its radio on.

One honesty check before we bank the savings. A dark radio cannot take a conversation the instant it is asked; it has to be warmed up before it is needed. Charge the plan for that lead time, and the numbers move:

Warm-up leadnone2 min5 min10 min
Satellites lit23.126.730.434.9

Even granting a full ten minutes of warm-up — far more than any radio needs — the fleet still runs under half lit. And notice what the lead time does to the prune pass. It saves 1.1 satellites with nothing charged for warm-up, and 0.1 at five minutes, where the plan with the pass (30.4) and the plan without it (30.5) are indistinguishable. On the averages alone it stops paying for itself almost immediately.

We keep it anyway, and the reason is not arithmetic. A pruned satellite is not one the plan suspects is idle; it is one the plan has proved serves no point that needs it. There is no defending a transmitter radiating at nobody when the check that finds it is free and runs on the ground weeks in advance. The averages stop rewarding the step long before the argument for it runs out — which is worth remembering the next time an optimization is judged by its mean.

Two things this does not buy, and the RFP will ask about both. It does not shrink the fleet: all seventy-two spacecraft are still required, because the satellites dark now are working in twenty minutes. And it does not relax the handover design — quite the opposite: since a link may only ever be handed to a satellite whose radio is already up, the activation timetable and the handover plan have to be generated together. We will honor that when we build the handover machinery.

Running it yourself

Nothing in this section is a claim you have to take on trust. The simulator is terminus, it is open source, and every table above is one command away. You need a Rust toolchain (1.79 or newer); nothing else.

git clone https://github.com/eventhelix/terminus
cd terminus
cargo run --release -p terminus-orbits --example access_constellation

Four examples carry this section:

ExampleWhat it printsRuntime
activation_planthe four activation policies, the proved minimum, warm-up costs~1 min
access_constellationthe altitude trade — where 1,800 km fails and 2,200 km holds~3 min
duty_ring_tradeA: the duty ring's reach wall; B: the latitude table; C: the phase sweep~7 min
phasing_optionsaligned vs half-slot vs random, and why the honeycomb fails~8 min

One practical note, and it is not optional: keep --release. These sweeps evaluate a full 11.2-day rotation against 216 sample points — at 30-second resolution for the coverage sweeps, one-minute steps for the activation planner — hundreds of millions of geometry evaluations — and a debug build turns those minutes into an afternoon. The runtimes above are from an ordinary laptop; treat them as the shape of the cost rather than a promise. That cost is the honest price of the fine sampling this section insisted on, and it is exactly why our first pass sampled every two minutes and got the wrong answer.

If you would rather read the code than run it, the counting core is crates/orbits/src/constellation.rs, the duty-ring and activation logic are duty.rs and activation.rs, and cargo test -p terminus-orbits checks every claim in this section — including the seed's failure at 1,800 km and its recovery at 2,200 km, and the one that caught us out, that the half-slot stagger breaks the very baseline it looks like it should improve. The coarse-sampling lesson lives in the examples rather than in a test: the sweeps run at 30-second steps because two-minute steps let real gaps through. Every link on this page points at the tag terminus-post-5b, the state of the code these numbers came from, so the line numbers hold even as the repository moves on.

The wheel, turning

So picture the finished thing from far above the pole: six silver rings fanned around a dark world, seventy-two points of light sliding along them; a red ribbon of civilization turning slowly beneath, and duty passing from ring to ring like a watch changing in the night. Every town under at least one satellite, every second, for as long as the fleet is maintained.

And every one of those conversations is now arriving at a satellite 2,200 km up and asking to reach a large language model. Where is the model? On the satellite overhead, which vanishes in a quarter of an hour? Spread across the fleet? Parked out at those strange balance points the star cannot steal? The next section takes on the question the whole proposal has been circling: where does the mind live?