The answer to 'I have two rigs' was 'make two profiles', which moves the
whole station — logbook included — to change which radio is connected.
Radios are a list now, like amplifiers, and the CAT chip switches between
them while everything else stays put.
The active radio still lives in the settings keys the rest of OpsLog
reads, so the consoles, the CAT sharing and the band-follow needed no
change at all.
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'.
It only became a menu from two radios, on the reasoning that a single
radio has nothing to switch to. That hides the feature from precisely the
operator who has not made a second radio yet — the click lands on a
settings dialog they did not ask for, which is what happened on the first
try. With one radio the menu now shows that radio and 'Add a radio…'.
Right-click still goes straight to the CAT settings.
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.
THE README WAS GONE. A commit of mine last night replaced OpsLog's readme
with the text of the TCI protocol specification — 322 lines of ours for 77
of Expert Electronics' — and nobody noticed because nothing builds from
it. Restored from the commit before, then updated.
What it was missing, all of it shipped since: the Kenwood/Elecraft and
Yaesu CAT backends and their consoles, the SunSDR console, TCI audio in
both directions, CAT sharing over Hamlib rigctl or TCI, the Elecraft
KPA500/KPA1500 and the Tuner Genius XL, Cloudlog and HAMLOG uploads, the
RDA district work, the DXCC Challenge, the editable satellite list, four
main-view panes instead of two, and Esri basemaps in place of Carto.
Also a section on importing a log, which had none — including the rule
that matters most to somebody moving years of contacts across: a deleted
DXCC entity keeps the number the file gives it, because cty.dat only knows
where a callsign is today.
It has not shipped yet, so an operator reading about the SunSDR console
should meet what it does — not a list of repairs to a thing they have
never run.
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.
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.
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.
Two things from one screenshot, and the second is arithmetic rather than a
bug.
Editing an award refreshed the grid and not the statistics matrix, so
changing which confirmations count left the table showing the old rule's
numbers with nothing to mark them stale. That is how a setting comes to
look as though it does nothing.
And OpsLog's 3229 against LoTW's 3004: the Challenge counts confirmed
band-slots on ten bands, and 60 m is not one of them. 3229 less the 224 in
the 60 m column is 3005. So the number was right and the QUESTION was
different — the panel now answers the Challenge one too, under the table
where the comparison is made.
The two totals also say what they are on hover. 'Total' and 'Général' side
by side tell nobody which counts entities and which counts band-slots, and
3309 read as entities is an impossible number — which is exactly how it
was read.
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.
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.
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.
A spot at 10131.5 showed as DATA in the cluster list and raised an alert
announcing SSB. Two tables answered the same question: the frontend knew
10.130-10.150 is the 30 m data segment, while the alert engine knew a CW
range that stopped at 10.130 and ended with a bare 'return SSB' for
everything else.
That fall-through was the fault. A band plan that answers 'SSB' for
anything it does not recognise is not a band plan, it is a default wearing
one — and it fed the alert text, the FlexRadio spot colours and the mode
the radio is told to switch to.
The Go table is now transcribed from the frontend's, segment for segment,
and returns nothing where it knows nothing. The order matters and is now
load-bearing — the FT8 and FT4 watering holes are listed BEFORE the wide
data blocks they sit inside — so there is a test that fails if anybody
sorts the table by frequency and quietly turns every FT8 hole into DATA.
WPM counts as CW while we are here: nothing else is reported in words per
minute, and RBN puts it on every line.
The log settles it, and it was not mute. Mute works: '→ mute:0,true;' and
the radio answers 'mute:0,true'. What happens is that the squelch REFUSES
to go off — we send 'sql_enable:0,false;', the radio echoes false, and
eighty milliseconds later announces 'sql_enable:0,true' again. Six times
in the log, always the same shape. The SQL lamp coming back on right after
a MUTE click is that revert landing, not the mute doing it.
So the optimistic hold is fixed rather than the phantom. It was a fixed
1.2 s, which is wrong in both directions: too short for a setting the
radio never echoes at all (AGC), and too long for one it refuses, where it
showed the operator a lie for a second. The requested value now stands
while the radio says nothing about that setting, and the instant the radio
reports any change for it, its word replaces ours. A refusal is therefore
visible immediately, and an unechoed setting still sticks.
Also from the same session: filters per MODE — the narrow end in CW, the
voice widths in SSB, a middle set for the digital modes — because 250 Hz
in SSB and 2.8 kHz in CW are buttons nobody presses. And APF is shown only
in CW: it rings a single tone out of the noise, which is a CW tool and
nothing else.
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.
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.
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.
TCI pushes everything about the radio unasked, so the console is mostly a
place to put what was already arriving and being logged once as unhandled.
Drive, tune drive, mic gain, TUNE, volume, mute, squelch, NB/NR/ANF/APF,
AGC, passband, RIT and XIT, VFO lock, and an S-meter in real dBm.
Merged with two corrections to the audio page the console's own work
turned up: the radio was being offered as a recording microphone, and the
RX monitor was enabled for a source it cannot play.
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.
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.
Reported on an Ultrabeam over USB: the port could not be chosen from the
dropdown, and the panel would not scroll.
That field was a Combobox — a list you can also type into — chosen so the
port of an adapter currently UNPLUGGED could still be configured. It is
now the same Select every other serial device in OpsLog uses, the one that
demonstrably works. The case it was built for is kept differently: a
configured port that is no longer detected is added to the list, so it
stays selected and visible instead of vanishing when the adapter is out.
Also hardened the Test button's guard, which called .trim() straight on
the port and the host. A settings object without them — an older build, a
save that landed short — throws there DURING RENDER, and a panel that
throws while rendering is exactly a panel with a dead dropdown that will
not scroll. Whether or not that is what this operator hit, it is a way to
produce both symptoms at once and it costs nothing to remove.
Reported with a screenshot: the map came up with API KEY REQUIRED written
twice across the middle of it, on the Light and Voyager views only. Those
are exactly the two Carto basemaps.
Nothing here could have caught it. The tiles still answer 200 with a real
image — the watermark is IN the image — so the map looks like it is
working and simply says otherwise in large grey letters.
Both views move to Esri, which is already behind Street and Satellite. That
is the point: a second key-free provider is a second chance to be cut off,
and this is now the third time a tile source has changed its terms under
us. Light is Esri's light canvas with its own label layer, and Voyager
becomes the topographic map, labelled Topo — the stored key stays
'voyager' so nobody who chose it is quietly moved somewhere else.
The canvas runs out of tiles at zoom 16 where the others go to 19, so
maxNativeZoom is set per basemap: Leaflet upscales the last real tile
instead of asking for a level that does not exist, which is the difference
between a slightly soft map and a blank grey square.
Written into the entry itself rather than added as a line of its own:
somebody reading about the feature should learn it there, not two entries
further down.
A fourth amplifier brand alongside the SPE, the Acom and the PowerGenius,
sharing the same multi-amplifier support: the linking, the fan-out and the
polling all work on it unchanged.
The protocol is the Elecraft one — a caret, letters, a semicolon — decoded
from the KPA1500 Programming Reference, with the document's own examples
kept as the test. ^WS gives forward power and SWR in one exchange, ^VI
gives volts in tenths and amps whole, and ^FL is HEX: read as decimal, the
'antenna not connected' fault matches nothing at all.
Three things this amplifier does that the others do not, each handled
where it shows: a KPA500 has no network side, the amplifier answers while
its main supplies are off, and going to OPERATE clears the current fault.
Nothing here has met real hardware yet.
Telling somebody's amplifier where to be is a write to their station, and
it now has a switch — the SAME switch an Acom uses. 'Keep the amplifier on
the radio's band' is one idea to an operator; that it needs a second
serial port and a rig emulator on one amplifier and a single ^BN on
another is our business, not a reason for two checkboxes.
On by default for a KPA, because an amplifier left on the wrong band is
the worse outcome of the two, and the operator who has wired it straight
to the radio turns it off once. The hint says exactly that case, since it
is the only one where a second voice telling the amplifier where to be is
unwelcome.
Two things the wiring got wrong, both visible on screen.
A KPA fell through to the PowerGenius branch of the amplifier card and was
drawn as one — titled 'POWERGENIUSXL · ELECRAFT KPA1500', with a
PowerGenius's meters and none of its own. It has a card now: OPERATE, ON
and OFF, band, power, SWR, temperature, supply, and the fault across the
end. The OPERATE button carries a tooltip saying it also clears the fault,
because that is the button an operator already has under the cursor when
they need it.
And the band-follow option was offered on it. That option exists because
an Acom POLLS a transceiver and has no command to be given a band, so
following it needs a second serial port and a rig emulator answering those
polls. The KPA has ^BN — OpsLog simply tells it, on the link it is already
using, and only when the band CHANGES. The option is gone from the KPA and
the telling is automatic.
Also: Acom rather than ACOM everywhere it is read, which is how the
company writes it. Identifiers, package names and log lines are left
alone — renaming those is churn with no reader.
Wired into the multi-amplifier support that already carries the SPE, the
ACOM and the PowerGenius: a fourth brand rather than a fourth mechanism.
The linking, the fan-out and the poll all work on it unchanged.
Three things the KPA does differently, and each is handled where it shows
rather than explained in a hint:
- A KPA500 has no network side at all. Choosing it puts the entry on
serial, and a configuration still asking for TCP is refused with a
line saying so instead of retrying an address that cannot answer.
- The amplifier answers ^ON while its main supplies are OFF — a sleeping
microcontroller stays awake for exactly that — so power-on stays
available over the network, unlike the SPE and ACOM which need their
serial control lines.
- Going to OPERATE clears the current fault, everything except
temperature. The widget says so under the fault, because the button
that fixes it is the one already on screen.
Defaults that leave a working configuration when the model is changed:
38400 baud, TCP port 1500, and a full-scale power mark of 500 or 1500 W
by model — a KPA500 read against a 1500 W scale looks idle at full
output.
One package for both amplifiers: they share the Elecraft command set — a
caret, letters, a semicolon, case-insensitive in and upper case out — the
same family as the K3/K4 panel. What differs is the transport and which
commands exist, not the grammar.
Everything here comes from the KPA1500 Programming Reference, and the
document's own examples ARE the test:
^WS1204 014; 1204 W and SWR 1.4:1 — power and SWR in one exchange
^VI513 061; 51.3 V and 61 A — volts in tenths, amps whole
^FL91; HEX, and 0x91 is 'antenna not connected?'
That last one is why the parsing is pinned rather than eyeballed: read as
decimal, 90 and 91 become 144 and 145 and match nothing, so an amplifier
shut down by high reflected power would report a fault OpsLog could not
name. SWR in tenths is confirmed by the reference too — 'expressed in
tenths, 123 is 12.3:1' — where it had only been inferred from Hamlib.
The client is question-and-answer under one lock, never two questions in
flight: the reference states there is no flow control and that commands
are paced by waiting for the reply. Fast cycle four times a second for
power, SWR and the fault; the rest once a second.
Faults are named in the operator's terms — 'the ATU found no match', not
'fault 92' — and an unknown code from a newer firmware still says
something rather than nothing.
Not wired to the app yet, and two commands are deliberately absent: ^TX
makes the amplifier transmit from software, and ^ON0 cuts the main
supplies with Wake-on-LAN as the way back. Neither belongs on a poll loop
or behind a button that can be pressed by accident.
The columns took the award's declared band list when it had one, so every
band the award PERMITS got a column whether or not anything had ever been
worked on it. DDFM permits 6m and 70cm; a station that has never worked a
French department on either was shown two empty columns, which reads as a
gap in the log rather than as a band nobody tried.
They now follow the bands this operator actually has contacts on for the
award. The declared list is the fallback for an award with nothing worked
yet — there, saying what the award is played on is the only honest answer
available — and the classic HF set is the fallback for an award that
declares nothing either.
Together with taking the order from the full band list, this is what puts
23cm on DDFM: the column appears because there are contacts on it.
The columns were the intersection of the award's bands with a fixed list
that ended at 70cm, so a band the award genuinely covers could not appear
whatever it said. DDFM is worked well into the microwaves and had no 23cm
column, and nothing anywhere would have produced one.
The intersection is now taken against every band in frequency order, the
same list internal/award already sorts its own band counts by. The classic
HF-plus-2m-and-70cm set stays as the fallback for an award with no band
list and no contacts yet — the full list would be twenty-nine columns of
mostly nothing.
Reported from a station running an IC-7850 over USB: with split engaged,
turning VFO B moved VFO A slightly. OpsLog writes nothing to that radio on
its own — but it shares it, and what it was sharing was wrong.
Hamlib's 'f' means the VFO IN USE. RigState follows ADIF, where FreqHz is
where we TRANSMIT, and the two are the same number until split is
engaged — so the adapter handed FreqHz over unchanged and every client
asking for the dial was given VFO B. A client that reads the dial and
writes it back, which is what WSJT-X and its like do, then wrote VFO B's
frequency into VFO A. Hence a shift the size of the split offset, and
hence 'it did not do this before': it only happens with split on.
get_split_freq ('i') already answers the transmit frequency and is
untouched. The one-line rule is lifted into shareRXFreq with a test that
includes this case, the simplex case, and a backend that reports split
without ever filling in the receive frequency — which would otherwise
answer a client with 0 Hz.
Two faults, and the first was a design mistake of mine.
The choice was written only as the award override, to keep the imported
CNTY as a record of what HAMLOG said. But CNTY is what the comparison
READS, so the contact went on disagreeing for ever: settle a hundred
rows, run the comparison again, get the same hundred back. The reasoning
was about preserving evidence; the effect was a button with no visible
consequence anywhere, which is indistinguishable from one that does
nothing. This is the operator's own log, and correcting a field in it is
the point of the exercise. The district now lands in CNTY as well as in
the award reference.
And the settled rows stayed on screen, which made the same button look
broken a second time. They are dropped as they are applied — locally,
rather than by re-running the comparison, which reads the whole log and
would be seconds of silence on a remote database to be told what is
already known. The agree/disagree counters follow.
Reported by VK4DX with the case that names itself: YB confirmed on 20m
digital, painted blue, because one unconfirmed YB station had also been
worked on that slot.
The ladder ran call_c > call_w > dxcc_c > dxcc_w, so a callsign worked and
not confirmed beat an entity CONFIRMED on the same band and mode. The grid
answers 'what do I still need here', and a confirmed entity needs nothing
whoever was worked afterwards — so the order is now call_c > dxcc_c >
call_w > dxcc_w.
Two things made it easy to get wrong, and both are fixed rather than
merely corrected. The code was a run of assignments where the later test
silently overwrote the earlier, so 'call worked' erased 'entity
confirmed'; it now takes the maximum. And the rule lived inside a scan
loop where nothing could reach it, so it is lifted into bandStatusCode
with a table test — including this exact case, and one that fails if
anybody reorders the constants.
The comparison could show two hundred contacts where the log and the
database name different districts, and leave the operator with nothing to
do about any of them but open each QSO and edit it by hand.
Now each row carries the two values as buttons: click the district you
keep, then apply. There are shortcuts for keeping one source everywhere,
and applying is a separate act — clicking through two hundred rows with
each one written as it is clicked would make a slip permanent before the
reading was finished.
The choice is written as the contact's AWARD REFERENCE, not over the
imported CNTY, and that distinction is the point. CNTY is what the
downloaded log said; overwriting it would destroy the evidence the
comparison runs on, and the next comparison would agree with itself and
prove nothing. The override is the operator's decision, it is what the
award engine counts, and it beats both sources. So the log keeps saying
what it said, the database keeps saying what it says, and the contact
counts for the district that was chosen.
The buttons show the districts themselves rather than a tick box: someone
arbitrating between RO-19 and KO-05 should be picking a value they can
read, not remembering which source a checked box stood for.
v0.26.15 had already shipped, so its entry about setting the source by
hand in ExpertSDR3 goes back exactly as released — it describes the
version operators are running. What replaces it belongs to the version
that carries the change.
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.
The TCI section of the audio settings was an investigation: open the
stream, read what arrives, record ten seconds to listen to, key a tone.
It answered every question it was built for — the frame layout, the
sample width, that the radio asks rather than follows a clock, that the
transmit audio source decides — and confirmed 80 W on real hardware.
None of that belongs in front of an operator now. The radio is simply one
of the devices in the two dropdowns, and choosing it IS the configuration:
one control, in the place where the question is already being asked.
Keeping the tick box beside it would have been two switches for one
decision, with the second one where nobody looks.
Gone with it: the stream/record/probe bindings, the audio.tci_rx setting,
and twenty-four translation keys. The plumbing they proved out stays and
now carries the voice keyer.
The RDA and Elecraft SWR entries move with it: both were written after
v0.26.14 was tagged, so they ship in the version that carries them rather
than in the one already on operators' machines.
A SunSDR carries its audio on the same WebSocket as its commands, so
OpsLog can take it directly: no virtual cable, no second sound card, no
Windows mixer between the recording and the air. The radio appears as a
device in both audio lists and can be chosen for either direction.
Everything here was settled on real hardware over one evening, and none of
it was guessable from the documentation:
- The receive stream answers format=3 for four-byte floats, so the
sample width is derived from the frame rather than trusted from the
field.
- The radio asks for transmit audio only when the transmission is the
CLIENT'S, and only when its transmit audio source is TCI rather than
the microphone.
- The chrono is a REQUEST, not a clock: no payload, carrying the size it
wants, 47 times a second. Audio goes out in answer to it and never on
a timer of our own — a timer was the first attempt and all 234 frames
of it were ignored.
Confirmed: a clean test recording, and 80 W out of a 1 kHz tone.