Commit Graph
559 Commits
Author SHA1 Message Date
rouggy 13434ca36c feat(icom): phase 5 — the voice keyer plays to the radio over the LAN
The audio session opens its TX side alongside RX (txenable in the conninfo,
16 kHz both ways), the stream gains a paced sender — 320 samples every 20 ms,
the frame mirroring what the rig itself sends on this socket, IDs swapped —
and the network Icom takes the same NetworkPlayer slot a TCI radio does: pick
'Radio (network audio)' as the To-radio device and the voice keyer needs no
cable and no virtual sound card. PTT brackets the message through the same
code as every other audio path.
2026-08-29 19:34:05 +02:00
rouggy 6345710de5 fix(icom): wait out the rig's session cleanup before redialling
The capture shows the spiral: a session goes deaf on CI-V (28 unanswered
commands, transport alive), the fast-fail rebuilds it, the new session answers
for one second — one state read got through — and is then strangled when the
rig purges the half-open old one. Twenty seconds a lap, no audio throughout,
and only the third or fourth session survives. When the previous session died
less than 90 s after connecting, the redial now pauses 20 s so the purge
happens BEFORE the new session, not on top of it. A long-lived session still
reconnects immediately.
2026-08-29 19:21:55 +02:00
rouggy 0cab1c1e6b debug(icom): count the questions asked during a CI-V silence
The operator's counter-hypothesis deserves an answer: RS-BA1 shows no such
dropouts, so are we sure the rig stops answering — or do we stop asking? The
quiet log now carries the number of CI-V commands sent since the last answer.
Zero would convict our own poll loop; a healthy count convicts the rig.
2026-08-29 18:43:46 +02:00
rouggy 1c71495446 fix(icom): give up on the quiet CI-V stream after 15 s, not 30
The capture settled it: the 7760 keeps the transport chatting (pings answered,
no socket error, no loss) while CI-V data simply stops, the in-place reopen is
ignored, and only a fresh session brings it back. So: one reopen attempt at
10 s — kept, it is free and the log will say if a firmware ever honours it —
and at 15 s the link is failed deliberately so the manager rebuilds it,
halving the outage. Also: the recovered-acknowledgement now sits on the DATA
packet, not on any packet — pings were toggling the detector, 160 reopens in
thirty seconds all logged as recoveries that never happened.
2026-08-29 18:35:41 +02:00
rouggy 2c0158b75c fix(icom): re-open the CI-V flow when the rig goes quiet mid-session
The IC-7760 recurrently stops answering CI-V a few minutes into a network
session while the transport keeps chatting — pings answered, session alive,
commands unanswered — and the only recovery was the 30 s watchdog's full
teardown. At 10 s of CI-V silence the pump now says 'open' again on the
existing stream, which is all it should take if the rig quietly closed the
data flow; the watchdog remains as the backstop. The send counters gain a
mutex since the pump now transmits too.
2026-08-29 16:56:06 +02:00
rouggy a2b019b280 debug(icom): the quiet detector watches CI-V DATA, not transport chatter
The first detector counted any packet and never fired: pings and idles keep
flowing while the rig stops answering CI-V commands — which is what the 7760's
recurring silence turns out to be. Now it fires on 10 s without a CI-V payload
and reports the ages of the transport, the last scope frame and the last
socket error, so the next occurrence says exactly what the rig was still
sending.
2026-08-29 16:54:37 +02:00
rouggy 490941f506 fix(icom): only forward-moving audio packets reach the sink
Every audio packet was delivered as if it were the next 20 ms, duplicates and
late retransmits included. The monitor's capped ring quietly threw the surplus
away, so the speakers sounded fine — but the recorder keeps every sample it is
given, and the file came out longer than the QSO, slowed and stuttering, each
lost-then-resent packet heard twice. trackRxSeq now says whether a packet
advances the stream, and only those are handed on.
2026-08-29 16:43:41 +02:00
rouggy 7f328d4eb5 debug(icom): say what the CI-V stream was doing when it goes quiet
A real IC-7760 keeps going silent on the CI-V stream two-three minutes into a
session while the control link stays alive. At 10 s of silence the pump now
logs the last real socket error and the retransmit backlog, and notes when the
stream resumes — the 30 s watchdog that follows cannot tell a dead socket from
a rig that stopped talking, and the next occurrence should.
2026-08-29 16:07:42 +02:00
rouggy c2db4c5f7e chore(icom): stop blaming a hijacker for every silent CI-V stream
The 30 s watchdog message asserted another program had taken the session; a
real IC-7760 went silent once with nothing else on the network, and the
message sent its operator hunting software that was not installed. State both
causes.
2026-08-29 16:04:06 +02:00
rouggy ffbbff80d6 feat(icom): a DATA button, native PSK, real watts — and the audio codec
The console gains a DATA mode button (USB-D, what FT8 and friends ride on);
the PSK button now maps to the rigs' native PSK mode (0x12) instead of erroring
as an unknown mode — rigs without one NAK it, exactly as RS-BA1's button does
there. The power meter shows real watts on the IC-7760, whose meter face runs
to 250 W where a 100 W rig's percentage was the watts. The clickable S-meter
gets a pointer cursor so it looks like what it is.

