Commit Graph
285 Commits
Author SHA1 Message Date
rouggy 1cbf3ecc4b perf(settings): every field in Preferences types into itself first
Preferences is one component holding two hundred pieces of state, and its
biggest panels are eight hundred lines of form — external services, CAT.
A plain controlled input sends every keystroke into that state, so every
character re-rendered the whole dialog, and the letter arrived after the
finger had left the key.

BufferedInput keeps the text where it is typed and hands it up 120 ms
later. A drop-in for Input, so the change is one import rather than a
hundred call sites — which means it has to be right in every shape those
call sites take:

  • Blur flushes at once, so clicking Save cannot lose the last word, and
    so does unmounting: a panel changed mid-word still hands up what was
    there.
  • A value that comes back DIFFERENT from what was sent up is adopted,
    even with the focus in the field. That is what keeps the fields that
    normalise as you type working — the callsign box that upper-cases,
    the port box that drops everything but digits. They echo a corrected
    value, and the correction wins.
  • A value changed from outside while the field is idle wins too: that
    is how loading the settings, or switching profile, refills the form.
  • Checkboxes, colour pickers and file fields pass straight through.
2026-09-08 00:15:44 +02:00
rouggy cdd91ab7e6 fix(ui): one section name, a chosen digital row, no MQTT chip
Three things reported together.

Preferences said the section name twice: a small line above every panel
repeated the heading right underneath it — "GENERAL" over "General" —
while the sidebar next to them already shows which section is open,
highlighted. Two lines and a highlight for one fact; the small line goes.

The band matrix opens on the digital row the operator chooses. Its
digital row has always rotated — DIGI, then each digital mode in their
own list — but it always STARTED on DIGI, so somebody who only works FT8
clicked through to their own mode on every callsign. Settings ▸ General
now says where the rotation starts, and the dropdown offers exactly the
modes the matrix rotates through (the phone-mode rule is now shared
rather than copied, so the two cannot drift). DIGI stays the default: it
is the right answer for anyone working several digital modes.

And the MQTT chip is gone from the status bar. That is the name of a
message protocol, not of anything an operator has — a chip in the status
bar has to say what it is about, and this one told nobody anything. What
it carried is shown in the Chase New panel, which is the place that uses
it; its poller went with it.
2026-09-08 00:10:06 +02:00
rouggy 3c7ea2d894 perf(ui): the dialogs you type in no longer sit on a blurred backdrop
A backdrop-filter covers the whole window and is recomputed every time
anything above it repaints. Behind these dialogs is an application that
never stops moving — CAT polls four times a second, spots arrive, meters
sweep, maps redraw — so the filter was being recomputed continuously, and
each keystroke's repaint dragged a full-window blur with it. That is why
the lag was felt in Preferences and nowhere else, and why fixing the
cluster macros did not end it.

Worse in one place: the cluster server editor opens FROM Preferences, so
its overlay was the SECOND full-window filter stacked over the first.
That is exactly where the delay was first reported.

The dialogs an operator types in for minutes — Preferences, the cluster
editor, the QSO editor, bulk edit, alert rules, award definitions — now
dim the background harder instead of blurring it. Everything else keeps
the blur: a confirmation you click through in a second costs nothing.
2026-09-08 00:00:47 +02:00
rouggy 7ff0c2ac69 fix(settings): Preferences stopped lagging behind the keyboard
Typing a cluster macro cost two things per character. The state lived on
SettingsModal, so every keystroke in one of those twenty-four boxes
re-rendered the WHOLE preferences dialog — every list, every form, every
panel. And the save wrote through to Go and into the database on each
one: a round trip per character.

The editor is now its own module-scoped component with its own state, so
a keystroke re-renders twelve rows. And writeUiPrefDebounced holds the
database write until the typing stops, while the local cache — which is
what everything reads back — is still written at once. Pending values are
flushed when the page goes away, so typing and immediately closing does
not lose the last word.

The behaviour is unchanged: still saved as you type, still no Save
button, because a text box whose contents only take effect on some other
button is how work gets lost.

