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:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,10 +523,24 @@ var decodeModeChar = map[string]string{
|
||||
"#": "JT65",
|
||||
"@": "JT9",
|
||||
"&": "MSK144",
|
||||
":": "Q65",
|
||||
"`": "FST4",
|
||||
}
|
||||
|
||||
// ambiguousModeChar is a marker the forks do not agree on.
|
||||
//
|
||||
// ":" is Q65 in WSJT-X and FT4 in JTDX, so the character alone cannot answer:
|
||||
// a JTDX operator decoding FT4 was told they were on Q65, and every verdict
|
||||
// downstream — new mode, new slot, the mode filter — was computed against a
|
||||
// mode nobody was using.
|
||||
//
|
||||
// The sender's own Status settles it. It comes from the same program, names the
|
||||
// mode in full, and is re-sent whenever it changes, so it knows what that
|
||||
// program is decoding in a way one character never can. The fallback is
|
||||
// WSJT-X's reading, which is the older and commoner one.
|
||||
var ambiguousModeChar = map[string]string{
|
||||
":": "Q65",
|
||||
}
|
||||
|
||||
// DecodeModeName resolves a Decode's mode field to a real mode name. statusMode
|
||||
// is the mode from the same program's last Status, used when the field is a
|
||||
// marker we do not know, or empty.
|
||||
@@ -535,6 +549,13 @@ func DecodeModeName(raw, statusMode string) string {
|
||||
if m, ok := decodeModeChar[raw]; ok {
|
||||
return m
|
||||
}
|
||||
if fallback, ok := ambiguousModeChar[raw]; ok {
|
||||
// Believe the program over the character it happened to print.
|
||||
if st := strings.ToUpper(strings.TrimSpace(statusMode)); st != "" {
|
||||
return st
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
// A mode name is at least two alphanumeric characters ("FT8", "JS8", "Q65").
|
||||
// Anything shorter, or carrying punctuation, is a marker rather than a name.
|
||||
if len(raw) >= 2 {
|
||||
|
||||
Reference in New Issue
Block a user