Network audio: rxcodec 0x04 — the experiments on the real 7760 settle the
codec table (0x10 = 16-bit stereo, 0x02 = 8-bit mono, both heard as garble),
and 0x04 is the 16-bit mono the playback path actually plays.
2026-08-29 15:48:26 +02:00
rouggy bd2edf6624 fix(icom): verify the data flag after a mode set, and correct it with 0x26
The IC-7760 acknowledges the 1A 06 data-mode command and stays in USB-D
anyway, which left the operator unable to get back to plain USB from the
console at all. After every mode set the flag is read back; on a mismatch it
is said again with 0x26 — mode, data flag and filter in one frame, the form
the newer rigs actually honour. Only on a mismatch, so rigs that predate 0x26
never see the command.
2026-08-29 15:36:55 +02:00
rouggy 89e239d83f fix(icom): decode the network RX audio as what the rig actually sends
The first real radio on the 50003 stream (an IC-7760) settles the two guesses
the experimental audio path shipped with. The payload starts at 0x18 — the
packet carries a big-endian payload length at 0x14 (0x500 on every packet) —
not at 0x16, which swallowed two header bytes into the PCM and laid a 50 Hz
click track under everything. And rxcodec 0x10 asks for TWO-channel LPCM,
which the mono playback path rendered as double-speed garble; 0x02 asks for
the one channel the monitor plays.
2026-08-29 15:32:44 +02:00
rouggy ad82c21dbc debug(icom): log the raw packets seen during a control handshake
A rig that answers something unrecognised (a newer model, another firmware)
and a rig that answers nothing are different faults; a silent timeout hides
which. Capped at a dozen packets so a working handshake cannot flood the log.
Prompted by the first IC-7760 network attempt — which turned out to be aimed
at the console's IP, where nothing listens; the remote server lives on the RF
deck.
2026-08-29 15:12:12 +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 704b614c38 fix(elecraft): calibrate the S-meter, and stop the SWR spike lingering
The S-unit mapping was provisional, waiting for a real radio. One arrived:
side by side with a K3's own display, raw 5 reads S7 and raw 9 reads S9+20,
so S9 sits near raw 6.5 — not 9 — and each raw step above it is worth ~8 dB.
The old scale showed everything two S-units low.