Also folds the satellite changelog into one [NEW] entry. Satellites are
new in this version — nobody reading the notes has seen any of it — so a
running account of how it was built, tab then tracking then rotator then
where the settings moved to, is the wrong shape. One entry saying what it
does.
2026-09-07 23:25:46 +02:00
rouggy 3e206268b4 feat(cluster): start from a known node instead of a blank form
Setting up a telnet cluster is the step operators get stuck on. The
address and the port are two pieces of information nobody has to hand,
and a typo in either looks exactly like a node that is down — there is
nothing to read, only a connection that never comes.

So the editor offers a list, and choosing one fills the fields. Six to
begin with: F4BPO, DXFun, SOTA, POTA, and the two Reverse Beacon feeds —
listed separately and named for what they carry, because they are one
network on two ports where 7000 is CW and RTTY and 7001 is FT8 and FT4,
and no amount of trying will tell you which is which.

The preset then gets out of the way. Everything stays editable, the name
is only filled when the operator has not chosen one of their own, and a
node typed in by hand behaves exactly the same. Reopening a node created
from a preset shows it selected, so the list also answers "which one is
this".
2026-09-07 23:17:20 +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 2283734210 feat(sat): PstRotator can point the antenna too
It handles azimuth and elevation, and a great many stations already run
it in front of a controller OpsLog has never heard of. For those,
OpsLog talking to the controller itself would be a second program
fighting PstRotator over the same cable — so it hands over the bearing
instead, and lets PstRotator turn the mast.

Both kinds sit behind one small interface, chosen in Settings. Neither is
more correct than the other: the right one is whichever the station
already has working.

The 450° overlap is deliberately NOT applied on the PstRotator path.
PstRotator knows which machine is on the other end and does its own; two
programs each deciding to go the long way round is exactly how an antenna
unwinds in the middle of a pass.

Position queries are asked at most every three seconds rather than on
every tick. A PstRotator query binds a socket and waits up to a second
and a half, and many setups answer nothing at all — so one silence is
enough and it stops asking, reporting the commanded position instead and
saying that is what it is.
2026-09-07 17:11:23 +02:00
rouggy 9dfa6f7d39 refactor(sat): one satellite list, and out of Hardware
Settings ▸ Lists ▸ Satellites is gone. It was a text box an operator
typed their birds into by hand, and it had nothing to do with the
satellites the tracker knew: the same station kept two lists of the same
satellites and they drifted apart. The SAT_NAME box on the entry form now
offers the followed set — or every satellite with a frequency plan when
none is followed — merged with whatever that old list still holds, read
and never written, because what it holds is somebody's past work.

And the satellite section moved out of Hardware, which it never was.
Which birds you chase, where your antenna stands and how old your
elements are is operating; the rotator inside it is the only hardware
there, and one block does not make a section a device.
2026-09-07 15:41:56 +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 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 9ce7cf3b69 feat: a typed watering hole carries its mode; Yaesu RTTY sideband
His log settles the 28.074 case: "SetCATFrequency 28.074 MHz" and the
state still reads mode=USB — nothing sent a mode, so the rig simply
stayed where it was. A spot click has always carried one; a frequency
typed by hand carried none. It now uses the same table and the same
tolerance as a spot (±3 kHz of a known FT8/FT4/JS8 frequency), and only
towards the digital modes: tuning AWAY from one leaves the mode alone,
because there the frequency says nothing about what the operator means.

RTTY on Yaesu is a choice the log cannot make: ADIF records "RTTY" and
the rig has MD06 (RTTY-L) and MD09 (RTTY-U). The older lower sideband
stays the default and a station whose FSK controller wants the upper one
says so once in Settings → CAT.
2026-09-06 23:01:08 +02:00
rouggy ed66e9394b fix(autocall): rest in overs, no phantom miss, and a readable auto readout
The rest between two series was two minutes — four overs on FT8, by
which time the DX has worked four other callers and half the time has
gone. It is now counted in the station's OWN overs and is one by default:
seven calls, one over listened through, and it goes again if the station
is still there. The deadline lands mid-cycle on purpose, so "sit out one
over" means one over rather than depending on a millisecond of clock
skew.

A period the target was DECODED in is never counted as a miss. A period
is judged more than once — decodes arrive in a burst and stragglers
follow — and a later judgement holds a partial view of it, not evidence
of absence: D2ACE answered in the very period the counter then read as a
miss.

