Commit Graph
209 Commits
Author SHA1 Message Date
rouggy 1b746f2452 fix(icom): CI-V idles carry real sequence numbers
Every idle went out with seq 0, seven a second, interleaved with
properly-numbered data — where RS-BA1 and wfview draw idle sequence numbers
from the same counter as everything else. A rig that follows the sequence
tolerates the zeros for a minute or so and then stops serving CI-V data on
the stream: the shape of every dropout the loaner IC-7760 has shown, with the
scope and the TX path both since eliminated as causes. Idles now take the
next tracked seq and sit in the retransmit buffer like any other packet.
2026-08-30 03:08:59 +02:00
rouggy 3db49e1213 debug(icom): say whether the split mode-align was accepted
The 0x26 01 copy was fire-and-forget, and a real 7760's sub VFO kept
yesterday's mode with nothing to show why — refused, or sent with a stale
mode byte. The mode is read first when unknown, and both outcomes are
logged.
2026-08-30 03:02:06 +02:00
rouggy 605c934d33 style(icom): the shared LED meters, and the sideband named
The console's thin home-made bars were the one holdout — every other panel
(Flex, Elecraft, the amps, the tuner) draws the same MeterBar. And the mode
badge said SSB, which on any band leaves the operator guessing which
sideband the rig is on: the panel now shows USB or LSB (the log keeps ADIF's
SSB).
2026-08-30 03:00:58 +02:00
rouggy fc080f3719 fix(icom): kill the leftover waveform in both command forms, and say so
The blind kill at connect still left a real 7760 streaming — the selector
byte differs by model, and the result was discarded so a quiet failure looked
like a cure. Both forms go out now (a NAK costs one frame) and the outcomes
are logged. The power table also gains its third measured anchor: a real
200 W reads full deflection, so raw 213 is 200 W — not the printed face's
250.
2026-08-30 02:43:43 +02:00
rouggy dc94aa94b6 fix(icom): needle inertia for the TX meters, and room for three digits
The CI-V meters are point samples: between two SSB syllables a poll lands on
0 W and a perfect SWR, so a 150 W transmission read 0-10 W most of the time
and the SWR sat at 1.0. Power now decays like a needle and SWR holds its peak
then snaps to the live value — the same meterPeak the Yaesu and Kenwood
consoles already use. The readout column also widens so '180 W' stays on one
line.
2026-08-30 02:40:07 +02:00
rouggy db7ac771b1 feat(icom): the ATU can be taken out of line
TUNE (1C 01 02) started a cycle; nothing sent the 0 that puts the tuner back
through. An ATU chip beside SPLIT now toggles it, and the tuner state is read
on the slow front-panel beat so the radio's own TUNER button stays in sync.
2026-08-30 02:36:46 +02:00
rouggy b12011fab7 fix(icom): send the leftover-waveform kill blind, at connect
The first attempt sent it only after CI-V answered — but the waveform flood
is what stops CI-V answering, so a rig wedged streaming never received its
cure. It now goes out at the end of Connect, before anything waits on a
reply: an unanswered set costs one frame, and the rig acts on what it decodes
whether or not we hear the acknowledgement.
2026-08-30 02:33:19 +02:00
rouggy d62cd10478 fix(icom): switch off a leftover waveform stream at first contact
The 0x27 output flag lives in the RADIO and survives our sessions: a scope
enabled by an older build (or another program) kept flooding every new
session with 700-byte frames — and on the IC-7760 that flood is what kills
the CI-V link. One 'waveform output off' as soon as the rig answers, once per
session; the front-panel display is deliberately untouched.
2026-08-30 02:28:41 +02:00
rouggy 7de7cb96c3 fix(icom): one sequence space for everything sent on the audio stream
The voice keyer and the live microphone each numbered their packets from 1.
A message played after a talk session re-used sequence numbers the rig had
already seen and was discarded wholesale: the PTT keyed for the full length
of the message and none of it was modulated. PlayTX now feeds the same framer
and counters as the microphone.
2026-08-30 02:25:37 +02:00
rouggy f98e95fe9e feat(icom): PTT and offset split at the operator's hand
The MOX and SPLIT controls lived at the bottom of the Transmit card, below
the fold on most layouts — a console whose PTT needs scrolling is not a
console. A strip under the mode row now carries the PTT and Split as
OFF/+1k/+5k/+10k, DXpedition style, on a new SetIcomSplitOffset that takes
the chosen TX offset (0 keeps the old CW/SSB convention). The mode-alignment
from the previous fix rides along on every engage.
2026-08-30 02:15:16 +02:00
rouggy 9c459c754f fix(icom): standby is not a fault — the quiet recovery needs prior CI-V
A radio in standby is silent on CI-V by design; only the transport chats.
The quiet-recovery added for the mid-session deafness treated that silence
as the fault it usually is and tore the session down every 15 s — taking the
console and its ON button with it, so a radio that was off when OpsLog
started could never be turned on. The recovery now arms only once the
session has heard at least one CI-V payload.
2026-08-30 01:55:35 +02:00
rouggy 77aba73096 fix(icom): a disconnected radio says so, not 'use the USB sound card'
The TX-audio entry points type-assert the transport; a nil port failed the
assertion and produced the sound-card message, sending an operator whose
radio was simply off — or on a new DHCP address — hunting through audio
devices.
2026-08-30 01:36:37 +02:00
rouggy da8f60a7b3 fix(icom): the SUB dial is always shown, and split aligns the TX mode
The unselected VFO was read only when split was on, so a dual-receiver rig's
second dial sat blank on the console. It is now read on the same slow beat
regardless. And engaging split copies the main's mode (and data flag) onto
the TX VFO with 0x26 01 — a split whose TX VFO still speaks yesterday's mode
transmits FM into a CW pileup; rigs that predate 0x26 NAK it harmlessly.
2026-08-29 21:53:16 +02:00
rouggy e8b5444fc2 feat(icom): live microphone over the network — the last remote brick
The talk button learns the network road the voice keyer already took: when
To-radio is the radio itself, the microphone is captured and re-framed into
the rig's 320-sample packets, the capture callback serving as the clock — the
mic delivers in real time, so no ring and no pacer. Counters and the frame
remainder live on the audio stream, so a talk session spans calls. PTT keys
before and releases after, through the same code as the USB path.