The SWR meter borrowed the power meter's needle inertia — hold the peak, then
close a quarter of the gap per poll. The K3 throws a brief SWR spike as an FT8
frame ends, and that decay turned one bad sample into twelve seconds of red on
the next transmission. SWR now holds the peak for the same 1.5 s and then
snaps back to the live reading: it is a warning light, not a needle.
2026-08-29 13:52:40 +02:00
rouggy e93d2d36a8 feat(watchlist): the ClubLog expedition enrichment — phase 2 lands
clublog.org/watch.php, per entry: the DXpedition flag, OQRS, LiveStream, the
log's QSO total and the last-24h rate — the fields the schema has carried since
phase 1. Refreshed on DXHunter's own cadence (hourly for expeditions,
six-hourly for the rest), two at a time with a breath between requests: the
application API key is shared by every install, so a hundred-entry list must
read as a trickle at ClubLog's end. Nothing to configure — OpsLog's own key,
already used for cty and Most Wanted, serves.

A starred pattern is asked about by its base (ClubLog has no log for VK9*).
The card gains the 24h rate beside the QSO total and the Live link to the
expedition's stream.
2026-08-29 01:49:13 +02:00
rouggy c9e98d5fdd feat(watchlist): exact match by default, * for a family
The prefix matching ported from DXHunter swallowed too much: a three-letter
special-event entry N8W lit up NEEDED for N8WCR, a different station entirely.
The implicit becomes explicit — a bare entry matches exactly that call, a
trailing star makes it a family: VK9* catches every VK9…, RI0SP* the
expedition's portable forms. A star anywhere else (or alone) is refused at Add
rather than silently matching nothing. Same rule in the backend Match and the
tab's own matcher, pinned by test.
2026-08-29 01:25:34 +02:00
rouggy 441eb295c6 feat(watchlist): header counters, mode filter — and the alert checks worked FIRST
DXHunter's header row, ported: Watchlist / Active / Needed counts up front and
the All-Modes select beside the other filters (DIGI matches the digital class,
SSB folds USB/LSB). Counters, card lists and the Active/Needed-only filters all
read the same mode-filtered view, so the numbers add up to what is on screen.

And the notify alert now asks the SAME worked-slot question the tab asks —
before making a sound. It used to fire on the raw spot while the tab's verdict
arrived on a debounce, so the bell rang for a slot that showed Worked a moment
later. Judged in the backend at emit time: exact slot for named modes, digital
class for generic DATA, today-only for contest entries.
2026-08-29 01:03:37 +02:00
rouggy f6b70184a7 chore(qso): drop the redundant SlotsSince free function 2026-08-29 00:26:32 +02:00
rouggy e4abcda94f feat(watchlist): the DXHunter watchlist as an OpsLog tab
The concept, transplanted: a list of callsigns or prefixes being hunted,
matched against the live spot stream, one card per entry with the spots
underneath and the two questions that matter answered on every line — is this
slot still needed, and what is it worth (the cluster's own NEW badges, read
from the same status index).

The file is DXHunter's own watchlist.json, field for field, ClubLog block
included though phase 2 will fill it — a file that round-trips unchanged is the
whole of 'same format', and a test pins it with a real DXHunter entry. Global
(dataDir), not per profile.

CONTEST is per entry, not the global mode DXHunter has: a contest entry is
judged against the current UTC day — the boundary lives in the query's date
bound, so midnight needs no timer and resets nothing. Normal entries read the
same in-memory worked index the alerts use. Prefix matching is why RI0SP
catches RI0SP/MM, pinned by test.

Notify goes through the existing alert:fired event — the frontend already
toasts and sounds it — throttled to one alert per entry per two minutes,
because a DXpedition lights every skimmer on the planet.

