fix(decodes): a decode's mode is a marker, not a mode name

Every station on an already-worked band was flagged NEW MODE.

A WSJT-X Decode does not carry the mode's name. It carries the
one-character marker from the decode line - "~" for FT8, "+" for FT4 - and
that character was passed straight through as though it were a mode. The
status resolver then compared "~" against the modes worked for the entity,
matched nothing, and concluded the mode had never been worked. Same cause
put "~ -07" in the comment of every decode spot pushed to the FlexRadio
panadapter, which nobody had traced back.

Resolved through a marker table, with the mode from the sender's last
Status as the fallback - Status is the message that carries the real name.
So an unlisted or future marker degrades to correct rather than to
nonsense, and a sender that puts the name in the field directly is believed
as-is. With neither available the mode is left empty, which makes the
resolver answer "worked": the safe side, since a wrong mode invents a
new-mode flag exactly as the marker did.
This commit is contained in:
2026-08-18 11:40:13 +02:00
parent 453e0df27b
commit 47992b5f03
3 changed files with 119 additions and 1 deletions
+13 -1
View File
@@ -183,6 +183,9 @@ type Server struct {
// lastFrom is the address each program's packets arrive from — where a Reply
// has to be sent. See SendReply.
lastFrom map[string]*net.UDPAddr
// lastMode is the mode NAME from each program's last Status, used to resolve
// a Decode's one-character mode marker.
lastMode map[string]string
lastDX string // WSJT: last non-empty DX Call seen, to detect a clear
// badPkts counts datagrams this listener could not parse, so the diagnostic
@@ -436,6 +439,14 @@ func (s *Server) handle(pkt []byte, remote *net.UDPAddr) {
}
s.trPeriod[w.ProgramID] = w.TRPeriod
}
// The mode NAME, which only Status carries: a Decode gives the
// one-character marker instead. See DecodeModeName.
if w.Mode != "" {
if s.lastMode == nil {
s.lastMode = map[string]string{}
}
s.lastMode[w.ProgramID] = w.Mode
}
s.mu.Unlock()
}
if !w.IsDecode && (w.TxMessage != "" || w.DECall != "") {
@@ -452,6 +463,7 @@ func (s *Server) handle(pkt []byte, remote *net.UDPAddr) {
s.mu.Lock()
dial := s.dialHz[w.ProgramID]
tr := s.trPeriod[w.ProgramID]
statusMode := s.lastMode[w.ProgramID]
s.mu.Unlock()
if dial <= 0 {
// No Status from THIS instance yet. Guessing with another
@@ -464,7 +476,7 @@ func (s *Server) handle(pkt []byte, remote *net.UDPAddr) {
ev.DecodeFreqHz = dial + w.DeltaFreqHz
ev.DecodeSNR = w.SNR
ev.DecodeCQ = w.IsCQ
ev.Mode = w.Mode
ev.Mode = DecodeModeName(w.Mode, statusMode)
ev.DecodeMsg = w.DecodeMsg
ev.DecodeAt = decodeTime(w.DecodeMsSinceMidnight)
ev.DecodeTRPeriod = tr