With this, a network Icom is a complete remote station over three UDP ports:
RX audio to the headset, live voice and recorded messages back, CW through
the rig's keyer, CAT for everything else.
2026-08-29 21:51:37 +02:00
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 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 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 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 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 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
rouggy 1201b44908 feat(tci): the transmit meters, and no temperature invented
TX_POWER and TX_SWR are read-only commands the radio answers when asked,
so they are asked for on each poll of a KEYED radio and not at all
otherwise: a receiving station pays nothing for meters nobody is watching.
Both appear next to the S-meter only while transmitting, because showing
them the rest of the time would show the last thing that happened as if it
were now.

An SWR of 0 draws as '—' rather than as 1.0. A perfect match on an antenna
nobody has measured is the one reading an operator should never be handed.

There is no temperature. The protocol's command list has TX_POWER and
TX_SWR and nothing thermal at all — so rather than leave the question
hanging, it is written down where the next person will look for it. A
temperature invented from something else, on a transmitter, is exactly the
kind of number somebody would trust.
2026-08-26 21:14:58 +02:00
rouggy dad71e9e4f fix(tci): a filter button does what it says, and mute is put under a log
250 now means 0-250. It meant 575-825: the width was right and it was
centred on the CW note, on the reasoning that a CW filter should contain
the note. That reasoning may be right for a radio and it is still wrong
here, because it is not what the button says — and a button that does not
do what it says is worse than one that does something simple. The two
edges are editable underneath for anything else, which is what TCI takes
anyway.