Tab wired like NET Control: opt-in from Tools, persisted, closable. Single
click fills the callsign, double click works the spot — the cluster's own
gesture, kept.
2026-08-29 00:25:40 +02:00
rouggy 284ee4ba7c feat(bulk): My DXCC / My CQ zone / My ITU zone
The My-station group covered nineteen fields and not the three numeric ones.
They take their own integer path rather than the generic text setter: the
columns are nullable integers, and while SQLite would coerce '14' quietly, a
shared MySQL logbook would not — and empty must become NULL, never ''. Bounds
checked (DXCC <1000, CQ 1-40, ITU 1-90) so a slip cannot stamp zone 400 across
a thousand rows.
2026-08-29 00:13:47 +02:00
rouggy 10ef984962 feat(k3): align RIT/XIT and the power step with the other consoles
'None of the consoles look alike' is the complaint ShiftRow exists to answer,
and the Elecraft panel was still driving its offset with a row of nudge buttons.
It now uses the shared control — ±, wheel, typed value — plus the same
Ctrl+←/→ 10 Hz clarifier the Icom and TCI panels have. The K3 keeps ONE offset
for RIT and XIT, so both rows show it, exactly like the Icom's.

The backend gains the absolute setter the shared control speaks
(SetKenwoodRITOffset, funnelled through the same RO write as the nudge), and
the power slider moves in 1 W steps — nobody asks a K3 for 'between 10 and 15
watts' in fives.

Also per review: the chase switches are labelled 'Chase POTA' / 'Chase SOTA',
matching 'Chase new grids' beside them.
2026-08-28 23:56:49 +02:00
rouggy 7e7ad50f60 feat(k3): a mode row on the Elecraft console — CW/USB/LSB/DATA/DATA-RTTY
The report: switching the K3 to a data mode 'does not work for FT8 — the K3
does not go into DATA'. The mode digit is only half the answer on that radio:
MD6 keeps whatever DT submode the previous session left, and with FSK D still
armed the rig keys FT8 with no rear-audio modulation.

The panel buttons say the whole thing: DATA is MD6+DT0 (DATA A, the soundcard
path), DATA RTTY is MD6+DT2 (FSK D). The DT submode is also read on the
settings beat — Elecraft only, a plain Kenwood would '?;' it — so the active
DATA button follows what the rig is actually in, front-panel changes included.
A dedicated SetKenwoodPanelMode rather than the logger's SetMode: a panel
button is the operator saying exactly what the rig should do, not an ADIF mode
to be mapped through preferences.
2026-08-28 23:35:25 +02:00
rouggy e4828482c4 fix(tci): send the sensor subscription in both cases
Two rounds of fixes and the SunSDR's meters are still silent. The one remaining
difference from the protocol document is the case: its examples read
TX_SENSORS_ENABLE:true,200; and ours went out lower-case. Every other command
works lower-case, but nothing proves this newer path does — a case-insensitive
server ignores the duplicate, a sensitive one finally hears it.
2026-08-28 22:54:44 +02:00
rouggy 8acfda4e42 fix(tci): resubscribe to the meters at 'ready', and log what comes back
The report from a real SunSDR: transmit power and SWR still empty after the
sensors fix. The subscription went out once, at connect — while ExpertSDR3 is
still streaming its initial state dump, exactly where a unidirectional control
command can be ignored. It is now renewed every time the server says 'ready'.

The sensor messages also join the logged-on-arrival set, capped like the rest:
the next log will say whether TX_SENSORS ever arrives, which is the question —
a radio that never sends it (AetherSDR may not) and a frame that arrived and
was dropped leave the same blank meters and need opposite fixes.
2026-08-28 22:51:25 +02:00
rouggy 73cae855ed chore(labels): park the label designer and printing on feature/labels
The feature needs more rounds than the next release can wait for, so main goes
back to before it: the packages, bindings, migration, UI and i18n all move to
the feature/labels branch, which holds every commit. The 0.26.23 block keeps
only the Station column.

The 0031 migration may already have run on a machine that launched a dev build;
the two label tables it created are inert and the recorded migration row is
harmless — the runner only applies filenames it has, so re-adding the migration
when the branch merges will skip cleanly there and apply everywhere else.
2026-08-28 21:35:50 +02:00
rouggy 5ee0ade54b feat(labels): print the labels — queue, address review, PDF, log update
The printing session in three steps. The worklist is the paper queue (ADIF
qsl_sent R/Q), grouped by callsign since several QSOs of one station share a
card. Each recipient is reviewed before anything prints: routing first — via
manager when qsl_via says so, direct when an address is known, bureau otherwise
— then the address itself, editable and fetchable from QRZ (the MANAGER's
address when routing says via). What is printed is the reviewed text verbatim,
not a re-resolution that could differ from what was checked.

