fix(elecraft): the ATU is switch 19, not 20

SWT20 was written from memory of the reference rather than from Table 7,
and 20 is not an ATU switch at all — it would have pressed something else
on a real K3. An operator read the table out for us: switch 19 is the ATU
row, TAP for a tuning cycle and HOLD for tuner in line or bypassed.

Adds the HOLD as its own ATU button, because on the radio they are two
different things: tuning is a cycle you start, bypassing is a state you
leave it in.

Power takes 0-110 W on an Elecraft, per the reference — a K3 makes a
little over its rated output — while a Kenwood keeps 200.
This commit is contained in:
2026-08-24 19:20:30 +02:00
parent 27d0952d01
commit a27fa08e4e
7 changed files with 61 additions and 15 deletions
+33 -10
View File
@@ -100,6 +100,7 @@ type KenwoodPanelController interface {
ClearKenwoodRIT() error
SetKenwoodTX(bool) error
TuneKenwoodATU() error
ToggleKenwoodATU() error
}
// kenwoodPanelSlowBeat is how many polls pass between full re-reads of the
@@ -365,12 +366,21 @@ func kenwoodBargraphPercent(v int) int {
}
// SetKenwoodPower sets the transmit power, in watts.
//
// A K3 takes PC000-110: 110 rather than 100 because the radio will make a
// little over its rated output and the reference says so. A Kenwood of the
// TS-890 sort goes to 200, so the ceiling follows the radio rather than being
// one number that is wrong for one of them.
func (k *Kenwood) SetKenwoodPower(w int) error {
max := 200
if k.elecraft {
max = 110
}
if w < 0 {
w = 0
}
if w > 200 {
w = 200
if w > max {
w = max
}
return k.setPanel(fmt.Sprintf("PC%03d;", w))
}
@@ -481,21 +491,34 @@ func (k *Kenwood) SetKenwoodTX(on bool) error {
return k.SetPTT(on)
}
// kenwoodATUTune is the command that starts an ATU tuning cycle on a K3.
//
// The K3 has no dedicated "tune" command: the reference exposes the front panel
// instead, and SWT20 is the tap of the ATU TUNE button. That mapping has NOT
// been confirmed on a radio here, which is why the command actually sent is
// logged — if it presses something else on a real K3, the log names what was
// sent instead of leaving an operator to guess which button OpsLog reached for.
const kenwoodATUTune = "SWT20;"
// instead, through SWT (tap) and SWH (hold) with a switch number from Table 7.
// Switch 19 is the ATU row — TAP starts a tuning cycle, HOLD puts the tuner in
// or out of line.
//
// It was written as SWT20 first, from memory of the reference rather than from
// the table, and 20 is not an ATU switch at all. Corrected against Table 7 of
// the K3 Programmer's Reference, which an operator read out for us. Every send
// is still logged: a switch-emulation command presses a real button on a real
// front panel, so what OpsLog sent must be recoverable afterwards.
const (
kenwoodATUTune = "SWT19;" // tap ATU TUNE — start a tuning cycle
kenwoodATUToggle = "SWH19;" // hold ATU — tuner in line / bypassed
)
// TuneKenwoodATU starts an ATU tuning cycle.
func (k *Kenwood) TuneKenwoodATU() error {
debugLog.Printf("kenwood: ATU tune — sending %q (K3 front-panel tap; report it if another button responded)", kenwoodATUTune)
debugLog.Printf("kenwood: ATU tune — sending %q (K3 Table 7: tap of the ATU TUNE switch)", kenwoodATUTune)
return k.setPanel(kenwoodATUTune)
}
// ToggleKenwoodATU puts the tuner in line or bypasses it — the HOLD of the same
// switch, which is how it is done on the radio itself.
func (k *Kenwood) ToggleKenwoodATU() error {
debugLog.Printf("kenwood: ATU in/out — sending %q (K3 Table 7: hold of the ATU switch)", kenwoodATUToggle)
return k.setPanel(kenwoodATUToggle)
}
// setPanel writes one command and schedules a settings re-read, so the panel
// shows what the radio did rather than what it was asked to do.
func (k *Kenwood) setPanel(cmd string) error {