The readout moved out of the Auto button and beside it. "Auto D2ACE 1/7
·1/3" read as one number, and the control changed width every period.
The callsign is now the biggest thing on the row, the calls and the
missed periods each carry a label, the miss count appears only once there
is one, and a station being waited for gets its own amber chip.
2026-09-06 14:36:32 +02:00
rouggy 70ada49776 fix: CI-V address 00, simplex uploaded as split, and S/F spots
Three field reports.

Icom CI-V address 00 could not be kept: zero was read as "not
configured" in all three places that validate it, so every save put the
rig back to the IC-7610's 0x98 — and the model dropdown followed, since
it is derived from the address rather than stored. Picking "Other
(custom address)" also had no effect of its own: the list re-derived
itself and snapped back to whatever rig matched. It now stays chosen.

Cloudlog/Wavelog showed "17m/17m" on ordinary FT8 contacts (OE6CLD).
Every QSO is stamped with a receive side equal to the transmit side, and
the uploaded record carried it; Wavelog draws band/band_rx whenever both
are there. In ADIF an absent BAND_RX means "same as transmit", so the
uploaded record now writes the receive side only when it differs. The
copy forwarded to another logger over UDP keeps writing it in full —
that is why it was stamped in the first place (Log4OM reads BAND_RX) —
through its own ForwardRecordADIF.

"S/F" in a spot comment joins superfox / sfox / F-H as FT8.
2026-09-06 13:12:00 +02:00
rouggy 23323c91e0 fix(autocall): the QSO in progress outranks the ladder
Six faults from an evening on 60 m, all in the same family: the engine
judging a station by what the log wants from it and forgetting what is
already under way.

- An exchange was abandoned mid-QSO. The reply lands in the same period
  the ladder is re-read, and that period was judged before the reply was
  taken into account, so a better-ranked caller took the slot from a
  station that had just come back to us. The answer is settled first now,
  and our own report counts as being inside the exchange too — which also
  protects a QSO the operator started by hand.
- A station just picked started with misses against it. Its transmit slot
  was unknown until a second decode, and with the parity unknown every
  period counted, including the one spent transmitting to it.
- The freed slot after "it is working somebody else" was thrown away: the
  period's decodes are in hand, so the next station is picked from them
  rather than fifteen seconds later. Never mid-over.
- Auto-call is never armed from a stored setting — not at launch, not on
  a profile switch. It is the one feature that puts the station on the
  air by itself and OpsLog starts with Windows.
- It says what it is waiting for: a wanted station in a QSO with somebody
  else now shows beside the Auto button instead of looking idle.
- Switching profile left the previous logbook's verdicts on screen. The
  worked-index, chase-new and the frontend's cached verdicts are dropped
  when the logbook changes.

FT decodes: distance column, a message addressed to you set whole in
green (the station you are calling keeps a tint — most of what it sends
goes to other people), badge order L / Wkd / WL, list cleared when the
RIG changes band.

Rotor: new world-map compass from EC1KD's design, with the Ultrabeam boom
and second lobe restored and the compact form preserved; the classic dial
is kept and Settings → Rotator chooses between them. Stop no longer
flickers on a rotor standing still — movement was inferred from a degree,
less than the jitter a controller reports at rest.
2026-09-05 23:02:31 +02:00
rouggy 8dbc4b7e62 feat(ui): the FT decodes panel, two DXHunter themes, and the maps
Decodes:
- one click SELECTS, two transmit. A single click handed the decode
  straight to WSJT-X as a reply, so brushing a row while reading the
  band started calling a station.
- the list empties for a receiver that changes band, and a receiver
  column appears when more than one is feeding one merged list.
- the period clock turns red while transmitting: it is the one thing on
  the screen that moves, so it is where the eye already is.
- a WL badge, after the LoTW "L" — one letter, always in the same
  place, so the column does not shift from row to row.
- the auto-call switch, its target and its count, and the chase list:
  naming the station you are waiting for is done while watching the
  band, not in a settings tree.

Themes: DXHunter's slate with its own blue, and the same slate with
OpsLog's orange. Counted across its sources rather than guessed from
one panel — blue is 132 uses to violet's 25, and the violet is the PSK
Reporter panel alone.

Watchlist: drawn as DXHunter draws it — the callsign in the interface
font rather than monospaced, which is the difference that shows with
the two windows side by side.

FT Map: arcs no longer run off the side of the map. The map shows one
world, and a path crossing the antimeridian was drawn past 180° into
the blank space beside it — from VK that is most of them.