And MUTE still lights the squelch on a real radio. Nothing in this code
can do that — the button sends mute and only mute, and the two are
separate state — so the radio's own announcements are logged as they
arrive. What it says after the command will settle whether this is our
reading or its doing; no more reasoning from here will.
2026-08-26 20:59:31 +02:00
rouggy fbe01bf19d fix(tci): wide level rows, honest filter labels, and mute read both ways
Three things reported from the radio, all of them mine.

THE LEVELS. Two half-width sliders side by side left each of them a couple
of centimetres long, which is not enough to set 15% with. One level per
row now, full width, with the value typed in or stepped with ± — and in
the radio's own units, so the volume is dB and the squelch a dBm
threshold, matching what ExpertSDR3's own window shows.

THE FILTERS. The button said 250 and the radio was set to 300-550: 250 Hz
wide, but sitting where no CW note is. The edges are computed from the
width now, and a narrow filter is CENTRED ON THE CW NOTE — a 250 Hz filter
from 100 to 350 would put the note outside its own passband. A button also
lights on the WIDTH the radio reports rather than on an exact pair of
edges, so moving one edge on the radio no longer darkens every button.

MUTE. Read from one shape only, while this radio reports the other
('mute:0,false'), so the button showed the opposite of the truth. Both are
accepted now.

None of this should have reached main before somebody had a radio in front
of it.
2026-08-26 20:54:28 +02:00
rouggy fc79be7c05 fix(tci): the drive commands need the TRX index, and the console holds its own clicks
Two faults, reported from a real SunSDR.

DRIVE AND TUNE DRIVE DID NOTHING, and neither did MUTE. Those commands
carry the transceiver index — 'drive:0,15;', not 'drive:15;' — and sent
without it the radio ignores them silently: no error, no answer, the power
unchanged. The rule was in the radio's own reports all along, which is
where it should have been read from: it announces 'drive:0,85' and
'mute:0,false' at connect, while 'mic_level:100' and 'volume:-12' come
with no index at all. Sending the shape the radio speaks in is the whole
rule, and it is now written down next to the two exceptions.

AGC LOOKED STUCK ON SLOW. The panel showed only what the radio reported
back, on the principle that the radio is the truth — but ExpertSDR3 does
not echo every setting it accepts, so a working button sat unlit. Changes
are shown at once and held for a moment now; whatever the radio announces
afterwards still wins, so a clamped or refused setting stays honest
without every working one looking broken.

Also from the same report, and fair: the consoles did not resemble each
other. The Icom panel's RIT control is now a shared component both use —
chip, signed offset, ± keys, wheel, and TYPING a value straight in, which
is the thing a row of ±10/±100 buttons cannot do. Ctrl+←/→ shifts the RIT
here as it does there. And LONG is gone from the AGC row: the protocol
takes it, but it is a hang time nobody reaches for between overs.
2026-08-26 19:15:48 +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
rouggy fdc2378191 feat(tci): name the transmit audio source when keying, per the protocol
The specification settles what a night of experiments could only guess at.
TRX takes an optional THIRD argument naming the signal source — tci, mic1,
mic2, micPC, ecoder2 — and TCI 2.0 says it plainly: 'The signal for
transmitting is always taken from the microphone selected in the
ExpertSDR3. If a third-party software connected via TCI wants to transmit
its audio signal, you must specify the third argument - TCI.'

Without it the radio sends no chrono at all, whatever the mode. That is
what the SSB attempts ran into, and what was misread here as 'digital
modes only' — the mode was never the rule, the missing argument was.

So OpsLog says it, following the 'To radio' device: 'tci' when the voice
keyer owns the transmission, nothing at all otherwise, which leaves the
operator's own microphone alone for every other PTT. Nobody has to find
that setting in ExpertSDR3 and set it again for every mode, which is how
it is remembered there.

Two more things from the same document. A chrono with no audio ready is
now answered with silence rather than left unanswered — the vendor calls
that preferable. And the receive stream declares float32 and two channels
instead of trusting the defaults: they are the documented defaults, but a
default is something another program sharing this radio can have changed,
and a stream in an unexpected format is heard as noise rather than as a
mistake.
2026-08-26 00:21:32 +02:00