fix(dxcc): /MM and /AM belong to no entity

RI1FJL/MM resolved to Franz Josef Land while the expedition was still sailing
there, telling the operator they had worked an entity they had not.

The old behaviour was deliberate — the comment read "strict DXCC says no
entity, but the log should still show the operator's country" — and that is
wrong for exactly this reason. A home country on a maritime mobile is not extra
information; it is a false claim about where the contact happened, and it is
the kind of false claim that gets a QSO submitted for an award it cannot win.

Trailing only. A LEADING "MM" is the Scotland prefix and "AM" is Spain, so
MM0ABC and AM5X keep their entities — getting that wrong would be a far larger
error than the one being fixed. /P, /M and /B are unaffected: a portable
station is still ashore.

In the cluster the column says "Maritime mobile — no DXCC" rather than going
blank, because blank is what an UNRESOLVED spot looks like and the two mean
opposite things — one is "we don't know yet", this is "there is nothing to
know". Display only: the QSO's own Country stays empty, since that field holds
a DXCC entity name and there is none.
This commit is contained in:
2026-08-13 12:25:33 +02:00
parent dc0b00b474
commit 4ba5569ca8
6 changed files with 443 additions and 361 deletions
+10
View File
@@ -17452,6 +17452,16 @@ func (a *App) ClusterSpotStatuses(spots []SpotQuery) []SpotStatus {
if a.dxcc == nil { if a.dxcc == nil {
continue continue
} }
// Say WHY a maritime/aeronautical mobile has no entity, rather than
// leaving the column blank — blank is what an unresolved spot looks like,
// and the two mean opposite things: one is "we don't know yet", this is
// "there is nothing to know, the station is at sea". Display only: the
// QSO's own Country stays empty, because it is a DXCC entity name and
// there is none.
if dxcc.IsMobileNoEntity(q.Call) {
out[i].Country = "Maritime mobile — no DXCC"
continue
}
m, ok := a.dxcc.Lookup(q.Call) m, ok := a.dxcc.Lookup(q.Call)
if !ok || m.Entity == nil { if !ok || m.Entity == nil {
continue continue
+4 -2
View File
@@ -10,7 +10,8 @@
"Station Control: the short and long path headings are repeated under the compass, at a readable size and clickable.", "Station Control: the short and long path headings are repeated under the compass, at a readable size and clickable.",
"Compact mode: the window now fits the entry strip instead of leaving a band of empty space below it.", "Compact mode: the window now fits the entry strip instead of leaving a band of empty space below it.",
"HRDLog: an ON AIR option publishes your live frequency, mode and rig on hrdlog.net.", "HRDLog: an ON AIR option publishes your live frequency, mode and rig on hrdlog.net.",
"ADIF: the county is exported as STATE,COUNTY as the standard requires, and a county imported that way now fills the state too." "ADIF: the county is exported as STATE,COUNTY as the standard requires, and a county imported that way now fills the state too.",
"A /MM or /AM callsign no longer resolves to a country — RI1FJL/MM read as Franz Josef Land while the expedition was still sailing there."
], ],
"fr": [ "fr": [
"Cluster : « Masquer les contactés » ne masque plus un spot qui est un nouveau préfixe, comté, carré ou parc dans une contrée déjà faite.", "Cluster : « Masquer les contactés » ne masque plus un spot qui est un nouveau préfixe, comté, carré ou parc dans une contrée déjà faite.",
@@ -20,7 +21,8 @@
"Contrôle station : les azimuts court et long chemin sont repris sous la boussole, lisibles et cliquables.", "Contrôle station : les azimuts court et long chemin sont repris sous la boussole, lisibles et cliquables.",
"Mode compact : la fenêtre épouse la bande de saisie au lieu de laisser une bande vide en dessous.", "Mode compact : la fenêtre épouse la bande de saisie au lieu de laisser une bande vide en dessous.",
"HRDLog : une option ON AIR publie ta fréquence, ton mode et ta radio en direct sur hrdlog.net.", "HRDLog : une option ON AIR publie ta fréquence, ton mode et ta radio en direct sur hrdlog.net.",
"ADIF : le comté est exporté sous la forme ÉTAT,COMTÉ comme l exige le standard, et un comté importé ainsi remplit aussi l état." "ADIF : le comté est exporté sous la forme ÉTAT,COMTÉ comme l exige le standard, et un comté importé ainsi remplit aussi l état.",
"Un indicatif en /MM ou /AM ne se résout plus en pays — RI1FJL/MM affichait Franz Josef Land alors que l expédition faisait encore route."
] ]
}, },
{ {
+31
View File
@@ -132,6 +132,18 @@ func (db *DB) Entities() []*Entity {
func (db *DB) Lookup(callsign string) (Match, bool) { func (db *DB) Lookup(callsign string) (Match, bool) {
db.mu.RLock() db.mu.RLock()
defer db.mu.RUnlock() defer db.mu.RUnlock()
// Maritime and aeronautical mobile belong to NO entity, and that is not a
// technicality — the station is at sea or in the air. RI1FJL/MM resolved to
// Franz Josef Land while the expedition was still on its way there, telling
// the operator they had worked an entity they had not.
//
// The previous choice was deliberate ("the log should still show the
// operator's country") and it is wrong for exactly that reason: a home
// country here is not extra information, it is a false claim about where the
// contact happened.
if IsMobileNoEntity(callsign) {
return Match{}, false
}
call := normalizeCallsign(callsign) call := normalizeCallsign(callsign)
if call == "" { if call == "" {
return Match{}, false return Match{}, false
@@ -364,3 +376,22 @@ func replaceAreaDigit(call string, d byte) string {
} }
return call return call
} }
// IsMobileNoEntity reports a callsign DXCC assigns to no entity: a TRAILING
// /MM (maritime mobile) or /AM (aeronautical mobile).
//
// Trailing only. A LEADING "MM" is the Scotland prefix and "AM" is Spain, so
// MM0ABC and AM5X are ordinary calls — treating those as entity-less would be a
// far larger error than the one this fixes.
func IsMobileNoEntity(callsign string) bool {
s := strings.ToUpper(strings.TrimSpace(callsign))
i := strings.LastIndex(s, "/")
if i < 0 {
return false
}
switch s[i+1:] {
case "MM", "AM":
return true
}
return false
}
+338 -338
View File
@@ -4,348 +4,348 @@ package dxcc
// numbers. Generated by joining cty.dat to the authoritative ARRL/ADIF entity // numbers. Generated by joining cty.dat to the authoritative ARRL/ADIF entity
// list (k0swe/dxcc-json) by primary prefix + canonical name. 344 entities. // list (k0swe/dxcc-json) by primary prefix + canonical name. 344 entities.
var dxccByName = map[string]int{ var dxccByName = map[string]int{
"afghanistan": 3, "afghanistan": 3,
"agalega & st. brandon": 4, "agalega & st. brandon": 4,
"aland islands": 5, "aland islands": 5,
"alaska": 6, "alaska": 6,
"albania": 7, "albania": 7,
"algeria": 400, "algeria": 400,
"american samoa": 9, "american samoa": 9,
"amsterdam & st. paul is.": 10, "amsterdam & st. paul is.": 10,
"andaman & nicobar is.": 11, "andaman & nicobar is.": 11,
"andorra": 203, "andorra": 203,
"angola": 401, "angola": 401,
"anguilla": 12, "anguilla": 12,
"annobon island": 195, "annobon island": 195,
"antarctica": 13, "antarctica": 13,
"antigua & barbuda": 94, "antigua & barbuda": 94,
"argentina": 100, "argentina": 100,
"armenia": 14, "armenia": 14,
"aruba": 91, "aruba": 91,
"ascension island": 205, "ascension island": 205,
"asiatic russia": 15, "asiatic russia": 15,
"asiatic turkey": 390, "asiatic turkey": 390,
"austral islands": 508, "austral islands": 508,
"australia": 150, "australia": 150,
"austria": 206, "austria": 206,
"aves island": 17, "aves island": 17,
"azerbaijan": 18, "azerbaijan": 18,
"azores": 149, "azores": 149,
"bahamas": 60, "bahamas": 60,
"bahrain": 304, "bahrain": 304,
"baker & howland islands": 20, "baker & howland islands": 20,
"balearic islands": 21, "balearic islands": 21,
"banaba island": 490, "banaba island": 490,
"bangladesh": 305, "bangladesh": 305,
"barbados": 62, "barbados": 62,
"bear island": 259, "bear island": 259,
"belarus": 27, "belarus": 27,
"belgium": 209, "belgium": 209,
"belize": 66, "belize": 66,
"benin": 416, "benin": 416,
"bermuda": 64, "bermuda": 64,
"bhutan": 306, "bhutan": 306,
"bolivia": 104, "bolivia": 104,
"bonaire": 520, "bonaire": 520,
"bosnia-herzegovina": 501, "bosnia-herzegovina": 501,
"botswana": 402, "botswana": 402,
"bouvet": 24, "bouvet": 24,
"brazil": 108, "brazil": 108,
"british virgin islands": 65, "british virgin islands": 65,
"brunei darussalam": 345, "brunei darussalam": 345,
"bulgaria": 212, "bulgaria": 212,
"burkina faso": 480, "burkina faso": 480,
"burundi": 404, "burundi": 404,
"cambodia": 312, "cambodia": 312,
"cameroon": 406, "cameroon": 406,
"canada": 1, "canada": 1,
"canary islands": 29, "canary islands": 29,
"cape verde": 409, "cape verde": 409,
"cayman islands": 69, "cayman islands": 69,
"central african republic": 408, "central african republic": 408,
"central kiribati": 31, "central kiribati": 31,
"ceuta & melilla": 32, "ceuta & melilla": 32,
"chad": 410, "chad": 410,
"chagos islands": 33, "chagos islands": 33,
"chatham islands": 34, "chatham islands": 34,
"chesterfield islands": 512, "chesterfield islands": 512,
"chile": 112, "chile": 112,
"china": 318, "china": 318,
"christmas island": 35, "christmas island": 35,
"clipperton island": 36, "clipperton island": 36,
"cocos (keeling) islands": 38, "cocos (keeling) islands": 38,
"cocos island": 37, "cocos island": 37,
"colombia": 116, "colombia": 116,
"comoros": 411, "comoros": 411,
"conway reef": 489, "conway reef": 489,
"corsica": 214, "corsica": 214,
"costa rica": 308, "costa rica": 308,
"cote d'ivoire": 428, "cote d'ivoire": 428,
"crete": 40, "crete": 40,
"croatia": 497, "croatia": 497,
"crozet island": 41, "crozet island": 41,
"cuba": 70, "cuba": 70,
"curacao": 517, "curacao": 517,
"cyprus": 215, "cyprus": 215,
"czech republic": 503, "czech republic": 503,
"dem. rep. of the congo": 414, "dem. rep. of the congo": 414,
"denmark": 221, "denmark": 221,
"desecheo island": 43, "desecheo island": 43,
"djibouti": 382, "djibouti": 382,
"dodecanese": 45, "dodecanese": 45,
"dominica": 95, "dominica": 95,
"dominican republic": 72, "dominican republic": 72,
"dpr of korea": 344, "dpr of korea": 344,
"ducie island": 513, "ducie island": 513,
"east malaysia": 46, "east malaysia": 46,
"easter island": 47, "easter island": 47,
"eastern kiribati": 48, "eastern kiribati": 48,
"ecuador": 120, "ecuador": 120,
"egypt": 478, "egypt": 478,
"el salvador": 74, "el salvador": 74,
"england": 223, "england": 223,
"equatorial guinea": 49, "equatorial guinea": 49,
"eritrea": 51, "eritrea": 51,
"estonia": 52, "estonia": 52,
"ethiopia": 53, "ethiopia": 53,
"european russia": 54, "european russia": 54,
"european turkey": 390, "european turkey": 390,
"falkland islands": 141, "falkland islands": 141,
"faroe islands": 222, "faroe islands": 222,
"fed. rep. of germany": 230, "fed. rep. of germany": 230,
"fernando de noronha": 56, "fernando de noronha": 56,
"fiji": 176, "fiji": 176,
"finland": 224, "finland": 224,
"france": 227, "france": 227,
"franz josef land": 61, "franz josef land": 61,
"french guiana": 63, "french guiana": 63,
"french polynesia": 175, "french polynesia": 175,
"gabon": 420, "gabon": 420,
"galapagos islands": 71, "galapagos islands": 71,
"georgia": 75, "georgia": 75,
"ghana": 424, "ghana": 424,
"gibraltar": 233, "gibraltar": 233,
"glorioso islands": 99, "glorioso islands": 99,
"greece": 236, "greece": 236,
"greenland": 237, "greenland": 237,
"grenada": 77, "grenada": 77,
"guadeloupe": 79, "guadeloupe": 79,
"guam": 103, "guam": 103,
"guantanamo bay": 105, "guantanamo bay": 105,
"guatemala": 76, "guatemala": 76,
"guernsey": 106, "guernsey": 106,
"guinea": 107, "guinea": 107,
"guinea-bissau": 109, "guinea-bissau": 109,
"guyana": 129, "guyana": 129,
"haiti": 78, "haiti": 78,
"hawaii": 110, "hawaii": 110,
"heard island": 111, "heard island": 111,
"honduras": 80, "honduras": 80,
"hong kong": 321, "hong kong": 321,
"hungary": 239, "hungary": 239,
"iceland": 242, "iceland": 242,
"india": 324, "india": 324,
"indonesia": 327, "indonesia": 327,
"iran": 330, "iran": 330,
"iraq": 333, "iraq": 333,
"ireland": 245, "ireland": 245,
"isle of man": 114, "isle of man": 114,
"israel": 336, "israel": 336,
"italy": 248, "italy": 248,
"itu hq": 117, "itu hq": 117,
"jamaica": 82, "jamaica": 82,
"jan mayen": 118, "jan mayen": 118,
"japan": 339, "japan": 339,
"jersey": 122, "jersey": 122,
"johnston island": 123, "johnston island": 123,
"jordan": 342, "jordan": 342,
"juan de nova, europa": 124, "juan de nova, europa": 124,
"juan fernandez islands": 125, "juan fernandez islands": 125,
"kaliningrad": 126, "kaliningrad": 126,
"kazakhstan": 130, "kazakhstan": 130,
"kenya": 430, "kenya": 430,
"kerguelen islands": 131, "kerguelen islands": 131,
"kermadec islands": 133, "kermadec islands": 133,
"kingdom of eswatini": 468, "kingdom of eswatini": 468,
"kure island": 138, "kure island": 138,
"kuwait": 348, "kuwait": 348,
"kyrgyzstan": 135, "kyrgyzstan": 135,
"lakshadweep islands": 142, "lakshadweep islands": 142,
"laos": 143, "laos": 143,
"latvia": 145, "latvia": 145,
"lebanon": 354, "lebanon": 354,
"lesotho": 432, "lesotho": 432,
"liberia": 434, "liberia": 434,
"libya": 436, "libya": 436,
"liechtenstein": 251, "liechtenstein": 251,
"lithuania": 146, "lithuania": 146,
"lord howe island": 147, "lord howe island": 147,
"luxembourg": 254, "luxembourg": 254,
"macao": 152, "macao": 152,
"macquarie island": 153, "macquarie island": 153,
"madagascar": 438, "madagascar": 438,
"madeira islands": 256, "madeira islands": 256,
"malawi": 440, "malawi": 440,
"maldives": 159, "maldives": 159,
"mali": 442, "mali": 442,
"malpelo island": 161, "malpelo island": 161,
"malta": 257, "malta": 257,
"mariana islands": 166, "mariana islands": 166,
"market reef": 167, "market reef": 167,
"marquesas islands": 509, "marquesas islands": 509,
"marshall islands": 168, "marshall islands": 168,
"martinique": 84, "martinique": 84,
"mauritania": 444, "mauritania": 444,
"mauritius": 165, "mauritius": 165,
"mayotte": 169, "mayotte": 169,
"mellish reef": 171, "mellish reef": 171,
"mexico": 50, "mexico": 50,
"micronesia": 173, "micronesia": 173,
"midway island": 174, "midway island": 174,
"minami torishima": 177, "minami torishima": 177,
"moldova": 179, "moldova": 179,
"monaco": 260, "monaco": 260,
"mongolia": 363, "mongolia": 363,
"montenegro": 514, "montenegro": 514,
"montserrat": 96, "montserrat": 96,
"morocco": 446, "morocco": 446,
"mount athos": 180, "mount athos": 180,
"mozambique": 181, "mozambique": 181,
"myanmar": 309, "myanmar": 309,
"n.z. subantarctic is.": 16, "n.z. subantarctic is.": 16,
"namibia": 464, "namibia": 464,
"nauru": 157, "nauru": 157,
"navassa island": 182, "navassa island": 182,
"nepal": 369, "nepal": 369,
"netherlands": 263, "netherlands": 263,
"new caledonia": 162, "new caledonia": 162,
"new zealand": 170, "new zealand": 170,
"nicaragua": 86, "nicaragua": 86,
"niger": 187, "niger": 187,
"nigeria": 450, "nigeria": 450,
"niue": 188, "niue": 188,
"norfolk island": 189, "norfolk island": 189,
"north cook islands": 191, "north cook islands": 191,
"north macedonia": 502, "north macedonia": 502,
"northern ireland": 265, "northern ireland": 265,
"norway": 266, "norway": 266,
"ogasawara": 192, "ogasawara": 192,
"oman": 370, "oman": 370,
"pakistan": 372, "pakistan": 372,
"palau": 22, "palau": 22,
"palestine": 510, "palestine": 510,
"palmyra & jarvis islands": 197, "palmyra & jarvis islands": 197,
"panama": 88, "panama": 88,
"papua new guinea": 163, "papua new guinea": 163,
"paraguay": 132, "paraguay": 132,
"peru": 136, "peru": 136,
"peter 1 island": 199, "peter 1 island": 199,
"philippines": 375, "philippines": 375,
"pitcairn island": 172, "pitcairn island": 172,
"poland": 269, "poland": 269,
"portugal": 272, "portugal": 272,
"pr. edward & marion is.": 201, "pr. edward & marion is.": 201,
"pratas island": 505, "pratas island": 505,
"puerto rico": 202, "puerto rico": 202,
"qatar": 376, "qatar": 376,
"republic of korea": 137, "republic of korea": 137,
"republic of kosovo": 522, "republic of kosovo": 522,
"republic of south sudan": 521, "republic of south sudan": 521,
"republic of the congo": 412, "republic of the congo": 412,
"reunion island": 453, "reunion island": 453,
"revillagigedo": 204, "revillagigedo": 204,
"rodriguez island": 207, "rodriguez island": 207,
"romania": 275, "romania": 275,
"rotuma island": 460, "rotuma island": 460,
"rwanda": 454, "rwanda": 454,
"saba & st. eustatius": 519, "saba & st. eustatius": 519,
"sable island": 211, "sable island": 211,
"samoa": 190, "samoa": 190,
"san andres & providencia": 216, "san andres & providencia": 216,
"san felix & san ambrosio": 217, "san felix & san ambrosio": 217,
"san marino": 278, "san marino": 278,
"sao tome & principe": 219, "sao tome & principe": 219,
"sardinia": 225, "sardinia": 225,
"saudi arabia": 378, "saudi arabia": 378,
"scarborough reef": 506, "scarborough reef": 506,
"scotland": 279, "scotland": 279,
"senegal": 456, "senegal": 456,
"serbia": 296, "serbia": 296,
"seychelles": 379, "seychelles": 379,
"shetland islands": 279, "shetland islands": 279,
"sierra leone": 458, "sierra leone": 458,
"singapore": 381, "singapore": 381,
"sint maarten": 518, "sint maarten": 518,
"slovak republic": 504, "slovak republic": 504,
"slovenia": 499, "slovenia": 499,
"solomon islands": 185, "solomon islands": 185,
"somalia": 232, "somalia": 232,
"south africa": 462, "south africa": 462,
"south cook islands": 234, "south cook islands": 234,
"south georgia island": 235, "south georgia island": 235,
"south orkney islands": 238, "south orkney islands": 238,
"south sandwich islands": 240, "south sandwich islands": 240,
"south shetland islands": 241, "south shetland islands": 241,
"sov mil order of malta": 246, "sov mil order of malta": 246,
"spain": 281, "spain": 281,
"spratly islands": 247, "spratly islands": 247,
"sri lanka": 315, "sri lanka": 315,
"st. barthelemy": 516, "st. barthelemy": 516,
"st. helena": 250, "st. helena": 250,
"st. kitts & nevis": 249, "st. kitts & nevis": 249,
"st. lucia": 97, "st. lucia": 97,
"st. martin": 213, "st. martin": 213,
"st. paul island": 252, "st. paul island": 252,
"st. peter & st. paul": 253, "st. peter & st. paul": 253,
"st. pierre & miquelon": 277, "st. pierre & miquelon": 277,
"st. vincent": 98, "st. vincent": 98,
"sudan": 466, "sudan": 466,
"suriname": 140, "suriname": 140,
"svalbard": 259, "svalbard": 259,
"swains island": 515, "swains island": 515,
"sweden": 284, "sweden": 284,
"switzerland": 287, "switzerland": 287,
"syria": 384, "syria": 384,
"taiwan": 386, "taiwan": 386,
"tajikistan": 262, "tajikistan": 262,
"tanzania": 470, "tanzania": 470,
"temotu province": 507, "temotu province": 507,
"thailand": 387, "thailand": 387,
"the gambia": 422, "the gambia": 422,
"timor - leste": 511, "timor - leste": 511,
"togo": 483, "togo": 483,
"tokelau islands": 270, "tokelau islands": 270,
"tonga": 160, "tonga": 160,
"trindade & martim vaz": 273, "trindade & martim vaz": 273,
"trinidad & tobago": 90, "trinidad & tobago": 90,
"tristan da cunha & gough": 274, "tristan da cunha & gough": 274,
"tromelin island": 276, "tromelin island": 276,
"tunisia": 474, "tunisia": 474,
"turkmenistan": 280, "turkmenistan": 280,
"turks & caicos islands": 89, "turks & caicos islands": 89,
"tuvalu": 282, "tuvalu": 282,
"uganda": 286, "uganda": 286,
"uk base areas on cyprus": 283, "uk base areas on cyprus": 283,
"ukraine": 288, "ukraine": 288,
"united arab emirates": 391, "united arab emirates": 391,
"united nations hq": 289, "united nations hq": 289,
"united states": 291, "united states": 291,
"uruguay": 144, "uruguay": 144,
"us virgin islands": 285, "us virgin islands": 285,
"uzbekistan": 292, "uzbekistan": 292,
"vanuatu": 158, "vanuatu": 158,
"vatican city": 295, "vatican city": 295,
"venezuela": 148, "venezuela": 148,
"vienna intl ctr": 206, "vienna intl ctr": 206,
"vietnam": 293, "vietnam": 293,
"wake island": 297, "wake island": 297,
"wales": 294, "wales": 294,
"wallis & futuna islands": 298, "wallis & futuna islands": 298,
"west malaysia": 299, // hand-fixed: generation joined it to 155 by mistake (9M2 = ADIF 299) "west malaysia": 299, // hand-fixed: generation joined it to 155 by mistake (9M2 = ADIF 299)
"western kiribati": 301, "western kiribati": 301,
"western sahara": 302, "western sahara": 302,
"willis island": 303, "willis island": 303,
"yemen": 492, "yemen": 492,
"zambia": 482, "zambia": 482,
"zimbabwe": 452, "zimbabwe": 452,
} }
+21 -21
View File
@@ -178,9 +178,9 @@ Sicily: 15: 28: EU: 37.50: -14.00: -1.0: *IT9:
t.Fatalf("load: %v", err) t.Fatalf("load: %v", err)
} }
cases := map[string]string{ cases := map[string]string{
"IW9EZO": "Italy", // Sicily (*IT9) → Italy "IW9EZO": "Italy", // Sicily (*IT9) → Italy
"IT9CLY": "Italy", "IT9CLY": "Italy",
"IG9A": "Italy", // African Italy (*IG9) → Italy "IG9A": "Italy", // African Italy (*IG9) → Italy
"IK0ABC": "Italy", "IK0ABC": "Italy",
"IS0XYZ": "Sardinia", // real DXCC entity — must stay Sardinia "IS0XYZ": "Sardinia", // real DXCC entity — must stay Sardinia
"IM0ABC": "Sardinia", "IM0ABC": "Sardinia",
@@ -210,25 +210,25 @@ Sicily: 15: 28: EU: 37.50: -14.00: -1.0: *IT9:
func TestNormalize(t *testing.T) { func TestNormalize(t *testing.T) {
cases := map[string]string{ cases := map[string]string{
"F4BPO": "F4BPO", "F4BPO": "F4BPO",
"f4bpo": "F4BPO", "f4bpo": "F4BPO",
" F4BPO ": "F4BPO", " F4BPO ": "F4BPO",
"F4BPO/P": "F4BPO", "F4BPO/P": "F4BPO",
"F4BPO/MM": "F4BPO", // maritime mobile → strip, keep home entity for the log "F4BPO/MM": "F4BPO", // maritime mobile → strip, keep home entity for the log
"F4BPO/AM": "F4BPO", // aeronautical mobile → strip, keep home entity for the log "F4BPO/AM": "F4BPO", // aeronautical mobile → strip, keep home entity for the log
"F4BPO/M": "F4BPO", // plain mobile keeps the home entity "F4BPO/M": "F4BPO", // plain mobile keeps the home entity
"F4BPO/5": "F5BPO", // "/5" re-homes to call area 5 "F4BPO/5": "F5BPO", // "/5" re-homes to call area 5
"HD5MW/8": "HD8MW", // "/8" → Galápagos call area (HD8) "HD5MW/8": "HD8MW", // "/8" → Galápagos call area (HD8)
"7X2ARA/4": "7X4ARA", // "/4" changes the AREA digit (2→4), NOT the 7 of the 7X prefix (was wrongly 4X2ARA = Israel) "7X2ARA/4": "7X4ARA", // "/4" changes the AREA digit (2→4), NOT the 7 of the 7X prefix (was wrongly 4X2ARA = Israel)
"3A2ABC/6": "3A6ABC", // digit-first prefix 3A: area digit is the 2, not the 3 "3A2ABC/6": "3A6ABC", // digit-first prefix 3A: area digit is the 2, not the 3
"4X1AB/2": "4X2AB", // 4X (Israel) area 1→2, keep the leading 4 "4X1AB/2": "4X2AB", // 4X (Israel) area 1→2, keep the leading 4
"DL/F4BPO": "DL", "DL/F4BPO": "DL",
"MM/KA9P": "MM", // leading MM = Scotland operating prefix "MM/KA9P": "MM", // leading MM = Scotland operating prefix
"MM/LY3X/P": "MM", "MM/LY3X/P": "MM",
"F4BPO/W6": "W6", "F4BPO/W6": "W6",
"VK9/F4BPO": "VK9", "VK9/F4BPO": "VK9",
"4U1UN/B": "4U1UN", // trailing /B = beacon → strip, keep the base call "4U1UN/B": "4U1UN", // trailing /B = beacon → strip, keep the base call
"B/F4BPO": "B", // leading B = China operating prefix, NOT a beacon "B/F4BPO": "B", // leading B = China operating prefix, NOT a beacon
} }
for in, want := range cases { for in, want := range cases {
if got := normalizeCallsign(in); got != want { if got := normalizeCallsign(in); got != want {
+39
View File
@@ -0,0 +1,39 @@
package dxcc
import "testing"
// /MM and /AM belong to NO DXCC entity: the station is at sea or in the air.
//
// This is not a technicality. RI1FJL/MM resolved to Franz Josef Land while the
// expedition was still sailing there, which told the operator they had worked
// an entity they had not.
func TestMobileNoEntity(t *testing.T) {
for _, tc := range []struct {
call string
want bool
}{
{"RI1FJL/MM", true},
{"YB1SCY/AM", true},
{"ri1fjl/mm", true},
{" RI1FJL/MM ", true},
// A LEADING MM is Scotland and AM is Spain. Treating those as
// entity-less would be a far larger error than the one this fixes.
{"MM0ABC", false},
{"AM5X", false},
{"MM/F4BPO", false},
{"AM/DL1ABC", false},
// Other suffixes keep their entity — a portable station is still ashore.
{"F4BPO/P", false},
{"F4BPO/M", false},
{"4U1UN/B", false},
{"F4BPO", false},
// Only the LAST element counts: RI1FJL/MM/P is still maritime, and a
// call whose /MM is not final is not.
{"RI1FJL/MM/P", false},
{"", false},
} {
if got := IsMobileNoEntity(tc.call); got != tc.want {
t.Errorf("IsMobileNoEntity(%q) = %v, want %v", tc.call, got, tc.want)
}
}
}