Cluster: "superfox", "fox/hound" and "F/H" in a comment are read as
FT8. They are WSJT-X's DXpedition transmit modes, and the comment fell
through to the band plan and came out DATA — which then decided the
band+mode verdict.

A decoder is named by what it IS: Nexus sends its packets as "Tempo",
the engine inside it, and OpsLog showed a program nobody has heard of.
2026-09-05 21:46:06 +02:00
rouggy be889681a9 chore: release v0.27.12 2026-09-05 19:07:21 +02:00
rouggy 6cbe29fef1 fix(hamlog): stop offering an upload that cannot succeed
HAMLOG.online no longer issues API keys, and its upload API takes
nothing else. An operator without a key cannot obtain one, so the
auto-upload switch, the on-close sweep and the 'Send to' entry were all
arming something that could only fail — silently, once per QSO.

Closed at the source rather than hidden in the UI: the upload returns a
sentinel that says why, the manager stops routing to it and says so once
a session, and the manual path refuses with the same words. The settings
page states it plainly instead of showing a switch that does nothing.

Nothing else goes. Their confirmations arrive as an ADIF FILE and never
needed a key, so that import stays; the sent/received state already in
operators' logs stays readable, filterable and bulk-editable; and the
upload itself is kept whole as uploadHamlogLive, still covered by its
request-shape tests, against the day keys come back.
2026-09-03 22:28:32 +02:00
rouggy f6532b2e85 style(dvk): show the DATA-input option only where it does something
Only Kenwood implements it — TX1 is that family's second transmit
command — and every other backend keys the one way it knows. A checkbox
offered to all of them would change nothing on most, which is the same
dead furniture as ANT2 on a radio with one socket. Gated on the Kenwood
backend, and the label says so.
2026-09-02 19:51:38 +02:00
rouggy 8685dbd6cf feat(dvk): key the DATA input, not the microphone
From a TS-590SG report: the voice keyer played through the rig's USB
codec and the radio transmitted silence. Its manual says why — 'TX P1,
0: SEND (normal transmission using the MIC input), 1: DATA SEND
(ACC2/USB input)' — and OpsLog only ever sent the bare TX, so the radio
dutifully opened a front microphone nobody was speaking into.

An option on the audio page, shown for CAT keying, says where the
keyer's audio actually arrives; the manager routes it to a backend that
draws the distinction and falls back to the ordinary key for every rig
where one PTT is all there is. Test PTT goes down the same path, so it
tests what will happen rather than something adjacent.
2026-09-02 16:17:50 +02:00
rouggy a6172c4323 feat(dxhunter): the chase gets a page of its own, and US states a switch
The chase switches were born on the DX Cluster page because the cluster
was the only thing that drew their badges. They now decide what the FT
decode list and Chase new say as well, and settings that govern three
screens should not live under the name of one of them. They move to a
DXHunter page — named for what it is about, and for what is meant to
land beside it as more of DXHunter's ideas are ported over.

US states join the switchable markers, gated the same way as POTA,
counties, prefixes and squares: badge, filter chip and display marker,
all from the one chaseAllows question.
2026-09-01 16:26:31 +02:00
rouggy de0771d797 feat(cat): USB for digital modes, on every backend
A soundcard mode is USB with audio in the mic path, and that is what
most rigs need. Asking for the mode by NAME is the better answer on a
modern transceiver with a DATA position and the wrong one on an older
set, where the CAT layer resolves 'digital' to RTTY/FSK — OmniRig does
exactly that, per rig file, and there is no arguing with it from here.
The operator clicking an FT8 spot landed in FSK, which keys from a
mark/space generator and cannot pass FT8 at all.

Applied in SetCATMode, the one funnel every backend and every caller
goes through, so it holds for a spot click, the band map and the mode
selector alike. The QSO is still logged as FT8: this is what the radio
is put in, not what the contact was.
2026-08-31 22:26:12 +02:00
rouggy 629bd8d84f feat(confirmations): a HamQTH default, and the pending status stops meaning 'sent'
HamQTH joins the Confirmations page with a sent side only — the site
publishes no confirmation feed, so there is nothing to receive — and it
defaults to R, the same 'still to upload' the other online services get.

