Commit Graph
19 Commits
Author SHA1 Message Date
rouggy 3fe14c2c77 feat(sat): the Satellites tab laid out again, and a visible ground track
Everything asked for and not delivered last time.

The sky plot moves to its own column on the LEFT of the map. It was
stacked in the readout column, where a plot that wants to be square
competed for width with the numbers and pushed the pass table off the
bottom of the screen — while the margin on the other side of the map sat
empty throughout. Its own width, dragged from its right edge, with
double-click to reset.

Every block of the readout column gets a heading and a chevron: the
pass, the position, what to tune, the pass list. Each remembers its own
state, because an operator working FM birds never looks at the linear
passband and one watching a schedule never looks at the range rate.
There were no headings at all before, which cost twice — nothing said
what a group of numbers was, and there was nowhere to put the control
that shuts it. A shut block keeps the one number it exists for in its
heading, and drops that badge again when open rather than repeating the
body a line below.

The tune panel now leads with the CENTRE of the passband. The
Doppler-corrected figure was the wrong number for a reference panel: it
moves every second, it is different for every operator, and it is not
what the frequency plan, the AMSAT tables or anybody on the air calls
the satellite's frequency. What the radio is actually on belongs beside
Tracking, where the radio is — it is already there — so this keeps the
correction only as the offset that explains the difference.

Badges rather than rows of grey words, in the app's own status tones, on
the things that decide something: the band of each frequency, the
Doppler offset, an inverting transponder, the passband width, and the
peak elevation of a pass.

And the ground track. It was `color: 'var(--info)'` on a map that
renders with preferCanvas, and a CSS variable is not a colour a canvas
strokeStyle can take — so the browser kept whatever was set last and the
track came out a pale near-white that vanished over the imagery and the
deserts alike. This is the same trap internal to GridSquareMap's own
comment: every other map in this app passes hex. Now a real colour over
a dark casing, the way a road is drawn, because one line cannot hold up
over both a pale sea and a dark continent but a line with an outline
can.
2026-09-10 15:09:27 +02:00
rouggy 3b6269978c fix(sat): saving the settings refreshes the Satellites tab
The tab read the followed list, the horizon and the locator once, when it
was mounted. It is normally open BEHIND the settings window while those
are edited, so un-following QO-100 left it in the pass table and in the
dropdown until the tab was reopened — indistinguishable, from the
outside, from a setting that had not saved at all.

SaveSatSettings now emits sat:settings, and the panel reloads the birds
and the passes on it, the same way it already does on sat:tle. One event
for every key: they all change what the tab should be showing. The
selection needs nothing new — a satellite that has stopped being
followed drops out of the dropdown, and the effect that keeps the
selection inside that list moves to the first one remaining.
2026-09-10 13:36:19 +02:00
rouggy 2615365684 fix(sat): the per-band antennas reach the satellite slices
Settings ▸ FlexRadio stores the band→antenna map keyed by the band name
in capitals — that is what the panel writes and what the entry form reads
back. applySatRadio looked its two bands up through bandForHz, which
returns the band plan's own spelling ("70cm"), so both lookups missed,
both antennas came back empty, and the early return left the pass on
whichever antenna the radio was last used on. An operator with XVTA on
2 m and XVTB on 70 cm had configured exactly the thing being ignored, and
nothing said so: the downlink ran through the wrong transverter in
silence.

The key is now computed by flexBandAntKey, which exists so the next
caller cannot make the same mistake, and a test pins the case in the
direction that broke. Both outcomes are logged — the resolved antennas,
or the bands nothing was configured for — because from the outside a
setting never made and a lookup that missed look identical.

Alongside, three things the same pass made obvious:

- Tracking shows the satellite's own azimuth, elevation, distance and
  altitude beside the frequencies. The strip held where the ANTENNA was
  pointing but not where the bird was, which is what says whether a pass
  is worth calling on. Elevation dims below the horizon so a satellite
  followed before its lever cannot be read as workable.

- Both satellite lists in the settings are sorted by name with the
  numbers taken as numbers. The available column followed the order
  birds.json happens to be written in and the followed column the order
  of the clicks, so finding one bird among sixteen meant reading all
  sixteen.

