feat: CW through the Yaesu keyer — fifth CW engine

The radio has a keyer and a command to feed it (KY), so an FTDX10 needs no
WinKeyer and no second cable, exactly as the Icom CI-V and Flex CWX engines
already do. The rig keys with its own timing, which is why the spacing is right
where a PC keying a line through USB latency drifts.

Text is filtered to what the keyer can actually send: an unsupported byte does
not produce an error on a Yaesu, it can abort the whole buffer, so the rest of a
macro would vanish silently. It is then fed in 24-character pieces, waiting for
room between them — the rig DROPS what does not fit, again with no error, so a
contest CQ would lose its tail.

STOP is the honest gap. Yaesu documents no buffer-clear, so it drops the
transmitter (TX0) instead: nothing queued reaches the air, which is what Escape
means to an operator. It deliberately does NOT send "KY0;" — a plausible-looking
clear that the rig would read as the CHARACTER zero and transmit.

A test caught a real one on the way: tabs and newlines were dropped as
"unsupported", gluing the words either side together, so a macro written on two
lines went out as CQCQ. Whitespace now becomes a word gap before filtering.

Settings warn when the Yaesu keyer is selected without the Yaesu CAT backend —
otherwise it simply never keys, with nothing on screen saying why.

Send path follows the CAT reference and how Hamlib drives these rigs; NOT yet
verified on the air.
This commit is contained in:
2026-07-29 12:42:40 +02:00
parent 520d17810c
commit 08f3e54afb
11 changed files with 252 additions and 11 deletions
+21
View File
@@ -15005,3 +15005,24 @@ func (a *App) SetYaesuBreakIn(on bool) error {
func (a *App) YaesuZeroIn() error {
return a.yaesuDo(func(y cat.YaesuController) error { return y.YaesuZeroIn() })
}
// YaesuSendCW keys a CW message through the Yaesu's own keyer (CAT "KY"), so an
// FTDX10 needs no WinKeyer and no second cable.
func (a *App) YaesuSendCW(text string) error {
if a.cat == nil {
return fmt.Errorf("cat not initialized")
}
err := a.cat.YaesuDo(func(y cat.YaesuController) error { return y.SendCW(text) })
if err != nil {
applog.Printf("yaesu cw: YaesuSendCW(%q) failed: %v", text, err)
}
return err
}
// YaesuStopCW aborts the message being sent.
func (a *App) YaesuStopCW() error {
if a.cat == nil {
return fmt.Errorf("cat not initialized")
}
return a.cat.YaesuDo(func(y cat.YaesuController) error { return y.StopCW() })
}