That default could not have worked as written. HamQTH and HAMLOG.online
keep their sent state in an ADIF extra, and eligibility blocked on the
key being PRESENT — so an operator setting the natural R default would
have stamped every new QSO 'already gone' and silently disabled the very
auto-upload the default arms. Eligibility now reads the VALUE: the ADIF
pending statuses mean pending, anything else means sent. Guard-tested
both ways.
2026-08-31 16:48:11 +02:00
rouggy 7152d11007 feat(cluster): county and prefix get their chase switches, and the grid one tells the whole truth
Chase POTA already withdrew its badge, filter chip and column when
unchecked; US counties and new prefixes now have the same switch, and
unchecking Chase new grids finally withdraws the NEW GRID badge too (the
backend stopped learning squares but the display kept shouting the ones
it knew — the setting is mirrored to localStorage so the display layer
can gate synchronously). All withdrawal happens at the display layer:
the facts keep being computed, so re-ticking a box needs no rescan.
2026-08-31 16:41:13 +02:00
rouggy 91569e12f4 feat(hamqth): QSO upload to the HamQTH logbook — the 8th external service
qso_realtime.php with the account's callbook credentials (which the
external-services config falls back to when its own are blank), one ADIF
record per QSO, prg=OpsLog. HTTP status IS the answer: 200 saved, 400
rejected (a duplicate counts as delivered, like HRDLog's insert 0), 403
credentials. Sent-state lives in APP_OPSLOG_HAMQTH_SENT extras like
HAMLOG.online — ADIF names no HamQTH field. Auto-upload on log, on-close
batch, right-click Send to, QSL Manager backlog, and a Test button that
authenticates against the callbook login, which cannot touch the log.

Fixes a real mis-route on the way: manual 'Send to HAMLOG.online' had no
branch in runManualUpload and fell through to QRZ.com — the selection was
uploaded to the wrong service with the QRZ key. Both extras-stamped
services now have their own branch.
2026-08-31 14:34:49 +02:00
rouggy 8846cba40c feat(confirmations): a Club Log received default, No out of the box
The Confirmations page's Club Log row gains its received side, applied to
every new QSO the way QRZ.com's already is — so the match download's Y
stands out against a ledger of explicit Ns.
2026-08-31 11:59:14 +02:00
rouggy 3cb8096141 feat(clublog): the matches come home, and Club Log lends its call list
Two new QSO columns, clublog_qso_download_status/date — a Club Log MATCH,
the service's own confirmation (both stations uploaded the QSO, paired
within 15 minutes). Full promoted-column lockstep: migration 0031, repo
insert/scan, ADIF dictionary + import + export (app-defined
CLUBLOG_QSO_DOWNLOAD_*), table columns, filter builder, bulk edit and the
QSO editor's Club Log row.

The QSL Manager's Club Log entry now actually downloads: getmatches.php
with the existing account settings and the embedded application key,
incremental via the match-completion date filter, matched call+band+mode
±15 min with a mode-blind fallback because Club Log reports 'false' for
modes it cannot infer. Matches always exist on both sides, so unmatched
ones are listed rather than skeleton-added.

And Super Check Partial can merge Club Log's weekly SCP list (~180k calls
worked on the air in the last 3 years) with MASTER.SCP — an opt-in
checkbox under the SCP setting.
2026-08-31 10:14:52 +02:00
rouggy 7c10517bf5 fix(chase): each unconfirmed badge keeps its own name; the block moves up
The unconf label was hard-coded to GRID? for every category — a prefix
worked-but-unconfirmed showed as a second grid badge beside the real one,
which read as impossible. Each badge now says its own name plus a question
mark. And the global Chase block moves above the Chase POTA/SOTA/grid
checkboxes, where its operator expected the master switch.
2026-08-30 21:28:52 +02:00
rouggy 4e28098a4e feat(chase): the global hunt — new, or new plus unconfirmed, every category
The gap between worked and confirmed becomes visible everywhere. A Chase
setting on the DX Cluster page picks the mode; in new-plus-unconfirmed the
verdicts for DXCC/band/mode/slot, prefix, county and state are judged against
CONFIRMED-only ledgers built from the operator's chosen sources (LoTW, card,
eQSL, QRZ.com — HRDLog has no confirmation field in ADIF to offer), and a
need that exists only because a QSL never arrived is flagged Unconf: the
cluster and decode badges render it dimmed, the convention the grid hunt
introduced. The grid's own Chase selector folds into the global one; its
square-matching scope stays where it was, being grid-specific. One snapshot
key change rebuilds the cache when any of it flips.
2026-08-30 21:02:34 +02:00
rouggy 12c61dc35a feat(dvk): delete a message — recording and label together
A deleted message is a free slot, per its operator; keeping the label made it
look half-deleted.
2026-08-30 20:15:45 +02:00
rouggy 7e6c0b4f7e fix(kpa): stop switching the KPA500 off, and answer its operator instantly
Three faults, one report. The slow poll asks ^TP — the KPA1500's ATU, which
a KPA500 (no ATU) never answers — and ask() dropped the whole connection on
any read timeout: a two-second stall and a reconnect every slow cycle, which
is why buttons lagged and the status read wrong. Worse, every serial reopen
toggled DTR/RTS — and those lines are the KPA500's POWER SWITCH (that is how
the Elecraft utility turns it on), so the amplifier obediently switched off
twenty seconds after its operator pressed nothing but Standby.