- A followed satellite that has since been renamed is resolved through
  the plan's aliases. LILACSAT-2 became LO-90, and the followed list is
  stored as plain text, so the bird the operator had chosen appeared as
  having no elements while the same satellite sat in the available
  column under its new name. Resolved in GetSatSettings rather than
  satSettings, which is read at startup before the plan is loaded.
2026-09-10 11:00:24 +02:00
rouggyandClaude Opus 5 b8491f3038 fix(sat): the antenna stops flickering, and the pass is readable from the header
THE FLICKER was a real bug and not a rotator problem. The rotator is asked where
it is at most every three seconds — a controller query binds a socket and waits —
but satTrackStep built a fresh status every second and carried over only Radio
and Error. So the antenna readout was filled in on one tick in three and blank
on the other two, which on screen is a rotator that keeps disconnecting. The
status is now rebuilt FROM the previous one, so a field nobody wrote this tick
keeps the value somebody wrote last tick.

THE HEADER now carries the two frequencies and the antenna bearing, beside the
button that started tracking. During a pass an operator watches the radio and
the antenna, not a column on the far side of the window — and that column is the
first thing they hide to get the map full width, which until now took the
numbers with it. The compass spins while the antenna is still on its way: a mast
takes tens of seconds to cross a pass, and "moving" against "stuck" is the whole
reason to look at it, which a number alone cannot show.

THE PRECISION drops from one hertz to a hundred. The Doppler moves about sixty
hertz a second on 70 cm, so the last two digits changed on every tick and the
display was a blur that could not be read and did not need to be. The radio
still gets the whole figure — the correction is computed and sent to the hertz —
this is only how much of it is worth putting in front of somebody. The shift
beside it says "+9.7 kHz" rather than "+9741 Hz", which is how it is read aloud.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 23:30:57 +02:00
rouggyandClaude Opus 5 37b798e9f5 fix(sat): changing satellite mid-pass moves the radio to it
Two birds are often up at once, and switching between them left the frequencies
on the first. The selection on the page is the DISPLAY's; the tracker held its
own name and went on following whatever it was started with. The way through was
to stop tracking and start it again, which is how it was found — and which is
also how a Flex throws away and rebuilds both its slices for no reason.

RetargetSatelliteTracking changes what is being followed without letting go of
the radio or the rotator. Everything derived from the old satellite is cleared so
the next step sets it afresh: the frequencies, the mode on both slices (set once
per satellite, not per tick), the antennas and the CTCSS tone — the new bird may
be U/V where the old one was V/U, which swaps which slice sits on which band.

And it happens at once rather than at the next tick. The loop gained a wake
channel: a second of the previous satellite's frequencies is a second of the
wrong pass, and the antenna would otherwise wait for the new bird to drift a
step away from where the old one happened to be.

Selecting a satellite with the radio idle is unchanged — a look, not a command.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 23:05:07 +02:00
rouggyandClaude Opus 5 660b1bfa1f feat(sat): the pass table is the whole list
Asked why the satellite dropdown exists when the passes are on the right, and
the honest answer was that the table was incomplete. Three kinds of satellite
never appeared in it: QO-100, which has no pass because it never sets; one whose
elements have not arrived, which cannot be predicted at all; and one whose next
pass falls beyond the prediction window. Following a satellite and not finding it
in the list reads as OpsLog having lost it.

They are listed at the end now, each saying which of the three it is, and
clicking one selects it exactly like a pass row. The table stops being "what is
coming" and becomes "what you follow, soonest first" — which is what an operator
was scanning it for anyway.

The dropdown stays, and now for one reason rather than by default: it is the
only selector left when the readout column is hidden, which is what an operator
does when they want the map full width during a pass.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 12:01:08 +02:00
rouggyandClaude Opus 5 b15055ba4a feat(sat): follow the azimuth only
A satellite tracker that insists on an elevation motor is a tracker switched off
for nearly everybody. A pass at the edge of the footprint — which is most of
them — never climbs above ten or fifteen degrees for its whole length, and a
yagi's beamwidth swallows that: the bearing alone is enough, and it is how most
stations that work satellites are actually built. The same switch rescues an
az/el station whose elevation motor has failed.

