fix(udp): ':' is Q65 to WSJT-X and FT4 to JTDX

Reported from a real shack: JTDX decoding FT4 came through as Q65. A
Decode carries a one-character marker rather than a mode name, and the
forks do not agree on that one — so the character alone cannot answer,
and its wrong answer reached everything behind it: new mode, new slot,
the mode filter, all computed against a mode nobody was using.

The sending program's own Status settles it. It comes from that program,
names the mode in full, and is re-sent whenever it changes, so it knows
what is being decoded in a way one character never can. Only the
ambiguous marker consults it: the ones both forks agree on keep
answering from the table, Status or no Status, and with no Status at all
':' still reads as Q65 — WSJT-X's meaning, the older and commoner.
This commit is contained in:
2026-09-04 18:48:25 +02:00
parent 1a32e4a228
commit 2d67e3d57f
3 changed files with 50 additions and 3 deletions
@@ -113,3 +113,27 @@ func TestDecodeKeepsTheRawModeMarkerForReplies(t *testing.T) {
ev.DecodeModeRaw, "~")
}
}
// Reported from a real shack: JTDX decoding FT4 showed up as Q65.
//
// The two forks disagree about ":" — Q65 in WSJT-X, FT4 in JTDX — so the
// character alone cannot answer, and the wrong answer poisons every verdict
// that follows: new mode, new slot, the mode filter. The program's own Status
// names the mode in full and settles it.
func TestAmbiguousModeCharDefersToTheProgram(t *testing.T) {
cases := []struct {
raw, status, want, why string
}{
{":", "FT4", "FT4", "JTDX decoding FT4"},
{":", "Q65", "Q65", "WSJT-X decoding Q65"},
{":", "", "Q65", "no Status yet — WSJT-X's reading, the older and commoner"},
// The unambiguous markers are unaffected, Status or no Status.
{"~", "FT4", "FT8", "a tilde is FT8 in both"},
{"+", "", "FT4", "a plus is FT4 in both"},
}
for _, c := range cases {
if got := DecodeModeName(c.raw, c.status); got != c.want {
t.Errorf("DecodeModeName(%q, %q) = %q, want %q — %s", c.raw, c.status, got, c.want, c.why)
}
}
}