fix(kenwood): keep the CAT link up while transmitting (K3 shared-CAT FT8)
A K3 (and other Kenwood-dialect rigs) answers "?;" to an IF; status poll while it is transmitting. ask() latched that as "IF unsupported" and ReadState returned an error, so the Manager dropped the whole CAT link mid-transmit — the log shows connected=false / "IF rejected" the instant JTDX sent TX;. That tore down the shared rigctld CAT the digital-mode software keys through. Two fixes: (1) track PTT (SetPTT) and skip the wire poll while it's held — ReadState hands back the cached last state, capped at 30 s so a missed SetPTT(false) can't freeze it; (2) never latch IF/ID as unsupported — those are universal on Kenwood/Elecraft, so a "?;" to them is a transient busy (mid-TX), not a missing command.
This commit is contained in:
+4
-2
@@ -12,7 +12,8 @@
|
|||||||
"DCU-1 rotor support: OpsLog can now steer controllers that speak the Hy-Gain DCU-1 protocol (RotorCard DXA for Yaesu DXA rotors, Idiom Press Rotor-EZ, Green Heron) over their COM port or a serial-over-IP bridge.",
|
"DCU-1 rotor support: OpsLog can now steer controllers that speak the Hy-Gain DCU-1 protocol (RotorCard DXA for Yaesu DXA rotors, Idiom Press Rotor-EZ, Green Heron) over their COM port or a serial-over-IP bridge.",
|
||||||
"Motorized antenna: Settings → Antenna now has a Covered bands selector (40 m–6 m) instead of a min/max range, for both the Ultrabeam and the SteppIR. Tick only the bands your antenna does — untick one you can't (e.g. 30 m without its extension) while keeping the rest — and OpsLog leaves the antenna put on every other band. This overrides an unreliable controller, so 80 m no longer clamps the elements down to 30 m. Existing range settings migrate automatically.",
|
"Motorized antenna: Settings → Antenna now has a Covered bands selector (40 m–6 m) instead of a min/max range, for both the Ultrabeam and the SteppIR. Tick only the bands your antenna does — untick one you can't (e.g. 30 m without its extension) while keeping the rest — and OpsLog leaves the antenna put on every other band. This overrides an unreliable controller, so 80 m no longer clamps the elements down to 30 m. Existing range settings migrate automatically.",
|
||||||
"Motorized antenna: on a band you did not tick in Covered bands, OpsLog no longer inhibits FlexRadio transmit for \"antenna moving\". Un-ticking a band now means hands off completely — no tuning and no TX block — so working it on another antenna is never gagged.",
|
"Motorized antenna: on a band you did not tick in Covered bands, OpsLog no longer inhibits FlexRadio transmit for \"antenna moving\". Un-ticking a band now means hands off completely — no tuning and no TX block — so working it on another antenna is never gagged.",
|
||||||
"Awards: a new Settings → Awards picker (under User configuration) lets you choose which awards to follow — a two-column list, all awards on the left, the ones you track on the right. The Awards tab then shows only those you follow (per profile; leave the right side empty to show them all)."
|
"Awards: a new Settings → Awards picker (under User configuration) lets you choose which awards to follow — a two-column list, all awards on the left, the ones you track on the right. The Awards tab then shows only those you follow (per profile; leave the right side empty to show them all).",
|
||||||
|
"Kenwood/Elecraft CAT: the link no longer drops while the rig is transmitting. A K3 (and other Kenwood-dialect rigs) answers \"?;\" to a status poll during transmit; OpsLog used to read that as \"rig doesn't support IF\" and disconnect — which tore down the shared CAT that WSJT-X / JTDX key through, so the radio wouldn't transmit properly. OpsLog now pauses status polling while PTT is held and keeps the link up."
|
||||||
],
|
],
|
||||||
"fr": [
|
"fr": [
|
||||||
"Édition de QSO : ajout du bouton QRZ ↗ à côté de l'indicatif, comme dans le formulaire de saisie — un clic ouvre le profil qrz.com de la station.",
|
"Édition de QSO : ajout du bouton QRZ ↗ à côté de l'indicatif, comme dans le formulaire de saisie — un clic ouvre le profil qrz.com de la station.",
|
||||||
@@ -24,7 +25,8 @@
|
|||||||
"Prise en charge des rotors DCU-1 : OpsLog pilote désormais les contrôleurs parlant le protocole Hy-Gain DCU-1 (RotorCard DXA pour rotors Yaesu DXA, Idiom Press Rotor-EZ, Green Heron) via leur port COM ou un pont série-sur-IP.",
|
"Prise en charge des rotors DCU-1 : OpsLog pilote désormais les contrôleurs parlant le protocole Hy-Gain DCU-1 (RotorCard DXA pour rotors Yaesu DXA, Idiom Press Rotor-EZ, Green Heron) via leur port COM ou un pont série-sur-IP.",
|
||||||
"Antenne motorisée : Réglages → Antenne propose désormais un sélecteur Bandes couvertes (40 m–6 m) au lieu d'une plage min/max, pour l'Ultrabeam comme pour la SteppIR. Coche seulement les bandes que fait ton antenne — décoche celle que tu ne fais pas (p. ex. le 30 m sans son extension) en gardant les autres — et OpsLog laisse l'antenne en place sur toutes les autres. Ceci prime sur un contrôleur peu fiable : le 80 m ne replie plus les éléments sur le 30 m. Les anciens réglages de plage sont migrés automatiquement.",
|
"Antenne motorisée : Réglages → Antenne propose désormais un sélecteur Bandes couvertes (40 m–6 m) au lieu d'une plage min/max, pour l'Ultrabeam comme pour la SteppIR. Coche seulement les bandes que fait ton antenne — décoche celle que tu ne fais pas (p. ex. le 30 m sans son extension) en gardant les autres — et OpsLog laisse l'antenne en place sur toutes les autres. Ceci prime sur un contrôleur peu fiable : le 80 m ne replie plus les éléments sur le 30 m. Les anciens réglages de plage sont migrés automatiquement.",
|
||||||
"Antenne motorisée : sur une bande non cochée dans Bandes couvertes, OpsLog n'inhibe plus l'émission du FlexRadio pour « antenne en mouvement ». Décocher une bande signifie désormais ne plus y toucher du tout — ni accord ni blocage TX — pour ne jamais couper le trafic sur une autre antenne.",
|
"Antenne motorisée : sur une bande non cochée dans Bandes couvertes, OpsLog n'inhibe plus l'émission du FlexRadio pour « antenne en mouvement ». Décocher une bande signifie désormais ne plus y toucher du tout — ni accord ni blocage TX — pour ne jamais couper le trafic sur une autre antenne.",
|
||||||
"Diplômes : un nouveau sélecteur Réglages → Diplômes (dans Configuration utilisateur) permet de choisir les diplômes à suivre — une liste à deux colonnes, tous les diplômes à gauche, ceux que tu suis à droite. L'onglet Awards n'affiche alors que ceux-là (par profil ; laisse la colonne de droite vide pour tous les afficher)."
|
"Diplômes : un nouveau sélecteur Réglages → Diplômes (dans Configuration utilisateur) permet de choisir les diplômes à suivre — une liste à deux colonnes, tous les diplômes à gauche, ceux que tu suis à droite. L'onglet Awards n'affiche alors que ceux-là (par profil ; laisse la colonne de droite vide pour tous les afficher).",
|
||||||
|
"CAT Kenwood/Elecraft : le lien ne tombe plus pendant que le rig émet. Un K3 (et d'autres rigs en dialecte Kenwood) répond \"?;\" à une interrogation d'état pendant l'émission ; OpsLog le prenait pour \"le rig ne supporte pas IF\" et se déconnectait — ce qui cassait le CAT partagé que WSJT-X / JTDX utilisent pour passer en émission, d'où l'absence d'émission. OpsLog suspend désormais l'interrogation d'état tant que le PTT est actif et garde le lien."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+32
-4
@@ -115,6 +115,17 @@ type Kenwood struct {
|
|||||||
// nothing. Off by default: that is how this backend behaved for its whole
|
// nothing. Off by default: that is how this backend behaved for its whole
|
||||||
// life before the question came up.
|
// life before the question came up.
|
||||||
lowerLines bool
|
lowerLines bool
|
||||||
|
|
||||||
|
// tx tracks whether we currently hold PTT (SetPTT true). A Kenwood/Elecraft rig
|
||||||
|
// answers "?;" to a status poll (IF;) WHILE TRANSMITTING; OpsLog used to read
|
||||||
|
// that as "the rig doesn't support IF;", latch it off and drop the whole CAT
|
||||||
|
// link — which tore down the shared-CAT PTT that WSJT-X / JTDX key through, so a
|
||||||
|
// K3 keyed but the logger's transmission fell apart. While PTT is held we skip
|
||||||
|
// the wire poll and hand back the last good state (lastState) instead. txAt caps
|
||||||
|
// the skip so a missed SetPTT(false) can't freeze the state for ever.
|
||||||
|
tx bool
|
||||||
|
txAt time.Time
|
||||||
|
lastState RigState
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLowerLines chooses whether DTR and RTS are deasserted on connect. Set
|
// SetLowerLines chooses whether DTR and RTS are deasserted on connect. Set
|
||||||
@@ -255,6 +266,15 @@ func (k *Kenwood) ReadState() (RigState, error) {
|
|||||||
if k.port == nil {
|
if k.port == nil {
|
||||||
return RigState{}, fmt.Errorf("kenwood: not connected")
|
return RigState{}, fmt.Errorf("kenwood: not connected")
|
||||||
}
|
}
|
||||||
|
// While transmitting, don't poll: the rig returns "?;" to IF; during TX, and
|
||||||
|
// treating that as a fault dropped the shared CAT link the digital-mode
|
||||||
|
// software keys through. Hand back the last known state. The 30 s cap resyncs
|
||||||
|
// if a SetPTT(false) was somehow missed, so a stuck TX can't freeze state.
|
||||||
|
if k.tx && !k.txAt.IsZero() && time.Since(k.txAt) < 30*time.Second {
|
||||||
|
s := k.lastState
|
||||||
|
s.Connected = true
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
raw, err := k.ask("IF;")
|
raw, err := k.ask("IF;")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return RigState{}, err
|
return RigState{}, err
|
||||||
@@ -339,6 +359,7 @@ func (k *Kenwood) ReadState() (RigState, error) {
|
|||||||
if s.Split {
|
if s.Split {
|
||||||
k.curRXFreq = s.RxFreqHz
|
k.curRXFreq = s.RxFreqHz
|
||||||
}
|
}
|
||||||
|
k.lastState = s // cache for the transmit window, where we can't poll
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +430,9 @@ func (k *Kenwood) SetPTT(on bool) error {
|
|||||||
if k.port == nil {
|
if k.port == nil {
|
||||||
return fmt.Errorf("kenwood: not connected")
|
return fmt.Errorf("kenwood: not connected")
|
||||||
}
|
}
|
||||||
|
k.tx = on
|
||||||
if on {
|
if on {
|
||||||
|
k.txAt = time.Now()
|
||||||
return k.write("TX;")
|
return k.write("TX;")
|
||||||
}
|
}
|
||||||
return k.write("RX;")
|
return k.write("RX;")
|
||||||
@@ -467,10 +490,15 @@ func (k *Kenwood) ask(cmd string) (string, error) {
|
|||||||
k.rx = k.rx[i+1:]
|
k.rx = k.rx[i+1:]
|
||||||
traceText("kenwood", "RX", frame)
|
traceText("kenwood", "RX", frame)
|
||||||
if frame == "?;" {
|
if frame == "?;" {
|
||||||
// The rig rejected the command. Remember it so the poll loop stops
|
// IF; and ID; are universal on Kenwood/Elecraft — a "?;" to them is a
|
||||||
// paying a 600 ms timeout for it on every cycle.
|
// transient "busy" (typically mid-transmit, or a menu open on the rig),
|
||||||
k.unsupported[want] = true
|
// NOT "unsupported". Latching them off would blind the poll loop for
|
||||||
debugLog.Printf("kenwood: this rig does not support %q — not asking again", cmd)
|
// good and read as "lost the rig". Only remember the OPTIONAL commands
|
||||||
|
// (FR/FT/…) so the poll loop stops paying a 600 ms timeout for those.
|
||||||
|
if want != "IF" && want != "ID" {
|
||||||
|
k.unsupported[want] = true
|
||||||
|
debugLog.Printf("kenwood: this rig does not support %q — not asking again", cmd)
|
||||||
|
}
|
||||||
return "", fmt.Errorf("kenwood: %s rejected", want)
|
return "", fmt.Errorf("kenwood: %s rejected", want)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(frame, want) {
|
if strings.HasPrefix(frame, want) {
|
||||||
|
|||||||
Reference in New Issue
Block a user