So it is an option, not a silent fallback, because it does cost something: a
bird straight overhead is a moving azimuth and a bearing that means nothing, and
whether to accept that is the operator's call. With it on, any rotor in the list
can be chosen — the PstRotator, the Rotator Genius, the ARCO, the tower already
turned for HF.

That works because the per-backend command dispatch moved out of the three
RotatorGoTo/Stop/Heading methods into linkGoTo/linkStop/linkHeading, so the
satellite tracker drives any of the seven backends through the same code the
compass uses instead of a second implementation of each. GetRotatorHeading loses
sixty lines of near-duplicate switch in the process, and a rotor with no
elevation axis now says so (HasElevation) rather than reporting a zero that
looks like a real bearing.

One trap, with a test on it: the step check compared both axes, so with the
elevation never commanded its difference stayed above the step for the whole
pass and every tick ordered the antenna to the bearing it was already on. A mast
has a finite number of turns in it.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 11:35:23 +02:00
rouggyandClaude Opus 5 8f17416eca feat(sat): the mode and the CTCSS tone where they can be seen
Both were already on screen, in a grey eleven-pixel footnote under the
frequencies, which is not where either of them belongs.

FM and SSB are two different evenings. One is a channel, a tone and a handheld;
the other is a passband, a beam and a VFO walked as the Doppler moves. So the
mode is a coloured badge — in the readout, in the transponder list where a
satellite offering both makes the choice matter, and in the header so it
survives hiding the readout column, which is exactly what an operator does when
they want the map full width during a pass.

The tone gets the weight of a frequency because that is what it is worth: a
repeater called without it does not answer, and what the operator hears is an
empty channel and concludes the satellite is not up. And when a bird needs no
tone it now says so — a blank line could not tell "no tone" from "OpsLog does
not know", and the difference is a pass.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 11:27:01 +02:00
rouggyandClaude Opus 5 fcf00e04f4 fix(sat): draw the footprint for the selected satellite only
A footprint is thousands of kilometres across. A dozen of them overlap into a
wash of translucent circles that hides the coastline, the ground track and the
satellites themselves — and the question a footprint answers, "can I hear it",
is only ever asked about the bird being worked.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 11:20:05 +02:00
rouggyandClaude Opus 5 543550c716 feat(sat): the map says what a pass is worth
An unselected satellite was a four-pixel dot with a one-pixel white edge, and
the basemap decides whether that is visible at all: a pale marker vanishes into
pale terrain, a grey one into a dark ocean, and the operator can switch between
the two in one session. So: a dark halo under a white ring around a larger dot.
Two rings, because no single colour reads on both.

The ones above the horizon now carry their name. Not all of them — a dozen
labels is a map nobody can read — but the two or three an operator is choosing
between right now, which is what saves hovering each grey dot in turn to find
them.

Hovering said "name · elevation · altitude", none of which decides anything.
What decides whether to reach for the radio is how long is left, how high it
will get and where to point, so the tooltip now carries the pass: elevation and
azimuth with its compass point, distance with an arrow for closing or receding,
rise and set with a countdown and a direction, and the peak. A bird already in
view shows its SET countdown instead of its rise — that is the number that
matters at that moment. No pass in the window says so, because a blank reads as
a fault. It costs no extra prediction: the pass list on screen is indexed by
name, and the first entry for a name is its next pass.

The tooltip also had to stop closing itself. The layer is rebuilt every five
seconds as the birds move, and a rebuilt marker is a new marker, so the detail
being read disappeared mid-sentence. The map now tracks the pointer and reopens
the tooltip of the dot it is still on — that one only, so nothing hangs open
once the mouse has moved away.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 11:04:26 +02:00
rouggy cf44b37bf4 feat(sat): a sky plot — the pass seen from underneath it
The map answers "where is the satellite over the earth". This answers
"where do I look", which during a pass is the question that matters.

The projection is the one every tracker uses and every operator already
reads: the centre is the zenith, the rim is the horizon, north is up. So
the radius is (90 − elevation), not the elevation — a bird overhead is a
dot in the middle, and a pass that hugs the rim is one that never rises.
Whether it comes over the roof or along the treeline is something no
amount of azimuth and elevation digits conveys, and one glance settles.

