fix: serial CAT keyed the radio on connect; Xiegu can key on RTS/DTR

Reported on a Xiegu G90. With the blue programming cable — three wires, no
modem lines — CAT works perfectly. Behind a DE-19, which carries audio, CAT and
PTT, the radio went into transmit the moment OpsLog connected and stayed there.

Windows raises DTR and RTS when a serial port is opened, and the DE-19 reads
them as PTT; Xiegu's own documentation asks for both low. The CI-V backend has
dropped them since it was written, for the same reason on Icom rigs with USB
SEND mapped to a line. Xiegu, Yaesu and Kenwood did not. They do now.

The second half of the report: WSJT-X through OpsLog's rigctld decoded fine and
never transmitted. Nothing was broken in that chain — set_ptt reaches the
backend, which sends the CI-V PTT command, which a G90 ignores. That is why
Xiegu keys on a hardware line instead. The backend can now do that: Settings →
CAT → Xiegu → how the rig is keyed (CI-V / RTS / DTR).

Untested here — no G90 in reach. The operator who reported it offered to try.
This commit is contained in:
2026-07-31 22:16:03 +02:00
parent 258fa717f1
commit d4bfd30636
8 changed files with 112 additions and 6 deletions
+11 -2
View File
@@ -114,6 +114,7 @@ const (
keyCATXieguPort = "cat.xiegu.port" // Xiegu CI-V serial port (G90/X6100…)
keyCATXieguBaud = "cat.xiegu.baud" // Xiegu CI-V baud (G90 default 19200)
keyCATXieguAddr = "cat.xiegu.addr" // Xiegu CI-V address (factory 0x70)
keyCATXieguPTTLine = "cat.xiegu.ptt_line" // "" | rts | dtr — G90 does not key over CI-V
keyCATYaesuPort = "cat.yaesu.port" // Yaesu CAT serial port (e.g. COM4)
keyCATYaesuBaud = "cat.yaesu.baud" // Yaesu CAT baud (FTDX10/101 default 38400)
keyCATKenwoodHost = "cat.kenwood.host" // Kenwood CAT over a network serial bridge (ser2net), "host:port"
@@ -370,6 +371,7 @@ type CATSettings struct {
XieguPort string `json:"xiegu_port"` // Xiegu CI-V serial port (G90/X6100…)
XieguBaud int `json:"xiegu_baud"` // Xiegu CI-V baud (G90 default 19200)
XieguAddr int `json:"xiegu_addr"` // Xiegu CI-V address (factory 0x70)
XieguPTTLine string `json:"xiegu_ptt_line"` // "", "rts" or "dtr" — how the rig is keyed
YaesuPort string `json:"yaesu_port"` // Yaesu CAT serial port (e.g. COM4)
YaesuBaud int `json:"yaesu_baud"` // Yaesu CAT baud (FTDX10/101 default 38400)
KenwoodHost string `json:"kenwood_host"` // "host:port" of a serial-over-network bridge (ser2net, Ethernet-serial
@@ -6748,7 +6750,7 @@ func (a *App) GetCATSettings() (CATSettings, error) {
if a.settings == nil {
return CATSettings{Backend: "omnirig", OmniRigNum: 1, PollMs: 250}, fmt.Errorf("db not initialized")
}
m, err := a.settings.GetMany(a.ctx, keyCATEnabled, keyCATBackend, keyCATOmniRigNum, keyCATOmniRigVFO, keyCATFlexHost, keyCATFlexPort, keyCATFlexSpots, keyCATFlexDecodeSpots, keyCATFlexDecodeSecs, keyCATXieguPort, keyCATXieguBaud, keyCATXieguAddr, keyCATYaesuPort, keyCATYaesuBaud, keyCATKenwoodPort, keyCATKenwoodBaud, keyCATKenwoodHost, keyCATIcomPort, keyCATIcomBaud, keyCATIcomAddr, keyCATIcomNetHost, keyCATIcomNetUser, keyCATIcomNetPass, keyCATIcomNetAudio, keyCATTCIHost, keyCATTCIPort, keyCATTCISpots, keyCATPollMs, keyCATDelayMs, keyCATDigitalDefault, keyCATShareEnabled, keyCATSharePort)
m, err := a.settings.GetMany(a.ctx, keyCATEnabled, keyCATBackend, keyCATOmniRigNum, keyCATOmniRigVFO, keyCATFlexHost, keyCATFlexPort, keyCATFlexSpots, keyCATFlexDecodeSpots, keyCATFlexDecodeSecs, keyCATXieguPort, keyCATXieguBaud, keyCATXieguAddr, keyCATXieguPTTLine, keyCATYaesuPort, keyCATYaesuBaud, keyCATKenwoodPort, keyCATKenwoodBaud, keyCATKenwoodHost, keyCATIcomPort, keyCATIcomBaud, keyCATIcomAddr, keyCATIcomNetHost, keyCATIcomNetUser, keyCATIcomNetPass, keyCATIcomNetAudio, keyCATTCIHost, keyCATTCIPort, keyCATTCISpots, keyCATPollMs, keyCATDelayMs, keyCATDigitalDefault, keyCATShareEnabled, keyCATSharePort)
if err != nil {
return CATSettings{}, err
}
@@ -6800,6 +6802,7 @@ func (a *App) GetCATSettings() (CATSettings, error) {
if n, _ := strconv.Atoi(m[keyCATXieguBaud]); n > 0 {
out.XieguBaud = n
}
out.XieguPTTLine = m[keyCATXieguPTTLine]
if n, _ := strconv.Atoi(m[keyCATXieguAddr]); n > 0 && n <= 0xFF {
out.XieguAddr = n
}
@@ -6920,6 +6923,7 @@ func (a *App) SaveCATSettings(s CATSettings) error {
keyCATXieguPort: strings.TrimSpace(s.XieguPort),
keyCATXieguBaud: strconv.Itoa(s.XieguBaud),
keyCATXieguAddr: strconv.Itoa(s.XieguAddr),
keyCATXieguPTTLine: strings.ToLower(strings.TrimSpace(s.XieguPTTLine)),
keyCATYaesuPort: strings.TrimSpace(s.YaesuPort),
keyCATYaesuBaud: strconv.Itoa(s.YaesuBaud),
keyCATKenwoodPort: strings.TrimSpace(s.KenwoodPort),
@@ -12743,7 +12747,12 @@ func (a *App) reloadCAT() {
// Xiegu G90/X6100/X6200/X5105 — CI-V, but a REDUCED command set: no scope,
// no DSP block, no data mode. Its own backend rather than the Icom one at
// another address, so we never poll a G90 for answers it cannot give.
a.cat.Start(cat.NewXiegu(s.XieguPort, s.XieguBaud, s.XieguAddr, s.DigitalDefault))
xg := cat.NewXiegu(s.XieguPort, s.XieguBaud, s.XieguAddr, s.DigitalDefault)
// A G90 ignores the CI-V PTT command; its interfaces key on a hardware
// line instead. Selected here so rigctld — and therefore WSJT-X — can
// actually put the rig on the air.
xg.SetPTTLine(s.XieguPTTLine)
a.cat.Start(xg)
case "yaesu":
// Native Yaesu CAT over the rig's USB/serial port — no OmniRig. Every
// Yaesu fault reported so far came from OmniRig's interpretation layer