chore: release v0.25.8

This commit is contained in:
2026-08-17 13:18:32 +02:00
parent 0bab7f05b9
commit 7be6f64596
10 changed files with 661 additions and 83 deletions
+27 -1
View File
@@ -1,6 +1,7 @@
package cat
import (
"errors"
"fmt"
"strings"
"sync"
@@ -535,11 +536,36 @@ func (b *IcomSerial) SetMode(mode string) error {
return nil
}
// errIcomAckLost is "the reply never came" — as opposed to a reply that said no
// (NG) or a dead port. Only this one is worth repeating: the command may well
// have been carried out and only its acknowledgement lost. Sentinel rather than
// a formatted string so callers can tell the two apart.
var errIcomAckLost = errors.New("icom: timeout waiting for response")
// SetPTT keys or unkeys the transmitter (CI-V 0x1C 0x00), retrying ONCE when the
// acknowledgement is lost.
//
// A missing FB is not a missing command — the rig acts on the frame as soon as it
// decodes it, and what expires is our wait for the answer on a bus shared with
// the rig's own transceive updates. JTDX in "Split Operating: Fake It" moves the
// dial immediately before every key-down, so the PTT ack queues behind that
// traffic, and one lost ack was fatal: rigctld answered RPRT -9, JTDX read that
// as losing rig control and tore the connection down mid-over, reopening it a
// moment later (an operator's log shows exactly that, twice, a new rigctld client
// within 300 ms of each failure). The same session over TCI never failed, because
// TCI carries no CI-V and needs no Fake It.
//
// Re-sending is safe: asking for a state the rig is already in changes nothing.
func (b *IcomSerial) SetPTT(on bool) error {
state := byte(0)
if on {
state = 1
}
err := b.exec(civ.CmdPTT, civ.SubPTT, state)
if err == nil || !errors.Is(err, errIcomAckLost) {
return err
}
applog.Printf("icom: PTT %v — no acknowledgement in %s, sending it once more", on, icomCmdTimeout)
return b.exec(civ.CmdPTT, civ.SubPTT, state)
}
@@ -619,7 +645,7 @@ func (b *IcomSerial) recv(timeout time.Duration, match func(civ.Decoded) bool) (
case <-cancel:
return civ.Decoded{}, fmt.Errorf("icom: interrupted")
case <-deadline:
return civ.Decoded{}, fmt.Errorf("icom: timeout waiting for response")
return civ.Decoded{}, errIcomAckLost
}
}
}