One PDF per label kind, never one file for all: a roll printer holds one stock
at a time, and a file mixing 29 mm addresses with 62 mm QSO labels could not be
printed at all. Pages are rasterised by the designer's own renderer at the
stock's dpi and carried into the PDF untouched — internal/pdf is a hand-written
image-page writer (DeviceGray + flate: monochrome, lossless, no cgo) because
that is the entire need.

Bureau stations get no address label (no envelope); return labels are printed
one per envelope. Finishing offers the log update: QSL_SENT=Y, the chosen date,
via B or D per routing — through the same BulkUpdateQSL the paper view uses.
2026-08-28 19:58:37 +02:00
rouggy 1a169fdb4f feat(labels): the Label Designer — QSO and address labels for paper QSL
The designer's data model lives in internal/labels: STOCKS are the physical
roll in the printer (geometry in mm, margins, dpi — seeded with the common
Brother DK sizes, the QL family being what prompted the feature), TEMPLATES are
one design each, of two kinds: the QSO label glued on the card, whose repeating
table carries several contacts of the same station, and the address label for
the envelope, whose lines collapse when a variable is empty.

Everything is measured in millimetres — labels are sold in mm, and an operator
lining a design up against a physical sticker thinks in mm; pixels exist only in
the renderer, at the stock's dpi. The canvas renderer is shared between the
editor's preview and the future print path, so there is no second
implementation for the preview to disagree with.

The preview is fed with the log's latest contacts rather than lorem ipsum: real
data shows a too-narrow column immediately.

Printing (PDF, one page per label at exact size) is the next module; nothing
here prints yet.
2026-08-28 19:41:15 +02:00
rouggy 47ddbed665 fix(tci): send the spot colour as an unsigned ARGB integer
Expert Electronics' own document gives the command whole:

    SPOT:RN6LHF,CW,7100000,16711680,ANY_TEXT;

16711680 is 0x00FF0000 — positive. This backend sent the same field as a SIGNED
32-bit value, from a third-party example: with the alpha byte set to FF for
opacity, 0xFFFFA500 becomes -22336, and a spot whose colour ExpertSDR cannot
read is dropped without a reply or an error.

Reported on ExpertSDR3 1.3, which speaks TCI 2.x — the protocol version was
never the problem, and my first reading of that report was wrong.

The first three spots are now logged verbatim: an ignored spot leaves no trace
whatsoever, so that line is the only evidence of what went out.
2026-08-28 16:23:32 +02:00
rouggy fd7ae77a61 fix(tci): subscribe to the radio's meters; fix the new-spot count; explain an SMTP refusal
TCI meters: the S-meter came only from RX_SMETER and the transmit meters from
TX_POWER / TX_SWR — commands ExpertSDR3 does not send. The protocol's answer is
a subscription (RX_SENSORS_ENABLE / TX_SENSORS_ENABLE, §4.4 of the TCI PDF),
after which the radio pushes RX_CHANNEL_SENSORS and TX_SENSORS. Nobody had
asked, so the console's meters sat empty in RX and in TX while everything else
worked.

Cluster: the held-spot counter looked for the row it froze on. A station spotted
again REPLACES its row, so that row vanishes in the ordinary course of things
and the count fell through to 'everything is new' — 4, 5, then 500. Counted by
timestamp now, which survives both the replacement and the ring buffer.