Silence is no longer a dead link (write errors still are), ^TP is never asked
again after one silence, the control lines are asserted once and held, and
the baud field becomes a list of the rates these amplifiers actually speak.
2026-08-30 19:14:35 +02:00
rouggy 721c43d569 style(settings): the Connections panel drops its two explainer paragraphs
Same cleanup as the General and DX Cluster panels: the section speaks for
itself, and the per-service hints remain where the choice is made.
2026-08-30 15:09:08 +02:00
rouggy 392b93f089 fix(audio): the Listen button works with the network stream as the source
Its enable condition predated the network audio: with From-radio empty — the
normal network setup — the button was greyed out, and an operator who had
stopped listening had no way to start again from this panel. The stale 'later'
hints on both buttons said the network paths did not exist; they do.
2026-08-30 02:04:51 +02:00
rouggy 7ac342e2d4 feat(icom): the speaker choice appears beside the RX-audio option too
Same remembered preference as the console's speaker button, surfaced where
the stream itself is enabled — tick RX audio and decide in the same breath
whether it reaches the speakers. Applied immediately, no save needed.
2026-08-29 20:02:28 +02:00
rouggy 6bf759b6ae fix(cw): Icom CI-V CW works over the network backend too
The CI-V keyer is the same 0x17 command on the same IcomController whatever
carries the link; only the warnings and enable checks insisted on the USB
backend, telling a network operator their working keyer would fail.
2026-08-29 16:39:43 +02:00
rouggy 6d4a110949 fix(cat): the settings panel opens on the radio that is actually active
The loader looked for an 'active' flag the radio list has never carried, so
reopening the panel always landed on the first entry — Radio 1's name over
whichever radio's settings were live, and every field edit (MY_RIG included)
silently rewriting the wrong entry. The backend is the only one who knows
which radio is on the air; ask it.
2026-08-29 15:52:37 +02:00
rouggy 6f126802cd feat(icom): the IC-7760 joins the model list
CI-V default address B2h, per the radio's own menu. Both places that know a
model: the settings dropdown and civ.ModelName.
2026-08-29 15:02:23 +02:00
rouggy 7322e2ab66 feat(watchlist): dockable in the Main tab; sticky filters; Needed-only hides worked lines
The watchlist joins the Main-tab pane list (Settings → Main view), rendered in
a card like the other docked panels.

The three filters persist across tab switches — the component unmounts on every
switch, and filters that reset each time are filters nobody trusts.

Needed-only now hides the worked SPOT LINES as well as the all-worked cards,
which is DXHunter's own Not-Worked behaviour and the reported complaint: a
filter that says needed and still lists five green Worked rows answers a
different question than the one asked.

