fix: FTDX101 answers the VFO query with two digits, and only the first was read
The log named the cause in one line: yaesu: receive VFO is read through FR (answered "FR01;"). An FTDX10 answers "FR0;" — one digit — so reading the digit straight after the prefix worked there and took the 0 of "01" on an FTDX101. The rig was on SUB and OpsLog said MAIN, which is the reported "Sub shows the main frequency". It also explains the second half: split is "TX VFO differs from RX VFO", so a receive VFO that alternated between right and wrong made the split flag flip between consecutive polls and swap freq and rx in the log. The state is now taken from the LAST digit before the terminator, which is correct on both forms, and the same reading applies to ST/FT/VS rather than leaving the next two-digit rig to be found in the field. On the operator's question of whether freq should simply be the active VFO: no — freq is the TRANSMIT frequency by ADIF convention (FREQ is the QSO frequency, FREQ_RX the receive one, set only when split). In simplex they are the same, so it does read as the active VFO; in split it must stay the transmit VFO or the log records the wrong frequency. What the operator saw was this bug, not that rule.
This commit is contained in:
+29
-5
@@ -236,11 +236,11 @@ func (y *Yaesu) ReadState() (RigState, error) {
|
||||
vfo := "A"
|
||||
switch {
|
||||
case y.rxVFOCmd != "":
|
||||
if r, err := y.ask(y.rxVFOCmd + ";"); err == nil && len(r) >= len(y.rxVFOCmd)+1 && r[len(y.rxVFOCmd)] == '1' {
|
||||
if r, err := y.ask(y.rxVFOCmd + ";"); err == nil && yaesuStateDigit(r, y.rxVFOCmd) == '1' {
|
||||
vfo = "B"
|
||||
}
|
||||
default:
|
||||
if r, err := y.ask("VS;"); err == nil && len(r) >= 3 && r[2] == '1' {
|
||||
if r, err := y.ask("VS;"); err == nil && yaesuStateDigit(r, "VS") == '1' {
|
||||
vfo = "B"
|
||||
}
|
||||
}
|
||||
@@ -437,11 +437,10 @@ func parseYaesuFreq(reply, prefix string) (int64, bool) {
|
||||
// correctly for one operator and backwards for another — one was on MAIN, the
|
||||
// other on SUB.
|
||||
func yaesuSplitFromReply(reply, cmd, vfo string) bool {
|
||||
r := strings.TrimSpace(reply)
|
||||
if !strings.HasPrefix(r, cmd) || len(r) < len(cmd)+1 {
|
||||
d := yaesuStateDigit(reply, cmd)
|
||||
if d == 0 {
|
||||
return false
|
||||
}
|
||||
d := r[len(cmd)]
|
||||
if cmd == "ST" {
|
||||
return d == '1'
|
||||
}
|
||||
@@ -506,3 +505,28 @@ func yaesuModeDigit(mode string, freqHz int64) byte {
|
||||
return 'C' // DATA-USB
|
||||
}
|
||||
}
|
||||
|
||||
// yaesuStateDigit returns the STATE digit of a reply — the last digit before the
|
||||
// terminator — or 0 if the reply does not belong to this command.
|
||||
//
|
||||
// The parameter is not always one character. An FTDX101 answers "FR01;" where an
|
||||
// FTDX10 answers "FR0;", and reading the FIRST digit took the 0 of "01" as the
|
||||
// answer: the rig was on SUB and OpsLog said MAIN, showing the main frequency and
|
||||
// making the split flip on alternate polls (F4NBZ, 2026-07-29). The last digit is
|
||||
// the state on both, so it is what gets read.
|
||||
func yaesuStateDigit(reply, cmd string) byte {
|
||||
r := strings.TrimSpace(reply)
|
||||
if !strings.HasPrefix(r, cmd) {
|
||||
return 0
|
||||
}
|
||||
r = strings.TrimSuffix(r, ";")
|
||||
digits := r[len(cmd):]
|
||||
if digits == "" {
|
||||
return 0
|
||||
}
|
||||
last := digits[len(digits)-1]
|
||||
if last < '0' || last > '9' {
|
||||
return 0
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
@@ -222,3 +222,45 @@ func TestYaesuReceiveVFOAndSplit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The state digit of a reply, when the parameter is not one character.
|
||||
//
|
||||
// An FTDX101 answers "FR01;" where an FTDX10 answers "FR0;". Reading the FIRST
|
||||
// digit took the 0 of "01" as the answer, so a rig on SUB reported MAIN: OpsLog
|
||||
// showed the main frequency, and — since split is "TX VFO differs from RX VFO" —
|
||||
// the split flag flipped between consecutive polls, swapping freq and rx in the
|
||||
// log (F4NBZ, 2026-07-29). The last digit is the state on both rigs.
|
||||
func TestYaesuStateDigit(t *testing.T) {
|
||||
cases := []struct {
|
||||
reply, cmd string
|
||||
want byte
|
||||
}{
|
||||
{"FR0;", "FR", '0'}, // FTDX10 form
|
||||
{"FR1;", "FR", '1'},
|
||||
{"FR01;", "FR", '1'}, // FTDX101 form — the bug: this read as '0'
|
||||
{"FR00;", "FR", '0'},
|
||||
{"ST1;", "ST", '1'},
|
||||
{"FT0;", "FT", '0'},
|
||||
{"VS1;", "VS", '1'},
|
||||
{"FR1", "FR", '1'}, // terminator already stripped
|
||||
|
||||
{"ST1;", "FR", 0}, // another command's reply is never accepted
|
||||
{"FR;", "FR", 0}, // query echoed with no value
|
||||
{"FRx;", "FR", 0}, // not a digit
|
||||
{"", "FR", 0},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := yaesuStateDigit(c.reply, c.cmd); got != c.want {
|
||||
t.Errorf("yaesuStateDigit(%q, %q) = %q, want %q", c.reply, c.cmd, got, c.want)
|
||||
}
|
||||
}
|
||||
|
||||
// End to end: the FTDX101 two-digit form must place the operator on SUB.
|
||||
if d := yaesuStateDigit("FR01;", "FR"); d != '1' {
|
||||
t.Fatalf("FR01 read as %q — the operator is on SUB and must be seen there", d)
|
||||
}
|
||||
// And with the receive VFO right, both-on-sub is correctly NOT split.
|
||||
if yaesuSplitFromReply("FT01;", "FT", "B") {
|
||||
t.Error("RX and TX both on sub reported as split")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user