The whole pass is drawn: a dashed track with arrowheads for the direction
of travel, a hollow circle where it rises, a filled one where it sets,
and a cross where the satellite is now — green above the horizon, grey
below, because the numbers are still right down there and nothing can be
worked through the earth.

The track is fetched once a minute, not once a second: the SHAPE of a
pass does not change while it happens. Only the marker moves, and that
rides on the tuning poll that was already running.
2026-09-07 22:50:29 +02:00
rouggy 37dadeda84 feat(sat): the pass list becomes a table worth reading
Headings, because a column of numbers with nothing above it makes an
operator work out what they are looking at every time. A real table, so
the satellite column takes the width the longest name needs — "ZHUHAI-1
OVS-1A" was cut to eight characters in a fixed one.

Colour where it carries meaning, and nowhere else. The maximum elevation
is the quality of the pass, so it is coloured like one: a bird 70°
overhead and one scraping 12° along the horizon are not the same evening,
and the table should say so without the operator reading every number. A
pass in progress is green, one starting within five minutes is amber. And
a dot for the mode: FM and SSB call for a completely different set-up,
and which the next pass is decides whether you reach for a handheld or
for the whole station.

The rotator's baud rate is a dropdown, like every other one in OpsLog.
2026-09-07 22:43:55 +02:00
rouggy b478cbfd2a feat(sat): a wider frequency list, a resizable readout, one locator
Three things reported together from the tab.

The shipped frequency plan went from eleven satellites to twenty-five:
the eight Tevel FM cubesats, EO-88, AO-109, CAS-4A and 4B, TO-108,
GreenCube's single-frequency digipeater, and QO-100's wideband
transponder beside its narrowband one. It remains a starting point in a
file the operator can correct — a transponder gets switched and no
release should be needed to follow it — and the picker still lists every
bird in the element set when the "with a plan" filter is unticked.

The readout column drags to any width between 240 and 720 pixels,
double-clicks back to its default, and folds away entirely. How much map
against how much detail is the operator's call: watching a footprint
cross an ocean and working a pass want opposite things.

And the locator is no longer asked for twice. Passes are predicted from
the station locator, which is set once in Station information; the field
here was only ever for an antenna at another site, so it says so and sits
folded away. Nobody should have to wonder which of two locators is in
use.

Also: "Driven by" is two columns wide. "OpsLog (EasyComm II)" did not fit
in a third of the row, and a truncated choice is a choice that cannot be
read.
2026-09-07 22:36:48 +02:00
rouggy b918a8395b fix(maps): one imagery choice per map, not one for two of them
The world map and the grid-square map shared a single key, so picking
satellite imagery to look at grids repainted the main map too, and there
was no way to have terrain on one and streets on the other. They are
different maps answering different questions, and the imagery that suits
one is not the imagery that suits the next.

Four keys now, one per map, in lib/mapBase beside the remembered views —
named in one place so a rename cannot silently orphan somebody's choice —
and portable, so a copied data folder brings them along. The grid map
inherits whatever was set under the old shared key rather than being
reset to the default: an operator who chose imagery there keeps it.
2026-09-07 21:06:10 +02:00
rouggy 3ed48336af feat(sat): set it up in Settings, work the pass in the tab
Two things belong in different places, and they were in one.

Settings → Satellites now holds the setup: which satellites to follow —
the same two-column shape as the awards, for the same reason, since a
feed carries two hundred birds and an operator works six — and the
orbital elements, their age, the fetch, and pasting your own. Following
none still means every satellite with both elements and a frequency plan,
so somebody who has not chosen yet is not handed an empty tab.

The panel keeps only what a pass needs. A countdown to AOS, or to LOS
once it is up, because that is the number that decides whether you sit
down; a bar for where in the pass you are, since mid-pass the useful
question is not the clock but whether you are past the peak; rise, peak
and set with compass directions, because "rises SW" is a direction to
look in and 213° is arithmetic. Distance, altitude and footprint. And
approaching or receding, which is the sign of the whole Doppler
correction and the only thing that explains why the frequencies are
moving the way they are.

The countdowns run in the browser from two timestamps. Predicting a pass
steps the orbit across a day thirty seconds at a time, which is not
something to do once a second for a clock the page can keep itself.
2026-09-07 15:34:26 +02:00
rouggy 7f03b046ab fix(sat): the locator was already set, and the map was on top of everything
Two things reported together on the new tab.

