fix: CI-V frames read at the wrong offset decoded as plausible frequencies

From F4JND's log: a rig sitting on 145.550 MHz FM produced status lines reading
16445550000, 8364550000, 5817408364 and 12640 Hz between good readings — the last
of those even resolved to a 6 cm band.

BCDToFreq treated the nibbles 0x0A..0x0F as the digits 10..15, so a byte stream
that had lost frame sync still decoded into a number. The same log shows why sync
was lost: PTT round trips timing out and a scope stream the rig kept rejecting
(0x27), both competing with the poll on one serial line.

Non-decimal nibbles are now refused. Each caller keeps its last good value rather
than publishing noise: the frequency reader returns an error, the sub-VFO reader
returns not-ok, and the scope keeps its previous edges. Garbage that LOOKS like a
frequency is worse than a refused frame — it reaches the display, the band slots
and eventually the log, and it is the operator who then has to disprove it.

The test builds its fixture with FreqToBCD rather than by hand, because my
hand-written one was byte-reversed and the test caught it.

This does NOT explain the white screen that was also reported, and I have not
claimed it does: it removes one source of absurd values reaching the UI, which is
worth doing on its own evidence.
This commit is contained in:
2026-07-30 14:05:30 +02:00
parent fbd6cf400f
commit ef84b04c99
6 changed files with 98 additions and 20 deletions
+17 -4
View File
@@ -160,16 +160,29 @@ func FreqToBCD(hz int64) []byte {
}
// BCDToFreq decodes Icom little-endian BCD frequency bytes back to Hz.
func BCDToFreq(b []byte) int64 {
//
// It returns ok=false when a nibble is not a decimal digit. That check is the
// point of the function: a CI-V byte stream that has lost frame sync — a reply
// read at the wrong offset, a collision with the scope stream — still decodes
// into a number if 0x0A..0x0F are quietly treated as 10..15. An operator on a
// 2 m FM channel then saw entries like 16445550000 Hz appear in the status bar
// between good readings (F4JND, 2026-07-30). Garbage that LOOKS like a
// frequency is worse than a refused frame: it reaches the display, the band
// slots, and eventually the log.
func BCDToFreq(b []byte) (int64, bool) {
var hz int64
mult := int64(1)
for i := 0; i < len(b) && i < 5; i++ {
hz += int64(b[i]&0x0F) * mult
lo, hi := b[i]&0x0F, b[i]>>4
if lo > 9 || hi > 9 {
return 0, false
}
hz += int64(lo) * mult
mult *= 10
hz += int64(b[i]>>4) * mult
hz += int64(hi) * mult
mult *= 10
}
return hz
return hz, true
}
// LevelToBCD encodes a 0-255 level as the 2 big-endian BCD bytes Icom's