fix(kenwood): split frequencies swapped on transmit
IF reports the VFO "in use". In split that is the RECEIVE VFO while receiving and the TRANSMIT VFO while transmitting — the backend took it as the receive VFO in both cases. Everything therefore read correctly until the PTT closed and then reversed, which is exactly how it was reported from a TS-590 on USB. The IF frame has carried the transmit bit all along; parseKenwoodIF already decoded it into f.TX. It was simply never consulted here. This is not only a display fault. FreqHz is what a QSO is LOGGED on, so a contact worked in split went into the log on the DX's frequency instead of the operator's — wrong in the log, wrong in every ADIF exported from it, and invisible until someone checked a QSO by hand. The emulated rig in the test package can now be keyed, so the case is pinned from both sides: verified failing without the fix (tx and rx exchanged) and passing with it.
This commit is contained in:
+24
-4
@@ -377,16 +377,36 @@ func (k *Kenwood) ReadState() (RigState, error) {
|
||||
f.Split = split
|
||||
|
||||
if f.Split {
|
||||
// The transmit VFO is the other one. Read it rather than assume, and fall
|
||||
// back to simplex if it cannot be read: a wrong TX frequency is written
|
||||
// into the log, which is worse than showing no split at all.
|
||||
// The OTHER VFO is the one IF did not report. Read it rather than assume,
|
||||
// and fall back to simplex if it cannot be read: a wrong TX frequency is
|
||||
// written into the log, which is worse than showing no split at all.
|
||||
other := "FB;"
|
||||
if f.VFO == "B" {
|
||||
other = "FA;"
|
||||
}
|
||||
var otherHz int64
|
||||
if r, err := k.ask(other); err == nil {
|
||||
if hz, ok := parseKenwoodFreq(r, strings.TrimSuffix(other, ";")); ok && hz > 0 && hz != rx {
|
||||
tx = hz
|
||||
otherHz = hz
|
||||
}
|
||||
}
|
||||
if otherHz > 0 {
|
||||
// WHICH of the two is the transmit frequency depends on whether the rig
|
||||
// is transmitting RIGHT NOW.
|
||||
//
|
||||
// IF reports the VFO "in use", and in split that is the RECEIVE VFO on
|
||||
// receive and the TRANSMIT VFO on transmit. The code took it as the
|
||||
// receive VFO always, so the moment the operator keyed up the two
|
||||
// frequencies swapped: correct on receive, reversed on transmit, which
|
||||
// is exactly how it was reported from a TS-590 on USB.
|
||||
//
|
||||
// It matters beyond the display. FreqHz is what a QSO is logged on, and
|
||||
// a contact made in split would have gone into the log on the DX's
|
||||
// frequency instead of the operator's.
|
||||
if f.TX {
|
||||
tx, rx = rx, otherHz
|
||||
} else {
|
||||
tx = otherHz
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,9 @@ type ts2000 struct {
|
||||
mode byte
|
||||
onB bool
|
||||
split bool
|
||||
// tx models the rig KEYED. On a real Kenwood in split, IF then reports the
|
||||
// TRANSMIT VFO as the one in use — which is the whole point of the test below.
|
||||
tx bool
|
||||
// lazyIF models a rig that answers FR/FT correctly but never fills IF's
|
||||
// split bit — the behaviour reported on a Flex through its Kenwood CAT
|
||||
// emulation, where the frequency reads perfectly and split never appears.
|
||||
@@ -138,8 +141,12 @@ func (r *ts2000) answer(cmd string) string {
|
||||
}
|
||||
// The catemu layout: IF | freq(11) | step(4) | RIT(±5) | 3 | mem(2) |
|
||||
// rx/tx | mode | VFO | scan | split | tone | tone#(2) | shift | ;
|
||||
txByte := 0
|
||||
if r.tx {
|
||||
txByte = 1
|
||||
}
|
||||
return fmt.Sprintf("IF%011d%04d%+06d%03d%02d%01d%c%c%01d%c%01d%02d%01d;",
|
||||
cur, 0, 0, 0, 0, 0, r.mode, vfoDigit, 0, split, 0, 0, 0)
|
||||
cur, 0, 0, 0, 0, txByte, r.mode, vfoDigit, 0, split, 0, 0, 0)
|
||||
case strings.HasPrefix(cmd, "FA") && len(cmd) > 3:
|
||||
fmt.Sscanf(cmd, "FA%d;", &r.vfoA)
|
||||
return ""
|
||||
@@ -456,3 +463,43 @@ func TestKenwoodBusyRigKeepsTheLink(t *testing.T) {
|
||||
t.Error("a rig refusing IF; forever was still reported as healthy")
|
||||
}
|
||||
}
|
||||
|
||||
// In split, the two frequencies must not swap the moment the operator keys up.
|
||||
//
|
||||
// IF reports the VFO "in use", and in split that is the RECEIVE VFO on receive
|
||||
// and the TRANSMIT VFO on transmit. The backend took it as the receive VFO
|
||||
// always, so everything read correctly until the PTT closed and then reversed —
|
||||
// reported from a TS-590 on USB.
|
||||
//
|
||||
// It matters beyond the display: FreqHz is what a QSO is LOGGED on, so a split
|
||||
// contact would have been logged on the DX's frequency instead of the operator's.
|
||||
func TestKenwoodSplitDoesNotSwapOnTransmit(t *testing.T) {
|
||||
rig := &ts2000{vfoA: 14200000, vfoB: 14205000, mode: '2', split: true}
|
||||
k := NewKenwood("COM-TEST", 9600, "FT8")
|
||||
k.dialPort = dialTo(rig)
|
||||
if err := k.Connect(); err != nil {
|
||||
t.Fatalf("connect: %v", err)
|
||||
}
|
||||
defer k.Disconnect()
|
||||
|
||||
// Receiving: IF reports VFO A, the receive dial.
|
||||
s, err := k.ReadState()
|
||||
if err != nil {
|
||||
t.Fatalf("read on receive: %v", err)
|
||||
}
|
||||
if !s.Split || s.FreqHz != 14205000 || s.RxFreqHz != 14200000 {
|
||||
t.Fatalf("on receive: split=%v tx=%d rx=%d — want tx 14205000, rx 14200000",
|
||||
s.Split, s.FreqHz, s.RxFreqHz)
|
||||
}
|
||||
|
||||
// Keyed: the rig switches to the transmit VFO and sets IF's TX byte.
|
||||
rig.onB, rig.tx = true, true
|
||||
s, err = k.ReadState()
|
||||
if err != nil {
|
||||
t.Fatalf("read on transmit: %v", err)
|
||||
}
|
||||
if s.FreqHz != 14205000 || s.RxFreqHz != 14200000 {
|
||||
t.Errorf("on transmit: tx=%d rx=%d — the two swapped; want tx 14205000, rx 14200000",
|
||||
s.FreqHz, s.RxFreqHz)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user