The locator: the station's is on the ACTIVE PROFILE, not in a settings
key. keyStationMyGrid is a legacy key that EnsureDefault migrated into
the profile long ago and nothing writes any more, so reading it told an
operator with a perfectly good locator on screen that he had not set one
— and refused every pass prediction on the strength of it.

The map: Leaflet stacks its panes and controls up to z-index 1000, which
without a stacking context of their own float over the whole application
— Preferences opened behind the map, its Save and Close buttons under it.
The other three maps in OpsLog each carry `isolate` for exactly this
reason; this one was missing it.
2026-09-07 12:07:12 +02:00
rouggy 90e363f49e feat(sat): point the antenna — EasyComm II az/el rotator
EasyComm is what satellite rotator controllers agreed on, so a box that
works with SatPC32, Gpredict or Hamlib works here. Serial or TCP, and its
own settings rather than the HF rotator's: an az/el pair is a different
machine on a different port, and an operator who has both must not have
to choose.

A great many EasyComm controllers — the Arduino trackers above all —
accept commands and never say a word back. That is legal and common, so a
silent controller is not treated as a broken one: it is still driven, and
the last commanded position is reported in its place, marked as commanded
rather than read. A stuck rotator must not be able to hide behind an
order it never carried out, which is why the panel shows the antenna's
position beside the satellite's.

The 450° overlap is the reason a satellite rotator is worth having, so it
is used: a pass crossing north continues past 360 instead of unwinding
three quarters of a turn with the antenna sweeping the ground. Below the
configured elevation the mast is left alone — the numbers are right all
the way round the orbit, but a rotator that chases a satellite through
the far side of the earth spends the night turning, and a mast has a
finite number of turns in it.
2026-09-07 11:34:35 +02:00
rouggy 465481f8f1 feat(sat): Doppler tracking on the radio
The hard part of satellite tuning is not the arithmetic, it is deciding
who owns the dial. A tracker that forces both frequencies fights the
operator every time they turn the knob to follow a station across a
linear transponder; one that never touches the receiver leaves them
chasing a signal that slides nine kilohertz across a 70 cm pass.

So the operator owns the receiver and the tracker follows them. Every
second it asks the radio where the receiver actually is. Where it put it,
nothing has changed. Further than a dial-turn's tolerance, and the
operator has chosen a station: what they landed on is converted back into
a nominal frequency, and the transmitter is derived from that. Which is
the division of labour on a linear bird — the operator listens, the radio
does the sums.

Three ways to reach the radio, because a satellite pair is a shape of
operating rather than a manufacturer's feature. An IC-9700 or IC-9100 is
asked for its OWN satellite mode: it pairs main and sub, gives full
duplex, and keeps the dials linked the way its designers meant, which is
always better than an imitation built out of split. A Flex gets two
slices, A the downlink and B the uplink, created when missing, because
"slice B does not exist" is not something to make an operator fix at the
start of a ten-minute pass. Everything else gets the downlink, and is
told so — half the job announced beats half the job hidden.

What goes in the log is the NOMINAL pair. Two stations working each other
through a transponder read different numbers off their dials at the same
instant; the only figure they can both agree on is the transponder's own.
FREQ is the uplink and FREQ_RX the downlink — the one place a satellite
QSO differs from every other kind, and the reason FREQ alone cannot
describe one.
2026-09-07 11:27:06 +02:00
rouggy 680bf410fe feat(sat): the Satellites tab
Three questions answered at once, because on a pass there is no time to
go looking for any of them: where the bird is, when the next one comes,
and what to tune. The map draws each satellite's footprint — the honest
answer to "can I hear it", since everything inside the circle has the
satellite above its horizon — and the selected one's path over the
ground. The pass list is every favourite in time order, the one in
progress in green.

The readout shows the corrected frequency large and the nominal one
beneath it. Only one of them, and an operator cannot tell a Doppler
correction from a mistuned transponder.

The map opens on the station rather than the Atlantic, and remembers
where it was left like the others. The panel is mounted only while its
tab is visible: it asks for the tuning once a second, and there is no
reason to compute an orbit nobody is looking at.
2026-09-07 10:56:28 +02:00