SMTP: '535 5.7.139 basic authentication is disabled' is a policy, not a typo.
The message now says so, and says what actually helps, without claiming a policy
when the server simply rejected the password.
2026-08-28 15:29:44 +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 d534825e92 fix(tci): pick the sideband from the frequency just commanded
Clicking a spot sets the frequency, then the mode 150 ms later, and SetMode
chose USB or LSB from t.freqA — the frequency the radio had ECHOED. From 7 MHz
onto a 14 MHz spot that echo has often not landed, so the sideband was computed
for the band being left: LSB on 20 m. Clicking the same spot again, the echo
having arrived, gave USB. Reported from a SunSDR as 'the frequency is right, the
mode is wrong until I click twice'.

SetFrequency now records what was asked for and SetMode prefers it until the
radio's own echo confirms the move.

The server's 'protocol:' announcement is also kept instead of being filed under
unhandled: it names the ExpertSDR and its TCI version, and panorama spots need
TCI 1.5 — older servers accept the spot commands and silently drop them, which
is indistinguishable from a broken logger. Now it says so.
2026-08-28 12:42:05 +02:00
rouggy 65cae0d822 perf(lotw): stop asking for the QSL details by default
A tester compared LogHX's two download options on the same account: two minutes
for the confirmations, twenty for the confirmations with QSO details. OpsLog was
always sending qso_qsldetail=yes — LogHX's slow option — which is the whole of
the wait people were reporting.

Marking a confirmation needs call, date, band and mode; the detail adds the QSL
date and the station's grid, state and county. Now a choice, off by default, and
forced on when the download is also ADDING the QSOs it cannot find — the one
case where those fields have no other source.
2026-08-28 08:54:42 +02:00
rouggy 0143a84cee fix(lotw): stop the heartbeat when the report starts arriving
Deferred, it stopped at the end of the function — so 'still waiting for LoTW to
build the report' kept printing between the megabyte lines, saying the opposite
of what was happening.
2026-08-28 08:43:06 +02:00
rouggy 82721110ed fix(lotw): count out the wait before the first byte
The progress lines only start once bytes arrive, and on a large account nothing
arrives for minutes — LoTW builds the entire report before sending any of it.
That silence WAS the complaint: the window still just said 'working'.

A heartbeat every fifteen seconds until the headers land, then the HTTP status,
then the megabytes — the first at 256 KB rather than 1 MB, so the very first
sign that it is moving comes early.
2026-08-28 08:38:34 +02:00
rouggy aec363b152 fix(lotw): show the download moving, retry a busy server, fail a dead one
Three reports of the same shape: a 503 for anything wider than a few days
before, and since the timeout was raised, a window that sits at 'working'
forever. Both are the same missing thing — nothing said what the transfer was
doing.

The body is now read in chunks and every megabyte is reported. A 503/502/504 is
retried twice, 20 s then 40 s, each attempt announced: LoTW answers 'busy' to a
wide report often enough that other loggers simply ask again. And the deadline
is no longer on the whole exchange, which either cut off a healthy slow download
or hid a dead one for twenty minutes — it is ten minutes to START answering
(LoTW builds the whole report first) and two minutes of silence once it has,
which is the difference between slow and dead.
2026-08-28 08:29:26 +02:00
rouggy 9270b227b0 fix(lotw): send a date even for 'all', and give the report time to arrive
Without qso_qslsince LoTW does not answer 'every confirmation' — it answers
with a few recent ones, as a 200 and a valid ADIF. 'All' therefore downloaded a
nearly empty report and said 'matched 1 of 1' on an account holding twelve
thousand. It now asks from 1945-11-15, older than any QSO LoTW accepts.

And the request had two minutes: LoTW spends several of them building a full
account before the first byte, so asking for everything ended in 'context
deadline exceeded while reading body' — a network-shaped message for a
too-short deadline. Twenty minutes, and the read cap raised past the 18 MB such
a report actually weighs.
2026-08-27 23:15:54 +02:00
rouggy cff590fe8a fix(lotw): keep another station's confirmations out, and show a suspect report
Unscoped, the report carries every station on the account — including the calls
belonging to another profile's logbook. Those match nothing here, and with 'add
the ones not found' ticked they would pour a second log into this one. The
station callsigns this logbook actually holds are now the filter: a portable
worked here is kept, an expedition call never used here is skipped and counted.

