fix(udp): ignore N0CALL, and stop returning a grid as a callsign
N0CALL is what WSJT-X transmits under when its owner never set a callsign. It has a letter, a digit and an ordinary shape, so nothing rejected it: it was spotted, coloured, counted as a new WPX prefix, and now that CQ grids are read it would have put a grid into the worked index under a callsign nobody holds. The obvious fix - adding it to looksLikeCall's reject list - was wrong, and the test caught it before it shipped. That function answers "could this token be a callsign at all", and the CQ grammar uses it to decide whether the word after CQ is a modifier (DX, NA, a zone) or the call itself. Teaching it that N0CALL is not a callsign made "CQ N0CALL JN36" skip a slot and return JN36. Shape and policy are different questions and now live in different functions. Chasing that turned up the real defect behind it: ANY unrecognised word after CQ made the parser skip a slot, and a four-character grid passes every shape test a callsign does. "CQ FOO JN36" returned JN36 as the sender - logged, spotted and coloured as a station. A grid in the callsign slot is now refused outright.
This commit is contained in:
@@ -284,19 +284,32 @@ func wsjtSender(message string) (call string, isCQ bool, grid string) {
|
||||
// Skip an optional modifier after CQ (DX / a region like NA / a zone like
|
||||
// 020) — it never looks like a callsign (no letter+digit mix).
|
||||
idx := 1
|
||||
if len(f) > 2 && !looksLikeCall(f[1]) {
|
||||
// A word after CQ that cannot be a callsign is a modifier — UNLESS it is a
|
||||
// placeholder, which does occupy the callsign slot, just uselessly. Without
|
||||
// that exception "CQ NOCALL JN36" skipped a slot it should have rejected.
|
||||
if len(f) > 2 && !looksLikeCall(f[1]) && !isPlaceholderCall(f[1]) {
|
||||
idx = 2
|
||||
}
|
||||
if idx < len(f) && looksLikeCall(f[idx]) {
|
||||
if idx+1 < len(f) && isGridField(f[idx+1]) {
|
||||
grid = f[idx+1]
|
||||
if idx < len(f) {
|
||||
c := f[idx]
|
||||
if isPlaceholderCall(c) {
|
||||
return "", true, ""
|
||||
}
|
||||
// A GRID sitting in the callsign slot means the real call was
|
||||
// unparseable and the skip above went one word too far. JN36 has letters
|
||||
// and digits and passes every shape test there is, so without this the
|
||||
// station's grid gets logged, spotted and coloured as its callsign.
|
||||
if looksLikeCall(c) && !isGridField(c) {
|
||||
if idx+1 < len(f) && isGridField(f[idx+1]) {
|
||||
grid = f[idx+1]
|
||||
}
|
||||
return c, true, grid
|
||||
}
|
||||
return f[idx], true, grid
|
||||
}
|
||||
return "", true, ""
|
||||
}
|
||||
// Standard exchange: the DE (sender) call is the second token.
|
||||
if len(f) >= 2 && looksLikeCall(f[1]) {
|
||||
if len(f) >= 2 && looksLikeCall(f[1]) && !isPlaceholderCall(f[1]) {
|
||||
return f[1], false, ""
|
||||
}
|
||||
return "", false, ""
|
||||
@@ -317,6 +330,24 @@ func isGridField(s string) bool {
|
||||
s[2] >= '0' && s[2] <= '9' && s[3] >= '0' && s[3] <= '9'
|
||||
}
|
||||
|
||||
// isPlaceholderCall reports a callsign nobody actually holds.
|
||||
//
|
||||
// N0CALL is WSJT-X's default: someone who installed it and never set their own
|
||||
// callsign transmits under it. It has a letter and a digit and a perfectly
|
||||
// ordinary shape, so nothing else rejects it — it would be spotted, coloured,
|
||||
// counted as a new prefix, and now that CQ grids are read it would bring a grid
|
||||
// into the worked index. NOCALL (letter O) has no digit and already fails the
|
||||
// shape test; it is listed so the rule can be read without deducing that.
|
||||
//
|
||||
// Kept OUT of looksLikeCall deliberately. That function answers "could this
|
||||
// token be a callsign at all", and the CQ grammar uses it to decide whether the
|
||||
// word after CQ is a modifier (DX, NA, a zone) or the callsign itself. Teaching
|
||||
// it that N0CALL is not a callsign made "CQ N0CALL JN36" skip to the next token
|
||||
// and return the GRID as the sender. Shape and policy are different questions.
|
||||
func isPlaceholderCall(s string) bool {
|
||||
return s == "N0CALL" || s == "NOCALL"
|
||||
}
|
||||
|
||||
// looksLikeCall is a loose callsign test: 3–12 chars of A–Z/0–9//, with at least
|
||||
// one letter AND one digit. Rejects the fixed exchange tokens (RR73/RRR/73) that
|
||||
// would otherwise pass.
|
||||
|
||||
Reference in New Issue
Block a user