The add-row checkbox is relabelled 'as contest' — it marks the entry being
ADDED, and read as a filter beside the All/DX/Contest buttons it looked
redundant.
2026-08-29 00:39:07 +02:00
rouggy bd8719ce99 feat(cluster): chase switches for POTA and SOTA
An operator who does not chase parks does not want NEW POTA shouting from every
activator spot. Two switches (Settings → DX Cluster, on by default): off, the
marker is withdrawn at the display layer — applySpotDisplay, the same chokepoint
the grid, the band map and the filter read through — so the badge, the colour,
the filter chip and the reference column all go quiet together, and a new-band +
new-POTA spot reads NEW BAND alone. The facts keep being computed; only the
telling stops, so ticking the box back on needs no rescan.
2026-08-28 23:48:22 +02:00
rouggy 5e38319379 style(settings): trim the DX Cluster panel's explanations
Per review: the digital-grouping sentence on the same-slot option, the
spot-lifetime explanation, the ring-buffer paragraph on the spots-kept field,
and the free-public-nodes line all go.
2026-08-28 23:44:01 +02:00
rouggy 06868e9ed1 style(settings): three more explanations off the General panel
The SCP paragraph, the digital-grouping parenthetical and the IARU region hint
all move into tooltips on their own controls.
2026-08-28 22:20:31 +02:00
rouggy 3ed643e072 style(settings): tidy the General panel
Per review: the panel-wide 'saved instantly' hint goes; the date-format picker
loses its embedded sample line (kept as the buttons' tooltip) so the row is one
line tall; the IARU region moves up under the date format, before the themes;
and the parentheticals after Distances-in-miles, Check-updates and the
telemetry opt-out go. The telemetry and ClubLog explanations survive as
tooltips — an opt-out about data leaving the machine, and an option changing
how entities resolve, should still say what they do somewhere.
2026-08-28 22:15:53 +02:00
rouggy f0f9898f7c feat(bandmap): draw the band plan of the operator's IARU region
The band maps carried one hard-coded table — Region 1's — so a US operator's
40 m stopped at 7200 with stations spotted at 7250 hanging past the top of the
map, and the SSB wash started 25 kHz early. The region is a station-level fact
stated once (Settings → General, portable pref); lib/bandplan owns the three
tables and publishes changes to the open maps the way the distance unit reaches
the grids.

The tables stay deliberately coarse: a band map's wash is context, not a
regulatory chart, so only the differences an operator notices are split out —
Region 2's 80 m to 4000 and 40 m to 7300 (phone from 7125), Region 3's 80 m to
3900, the wider VHF/UHF allocations. National fine print does not belong in a
background tint.
2026-08-28 21:55:37 +02:00
rouggy 766b0f95a4 feat(tci): key CW through the radio's own macro keyer
The sixth CW engine, and the one the TCI backend was left without. A SunSDR
keys from the WebSocket that already carries the CAT: no WinKeyer, no second
serial port, nothing to wire.

    CW_MACROS:0,<text>;     send
    CW_MACROS_SPEED:<wpm>;  speed
    CW_MACROS_STOP;         abort

Confirmed against the TCI command table in ars-ka0s/eesdr-tci — the source that
settled SPOT. CW_MACROS rather than TCI 2.0's CW_MSG: it exists from 1.6, and
OpsLog resolves its own variables, so the before/after callsign fields have
nothing to carry.

Commas and semicolons are stripped before sending. They are the protocol's own
separators: a comma would become another argument, a semicolon would end the
command with the message half keyed and the rest parsed as a command.

Only the MACRO speed is set, not the paddle keyer's — an operator who set their
paddle to 28 wpm did not ask for it to change because a macro went out at 25.
And there is no backspace: TCI can stop a message but cannot un-type one, so the
type-ahead correction the Flex engine offers is absent rather than faked.
2026-08-28 13:01:55 +02:00
rouggy 9a21c936b1 feat(units): show distances in miles
Everything is computed in kilometres and converted once at display time, in
lib/units — storing miles anywhere would give the same number two sources of
truth and a rounding error that grows with every hop.

The grids capture the unit inside their column definitions, header and formatter
both, so a change is published to them the way the date format already is;
without that a toggle would only appear after a language change or a restart.
The preference is portable, like the other display ones.
2026-08-28 08:25:02 +02:00
rouggy f98a831195 feat(cat): MY_RIG follows the radio that is connected
With one rig, MY_RIG belonged in Operating conditions and nowhere else.
With several, that becomes the wrong place: operating conditions describe
a PLAN — 'on 20 m I use the beam and the 7300' — while the question a
logged QSO asks is which radio was keying.

So each saved radio carries its own MY_RIG, consulted BEFORE the per-band
station. When the two disagree — a second rig borrowed for one evening on
20 m — the one that is transmitting is the true answer.

Left empty it changes nothing at all: the old chain (band default, then
the profile's rig) answers exactly as it did, which is what every
single-radio station will keep doing without touching a setting.
2026-08-27 01:03:27 +02:00
rouggy 2594697e00 fix(cat): the chip says what YOU called the radio
It showed what the radio calls itself over CAT — 'Kenwood (911)' for a K3,
a bare 'CAT' for a Flex that reports nothing useful. That is the answer to
a question nobody asks once the rig has a name in the list, and it made
the two radios look alike in the one place they have to be told apart.

The name the operator typed comes first now, on the chip and in the menu,
and falls back to the radio's own identity when they typed none. The model
identity is still in the tooltip, where it answers 'what is actually
connected' without taking the place of 'which of my rigs is this'.
2026-08-27 00:42:26 +02:00
rouggy 4689505fb9 feat(cat): several radios, switched from the status bar
The answer to 'I have two rigs' was 'make two profiles', which is a heavy
instrument for a light question: a profile carries the whole station —
logbook, callsign, awards, cluster, macros — so switching one to change
which radio is connected takes all of that with it, including a change of
logbook when the profiles point at different databases.

Radios are a list now, the way amplifiers already are. The CAT chip in the
status bar opens it, one click connects the chosen rig, and nothing else
about the station moves.

The storage keeps the ACTIVE radio in the same settings keys everything
already reads (cat.backend, cat.icom_port, …), and switching writes the
chosen entry into them and reloads the link. So the consoles, the CAT
sharing, the band-follow and every panel see exactly what they saw before
and needed no change at all — the list is a second store beside the
configuration, not a replacement for it.

Details that matter more than they look:
  - An operator who has run one rig for a year opens the list and finds it
    there, because a missing list reads as the CURRENT settings rather
    than as nothing. It is written on the first save, not on the first
    read.
  - Editing the CAT panel updates the active entry, or an edit made before
    switching away would be lost on the way back.
  - A new radio starts as a COPY of the current one: a second rig is
    usually the same shape with one port changed, and an empty form is a
    form to fill in twice.
  - Switching saves the panel first, so edits to the radio being left
    behind are kept.
  - The chip only becomes a menu with more than one radio; with one it
    opens the CAT settings exactly as it always did.
2026-08-27 00:22:19 +02:00
rouggy afde919e5b fix(audio): the radio is offered only where it can actually be
Adding it to the device lists put it in all four dropdowns, and an
operator picked it as their RECORDING MIC — which would have recorded the
station they were listening to instead of their own voice, on every voice
message and every QSO recording. It is not a microphone, and it is not a
pair of speakers either: the audio going the other way is transmit audio.
So it is listed for 'From radio' and 'To radio', and taken out of the mic
and the listening dropdowns.

And the RX monitor is disabled when the radio IS the source, with a line
saying why: the TCI stream is pushed into the QSO recorder, and nothing
plays it to the speakers. The button was there, enabled, doing nothing
audible — which is worse than a button that says it cannot.
2026-08-26 19:04:02 +02:00
rouggy 98c49f6bbc feat(tci): a control console for the SunSDR
TCI already carries the frequency, the mode, the meters and now the audio.
It also carries everything else about the radio — and OpsLog was logging
most of it once as '(unhandled once)' and throwing it away. The console is
mostly a place to put what was already arriving.

That makes it the cheapest panel here, and it is worth saying why. A K3
console costs a command and a reply for every value it shows, which is why
it reads its settings in a rotation and its meters only while on screen.
TCI PUSHES: the radio announces its drive, its filters, its noise blanker
and the rest on connect, and again whenever any of them changes —
including when the operator changes them in ExpertSDR3's own window, which
this panel therefore follows without asking anything.

What it drives: drive and tune drive, mic gain, TUNE, volume, mute,
squelch and its threshold, NB, NR, ANF, APF, AGC speed, the passband, RIT
and XIT with their offsets, and the VFO lock. The S-meter is a real dBm
reading, so its S units are arithmetic rather than the calibration guess a
K3's meter needs.

Setters never update the cached state. The radio answers with the new
value, and taking its word is what keeps the panel honest when a setting
is refused, clamped, or changed at the radio a second later — the one
exception being a slider mid-drag, held for 900 ms so it is not dragged
back by its own echo.

Capped width and centred, like the other consoles. Also offered as a
docked pane — and the Elecraft console is offered there too now: App has
always had that pane, Settings simply never listed it.
2026-08-26 18:31:44 +02:00