A near-empty report is also the failure that reads as success — 'matched 1 of 1'
where the account holds twelve thousand. Under 4 KB, what LoTW answered is shown
verbatim instead.
2026-08-27 23:12:44 +02:00
rouggy 08d316b1e0 feat(cluster): a SOTA column, and a spot click that credits the summit
The SOTA feeds put the summit in the spot's own text, so there is nothing to
poll: a regex on the comment gives the reference, and clicking the spot writes
SOTA@<ref> into the award references the way a park already wrote POTA@<ref>.

The regex is narrow on purpose — association/region-NNN, anchored — because
POTA (US-4475), WWFF (DLFF-0001) and portable callsigns (DL/SP9DPM/P) share
that field and none of them is a summit. Pinned by a table test.

Off by default: the reference is only there on the summit feeds, and an empty
column for every operator who does not chase them is worse than a checkbox.
2026-08-27 21:33:17 +02:00
rouggy fa1c41388d fix(icom): say when a radio has no scope stream instead of drawing nothing
The CI-V trace settles it: the IC-7851 answers 27 10 01 with FB and 27 14 00
with its mode, but refuses 27 11 in BOTH shapes — it controls its scope over
CI-V and never streams it. Its last firmware is from 2016, older than the
waveform stream itself; Hamlib's caps claim otherwise, the radio is the
authority.

Rejected both ways is a permanent answer, so it is remembered and reported:
the panadapter says the radio does not send its scope, which is the one thing a
black rectangle could not say.
2026-08-27 16:30:39 +02:00
rouggy d2cfad490a revert(icom): keep 0x27 0x11 as the scope data switch, log what the retry got
Hamlib documents the spectrum scope on the IC-785x, so a rejected 0x27 0x11 is
not proof the command is missing — and taking 0x27 0x10 for the data switch
would have blanked that operator's own scope screen when OpsLog disabled it.

Both shapes were refused within 96 ms, so neither was a timeout, and guessing
the next subcommand is how the last two rounds went. The retry now logs the
second attempt's own error and the failure points at the CI-V trace.
2026-08-27 16:25:29 +02:00
rouggy 69d78ba8c0 fix(icom): fall back to 0x27 0x10 when a rig has no 0x27 0x11
The IC-7851 rejects 0x27 0x11 in BOTH shapes: on that radio 0x27 0x10 is the
waveform-output switch itself, not a display switch. Rejected both ways, take
0x10 as the output command and switch it off on disable too — there it stops a
stream rather than blanking the operator's own scope screen.

A mode/span/edge answer also settles the selector question directly: three bytes
or more means the rig wants the main/sub form. Worth reading, because a
wrong-shaped SET is answered with silence on this firmware, and a timeout is
something the rejection retry cannot act on — which is why the fixed/centre
sets were timing out.
2026-08-27 16:18:55 +02:00
rouggy 9cf1984fb2 fix(icom): ask the radio which shape its scope commands take
The IC-7851 rejects "27 11 01" and wants "27 11 00 01" — the main/sub selector
its two scopes need. The shape was picked from a list of CI-V addresses, so an
address not on it got the wrong one and the scope stayed blank with a rejection
in the log and nothing on screen.

A rejection is an unambiguous answer, so a rejected 0x27 set is now re-sent in
the other shape and the winner remembered. A TIMEOUT deliberately is not
retried: several firmwares never ack a 0x27 set, and treating silence as a
wrong guess would flip a rig that was already working.
2026-08-27 16:14:53 +02:00
rouggy ee8fd32bf7 fix(matrix): dot the portable variants too
The header counts QSOs with the callsign through callMatch — RI1FJL/1 and
ZA/RI1FJL included — while the grid's own per-callsign columns matched the bare
string. A cell could hold one of the QSOs the header had counted and show
nothing.
2026-08-27 15:57:41 +02:00
rouggy 965ed4c792 feat(matrix): mark the slots already worked with the callsign in hand
The worked-before cell answered one question with two facts fused into it.
bandStatusCode takes the highest of them, so a confirmed entity outranks a
worked callsign — right for awards, wrong while chasing: a slot worked with the
DXpedition this morning reads "entity confirmed" and says nothing about it.

