What internal/sat could not know: where the antenna is, which birds the
operator cares about, and where the files live.
Startup reads the cached elements and the frequency plan from disk and
nothing else — one file and a few hundred parses, so the tab is full the
moment it is opened, on a shack PC with no internet as much as on one
with. Fetching is the slow, optional half and never blocks a launch; it
happens on its own only when the set is stale and the operator asked for
it.
Elements pasted in by hand go in their own file. The feed cache is
replaced wholesale on every refresh, so a freshly launched satellite —
whose elements circulate on a mailing list days before any feed carries
it, which is exactly the week everybody wants to hear it — would
otherwise be wiped by the first automatic update.
The list joins both halves and shows what is missing on either side. A
bird with elements and no plan is one the operator can still track; a
bird with a plan and no elements is the visible symptom of an element set
that is too old. Dropping either turns a fixable configuration problem
into a satellite that "does not exist".
GetSatelliteTuning is the working answer, and everything that will later
drive a radio is built on top of it rather than beside it, so the display
and the rig can never disagree. It keeps the operator's frequency
nominal and applies Doppler only on the way out: on a linear pass the
station being answered stays put on the dial while both radios chase the
shift. A geostationary bird is corrected by nothing at all.
Two things the tracker cannot work without, both kept apart from the
orbital maths on purpose.
The elements come from Celestrak's amateur group, with PE0SAT as the
fallback for the hour when Celestrak is rate-limiting a hundred trackers
at once. A malformed satellite is skipped rather than fatal — a feed of
two hundred birds with one bad checksum must still give the operator the
other hundred and ninety-nine — and the count is returned so the app can
say so. The cache is plain TLE text in the data directory, written
beside and renamed, and only replaced once a feed has produced usable
elements: a captive portal must not take away the set the station
already had. Loading it first is what makes the satellite tab full on a
shack PC with no internet.
The frequency plan is separate because it changes for different reasons:
elements every few days from a feed, a transponder when the satellite is
commanded into another mode. The shipped list is a starting point, copied
to the data directory on first use and read from there afterwards, so an
operator can correct a frequency without waiting for a release and keep
the correction across updates — and a file they have broken is reported,
not overwritten.
UplinkFor is the part that matters on the air. On an inverting linear
transponder, tuning up the downlink means going down the uplink; get it
backwards and you transmit at the far end of the passband from the
station you can hear, which is the classic first evening on a linear
bird.
Names are matched on letters and digits alone. Celestrak says
"RADFXSAT (FOX-1B)" where every operator says AO-91, and nobody spells
Es'hail the same way twice.
The foundation of the satellite branch, and nothing above it yet: where a
satellite is (SGP4 from akhenakh/sgp4, Apache-2.0 and pure Go, so the
no-cgo rule holds), where it will be (passes with an elevation floor,
because a three-degree scrape is a line in a table that will never be a
QSO), and what its motion does to a frequency.
The Doppler pair is the part worth being careful about: the two
corrections go in OPPOSITE directions. The downlink arrives shifted and
we tune to meet it; the uplink must LEAVE shifted the other way to land
on the transponder's nominal input. A test pins the signs and the size —
7 km/s on 2 m is about 3.4 kHz.
Elements keep their raw lines beside the parsed form: that is what the
cache stores and what an operator pastes by hand for a bird no feed
carries yet, which is exactly when everyone wants to hear it.