BandStatus now carries the callsign's own answer alongside the entity's, and the
cell draws a dot for it. The dot is deliberately identical everywhere — same
shape, same colour, over all five backgrounds — so it is read as one thing
rather than as five variants of the colour underneath it.
2026-08-27 08:55:37 +02:00
rouggy b487ca1145 merge: the SunSDR console, working this time
Everything here came from one operator with the radio in front of him, and
most of it was mine to fix. The drive commands need the transceiver index
('drive:0,15;', not 'drive:15;') or the radio ignores them without a word.
The console now holds a click the radio does not echo, so a working button
stops looking dead — and lets go the moment the radio contradicts it, so a
REFUSED setting still tells the truth. Filters per mode, APF only in CW,
levels on wide rows with typed values, the RIT control shared with the
Icom console, the transmit meters, and a TUNE that can be switched off
again.

Two findings worth keeping: this radio re-asserts sql_enable eighty
milliseconds after being told to turn the squelch off, and it never
answers TX_POWER or TX_SWR — both are its own doing, and both are visible
in the log rather than argued about.
2026-08-26 22:07:24 +02:00
rouggy 27b342a5e7 chore(tci): cap the diagnostic logging per message type
The transmit meters are asked for four times a second while keyed, so
logging every arrival would fill an evening's log — and a diagnostic that
fills a log is one that gets switched off instead of read. Twenty of each
is enough to tell an answer from a silence, which is all it is for.
2026-08-26 22:07:11 +02:00
rouggy c1cd76bbcf fix(import): a deleted DXCC entity is not re-stamped from cty.dat
Reported with a Logger32 export: the record said <DXCC:3>151 and OpsLog
stored 54.

cty.dat answers one question — where is this callsign TODAY — and that is
the wrong question for a log. R1MVI was Malyj Vysotskij Island, entity
151, until the ARRL deleted it in 2012; the same call resolves to European
Russia today. Forcing the lookup onto a 2004 contact therefore destroys a
credit that can NEVER be worked again, because the place no longer counts.

So the ADIF's number wins whenever it names a deleted entity, and nothing
is corrected on those records at all — the country name and the zones of a
deleted entity are equally beyond what a present-day prefix table knows.
The list is the ADIF specification's own enumeration, sixty-odd entities
that only change when the ARRL deletes another one.

The guard sits in the forced path, so it also covers the grid's 'Update
from cty.dat' on a selection — the other way an operator could have lost
the same credits, one right-click at a time.
2026-08-26 21:59:53 +02:00
rouggy 130c4e72e0 chore(tci): log the transmit meters as they arrive, or fail to
The log shows 'tx_power;' going out four times a second during a tune and
nothing coming back — but that proves less than it looks: a reply that
arrived and failed to parse leaves exactly the same trace as one that
never came. Both are now visible.
2026-08-26 21:28:09 +02:00
rouggy 980c54a616 fix(tci): TUNE can be switched off again, and the meters watch it
Two faults with one cause: this radio does not echo 'tune:0,true'.

So the panel never knew a tune was running. The button stayed on TUNE and
every further press sent another START — there was no way to stop it from
here at all. The state is recorded when the command is sent now; whatever
the radio says afterwards still wins, it simply never says anything.

And the transmit meters were asked for only while t.tx, which a tune
carrier does not set: the radio reports tuning as its own state, not as a
transmission. So power and SWR sat at zero for the whole tune — the exact
carrier an operator holds a tune for in order to watch an SWR on. They now
follow PTT or TUNE, and the S-meter reads '—' under our own carrier
either way.
2026-08-26 21:25:06 +02:00