diff --git a/app.go b/app.go index 944508a..6c8d7aa 100644 --- a/app.go +++ b/app.go @@ -17452,6 +17452,16 @@ func (a *App) ClusterSpotStatuses(spots []SpotQuery) []SpotStatus { if a.dxcc == nil { 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) if !ok || m.Entity == nil { continue diff --git a/changelog.json b/changelog.json index 7fb15f0..f320d0f 100644 --- a/changelog.json +++ b/changelog.json @@ -10,7 +10,8 @@ "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.", "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": [ "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.", "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.", - "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." ] }, { diff --git a/internal/dxcc/dxcc.go b/internal/dxcc/dxcc.go index d54802f..184368c 100644 --- a/internal/dxcc/dxcc.go +++ b/internal/dxcc/dxcc.go @@ -132,6 +132,18 @@ func (db *DB) Entities() []*Entity { func (db *DB) Lookup(callsign string) (Match, bool) { db.mu.RLock() 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) if call == "" { return Match{}, false @@ -364,3 +376,22 @@ func replaceAreaDigit(call string, d byte) string { } 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 +} diff --git a/internal/dxcc/dxcc_names_gen.go b/internal/dxcc/dxcc_names_gen.go index 836174b..56c1ee8 100644 --- a/internal/dxcc/dxcc_names_gen.go +++ b/internal/dxcc/dxcc_names_gen.go @@ -4,348 +4,348 @@ package dxcc // numbers. Generated by joining cty.dat to the authoritative ARRL/ADIF entity // list (k0swe/dxcc-json) by primary prefix + canonical name. 344 entities. var dxccByName = map[string]int{ - "afghanistan": 3, - "agalega & st. brandon": 4, - "aland islands": 5, - "alaska": 6, - "albania": 7, - "algeria": 400, - "american samoa": 9, + "afghanistan": 3, + "agalega & st. brandon": 4, + "aland islands": 5, + "alaska": 6, + "albania": 7, + "algeria": 400, + "american samoa": 9, "amsterdam & st. paul is.": 10, - "andaman & nicobar is.": 11, - "andorra": 203, - "angola": 401, - "anguilla": 12, - "annobon island": 195, - "antarctica": 13, - "antigua & barbuda": 94, - "argentina": 100, - "armenia": 14, - "aruba": 91, - "ascension island": 205, - "asiatic russia": 15, - "asiatic turkey": 390, - "austral islands": 508, - "australia": 150, - "austria": 206, - "aves island": 17, - "azerbaijan": 18, - "azores": 149, - "bahamas": 60, - "bahrain": 304, - "baker & howland islands": 20, - "balearic islands": 21, - "banaba island": 490, - "bangladesh": 305, - "barbados": 62, - "bear island": 259, - "belarus": 27, - "belgium": 209, - "belize": 66, - "benin": 416, - "bermuda": 64, - "bhutan": 306, - "bolivia": 104, - "bonaire": 520, - "bosnia-herzegovina": 501, - "botswana": 402, - "bouvet": 24, - "brazil": 108, - "british virgin islands": 65, - "brunei darussalam": 345, - "bulgaria": 212, - "burkina faso": 480, - "burundi": 404, - "cambodia": 312, - "cameroon": 406, - "canada": 1, - "canary islands": 29, - "cape verde": 409, - "cayman islands": 69, + "andaman & nicobar is.": 11, + "andorra": 203, + "angola": 401, + "anguilla": 12, + "annobon island": 195, + "antarctica": 13, + "antigua & barbuda": 94, + "argentina": 100, + "armenia": 14, + "aruba": 91, + "ascension island": 205, + "asiatic russia": 15, + "asiatic turkey": 390, + "austral islands": 508, + "australia": 150, + "austria": 206, + "aves island": 17, + "azerbaijan": 18, + "azores": 149, + "bahamas": 60, + "bahrain": 304, + "baker & howland islands": 20, + "balearic islands": 21, + "banaba island": 490, + "bangladesh": 305, + "barbados": 62, + "bear island": 259, + "belarus": 27, + "belgium": 209, + "belize": 66, + "benin": 416, + "bermuda": 64, + "bhutan": 306, + "bolivia": 104, + "bonaire": 520, + "bosnia-herzegovina": 501, + "botswana": 402, + "bouvet": 24, + "brazil": 108, + "british virgin islands": 65, + "brunei darussalam": 345, + "bulgaria": 212, + "burkina faso": 480, + "burundi": 404, + "cambodia": 312, + "cameroon": 406, + "canada": 1, + "canary islands": 29, + "cape verde": 409, + "cayman islands": 69, "central african republic": 408, - "central kiribati": 31, - "ceuta & melilla": 32, - "chad": 410, - "chagos islands": 33, - "chatham islands": 34, - "chesterfield islands": 512, - "chile": 112, - "china": 318, - "christmas island": 35, - "clipperton island": 36, - "cocos (keeling) islands": 38, - "cocos island": 37, - "colombia": 116, - "comoros": 411, - "conway reef": 489, - "corsica": 214, - "costa rica": 308, - "cote d'ivoire": 428, - "crete": 40, - "croatia": 497, - "crozet island": 41, - "cuba": 70, - "curacao": 517, - "cyprus": 215, - "czech republic": 503, - "dem. rep. of the congo": 414, - "denmark": 221, - "desecheo island": 43, - "djibouti": 382, - "dodecanese": 45, - "dominica": 95, - "dominican republic": 72, - "dpr of korea": 344, - "ducie island": 513, - "east malaysia": 46, - "easter island": 47, - "eastern kiribati": 48, - "ecuador": 120, - "egypt": 478, - "el salvador": 74, - "england": 223, - "equatorial guinea": 49, - "eritrea": 51, - "estonia": 52, - "ethiopia": 53, - "european russia": 54, - "european turkey": 390, - "falkland islands": 141, - "faroe islands": 222, - "fed. rep. of germany": 230, - "fernando de noronha": 56, - "fiji": 176, - "finland": 224, - "france": 227, - "franz josef land": 61, - "french guiana": 63, - "french polynesia": 175, - "gabon": 420, - "galapagos islands": 71, - "georgia": 75, - "ghana": 424, - "gibraltar": 233, - "glorioso islands": 99, - "greece": 236, - "greenland": 237, - "grenada": 77, - "guadeloupe": 79, - "guam": 103, - "guantanamo bay": 105, - "guatemala": 76, - "guernsey": 106, - "guinea": 107, - "guinea-bissau": 109, - "guyana": 129, - "haiti": 78, - "hawaii": 110, - "heard island": 111, - "honduras": 80, - "hong kong": 321, - "hungary": 239, - "iceland": 242, - "india": 324, - "indonesia": 327, - "iran": 330, - "iraq": 333, - "ireland": 245, - "isle of man": 114, - "israel": 336, - "italy": 248, - "itu hq": 117, - "jamaica": 82, - "jan mayen": 118, - "japan": 339, - "jersey": 122, - "johnston island": 123, - "jordan": 342, - "juan de nova, europa": 124, - "juan fernandez islands": 125, - "kaliningrad": 126, - "kazakhstan": 130, - "kenya": 430, - "kerguelen islands": 131, - "kermadec islands": 133, - "kingdom of eswatini": 468, - "kure island": 138, - "kuwait": 348, - "kyrgyzstan": 135, - "lakshadweep islands": 142, - "laos": 143, - "latvia": 145, - "lebanon": 354, - "lesotho": 432, - "liberia": 434, - "libya": 436, - "liechtenstein": 251, - "lithuania": 146, - "lord howe island": 147, - "luxembourg": 254, - "macao": 152, - "macquarie island": 153, - "madagascar": 438, - "madeira islands": 256, - "malawi": 440, - "maldives": 159, - "mali": 442, - "malpelo island": 161, - "malta": 257, - "mariana islands": 166, - "market reef": 167, - "marquesas islands": 509, - "marshall islands": 168, - "martinique": 84, - "mauritania": 444, - "mauritius": 165, - "mayotte": 169, - "mellish reef": 171, - "mexico": 50, - "micronesia": 173, - "midway island": 174, - "minami torishima": 177, - "moldova": 179, - "monaco": 260, - "mongolia": 363, - "montenegro": 514, - "montserrat": 96, - "morocco": 446, - "mount athos": 180, - "mozambique": 181, - "myanmar": 309, - "n.z. subantarctic is.": 16, - "namibia": 464, - "nauru": 157, - "navassa island": 182, - "nepal": 369, - "netherlands": 263, - "new caledonia": 162, - "new zealand": 170, - "nicaragua": 86, - "niger": 187, - "nigeria": 450, - "niue": 188, - "norfolk island": 189, - "north cook islands": 191, - "north macedonia": 502, - "northern ireland": 265, - "norway": 266, - "ogasawara": 192, - "oman": 370, - "pakistan": 372, - "palau": 22, - "palestine": 510, + "central kiribati": 31, + "ceuta & melilla": 32, + "chad": 410, + "chagos islands": 33, + "chatham islands": 34, + "chesterfield islands": 512, + "chile": 112, + "china": 318, + "christmas island": 35, + "clipperton island": 36, + "cocos (keeling) islands": 38, + "cocos island": 37, + "colombia": 116, + "comoros": 411, + "conway reef": 489, + "corsica": 214, + "costa rica": 308, + "cote d'ivoire": 428, + "crete": 40, + "croatia": 497, + "crozet island": 41, + "cuba": 70, + "curacao": 517, + "cyprus": 215, + "czech republic": 503, + "dem. rep. of the congo": 414, + "denmark": 221, + "desecheo island": 43, + "djibouti": 382, + "dodecanese": 45, + "dominica": 95, + "dominican republic": 72, + "dpr of korea": 344, + "ducie island": 513, + "east malaysia": 46, + "easter island": 47, + "eastern kiribati": 48, + "ecuador": 120, + "egypt": 478, + "el salvador": 74, + "england": 223, + "equatorial guinea": 49, + "eritrea": 51, + "estonia": 52, + "ethiopia": 53, + "european russia": 54, + "european turkey": 390, + "falkland islands": 141, + "faroe islands": 222, + "fed. rep. of germany": 230, + "fernando de noronha": 56, + "fiji": 176, + "finland": 224, + "france": 227, + "franz josef land": 61, + "french guiana": 63, + "french polynesia": 175, + "gabon": 420, + "galapagos islands": 71, + "georgia": 75, + "ghana": 424, + "gibraltar": 233, + "glorioso islands": 99, + "greece": 236, + "greenland": 237, + "grenada": 77, + "guadeloupe": 79, + "guam": 103, + "guantanamo bay": 105, + "guatemala": 76, + "guernsey": 106, + "guinea": 107, + "guinea-bissau": 109, + "guyana": 129, + "haiti": 78, + "hawaii": 110, + "heard island": 111, + "honduras": 80, + "hong kong": 321, + "hungary": 239, + "iceland": 242, + "india": 324, + "indonesia": 327, + "iran": 330, + "iraq": 333, + "ireland": 245, + "isle of man": 114, + "israel": 336, + "italy": 248, + "itu hq": 117, + "jamaica": 82, + "jan mayen": 118, + "japan": 339, + "jersey": 122, + "johnston island": 123, + "jordan": 342, + "juan de nova, europa": 124, + "juan fernandez islands": 125, + "kaliningrad": 126, + "kazakhstan": 130, + "kenya": 430, + "kerguelen islands": 131, + "kermadec islands": 133, + "kingdom of eswatini": 468, + "kure island": 138, + "kuwait": 348, + "kyrgyzstan": 135, + "lakshadweep islands": 142, + "laos": 143, + "latvia": 145, + "lebanon": 354, + "lesotho": 432, + "liberia": 434, + "libya": 436, + "liechtenstein": 251, + "lithuania": 146, + "lord howe island": 147, + "luxembourg": 254, + "macao": 152, + "macquarie island": 153, + "madagascar": 438, + "madeira islands": 256, + "malawi": 440, + "maldives": 159, + "mali": 442, + "malpelo island": 161, + "malta": 257, + "mariana islands": 166, + "market reef": 167, + "marquesas islands": 509, + "marshall islands": 168, + "martinique": 84, + "mauritania": 444, + "mauritius": 165, + "mayotte": 169, + "mellish reef": 171, + "mexico": 50, + "micronesia": 173, + "midway island": 174, + "minami torishima": 177, + "moldova": 179, + "monaco": 260, + "mongolia": 363, + "montenegro": 514, + "montserrat": 96, + "morocco": 446, + "mount athos": 180, + "mozambique": 181, + "myanmar": 309, + "n.z. subantarctic is.": 16, + "namibia": 464, + "nauru": 157, + "navassa island": 182, + "nepal": 369, + "netherlands": 263, + "new caledonia": 162, + "new zealand": 170, + "nicaragua": 86, + "niger": 187, + "nigeria": 450, + "niue": 188, + "norfolk island": 189, + "north cook islands": 191, + "north macedonia": 502, + "northern ireland": 265, + "norway": 266, + "ogasawara": 192, + "oman": 370, + "pakistan": 372, + "palau": 22, + "palestine": 510, "palmyra & jarvis islands": 197, - "panama": 88, - "papua new guinea": 163, - "paraguay": 132, - "peru": 136, - "peter 1 island": 199, - "philippines": 375, - "pitcairn island": 172, - "poland": 269, - "portugal": 272, - "pr. edward & marion is.": 201, - "pratas island": 505, - "puerto rico": 202, - "qatar": 376, - "republic of korea": 137, - "republic of kosovo": 522, - "republic of south sudan": 521, - "republic of the congo": 412, - "reunion island": 453, - "revillagigedo": 204, - "rodriguez island": 207, - "romania": 275, - "rotuma island": 460, - "rwanda": 454, - "saba & st. eustatius": 519, - "sable island": 211, - "samoa": 190, + "panama": 88, + "papua new guinea": 163, + "paraguay": 132, + "peru": 136, + "peter 1 island": 199, + "philippines": 375, + "pitcairn island": 172, + "poland": 269, + "portugal": 272, + "pr. edward & marion is.": 201, + "pratas island": 505, + "puerto rico": 202, + "qatar": 376, + "republic of korea": 137, + "republic of kosovo": 522, + "republic of south sudan": 521, + "republic of the congo": 412, + "reunion island": 453, + "revillagigedo": 204, + "rodriguez island": 207, + "romania": 275, + "rotuma island": 460, + "rwanda": 454, + "saba & st. eustatius": 519, + "sable island": 211, + "samoa": 190, "san andres & providencia": 216, "san felix & san ambrosio": 217, - "san marino": 278, - "sao tome & principe": 219, - "sardinia": 225, - "saudi arabia": 378, - "scarborough reef": 506, - "scotland": 279, - "senegal": 456, - "serbia": 296, - "seychelles": 379, - "shetland islands": 279, - "sierra leone": 458, - "singapore": 381, - "sint maarten": 518, - "slovak republic": 504, - "slovenia": 499, - "solomon islands": 185, - "somalia": 232, - "south africa": 462, - "south cook islands": 234, - "south georgia island": 235, - "south orkney islands": 238, - "south sandwich islands": 240, - "south shetland islands": 241, - "sov mil order of malta": 246, - "spain": 281, - "spratly islands": 247, - "sri lanka": 315, - "st. barthelemy": 516, - "st. helena": 250, - "st. kitts & nevis": 249, - "st. lucia": 97, - "st. martin": 213, - "st. paul island": 252, - "st. peter & st. paul": 253, - "st. pierre & miquelon": 277, - "st. vincent": 98, - "sudan": 466, - "suriname": 140, - "svalbard": 259, - "swains island": 515, - "sweden": 284, - "switzerland": 287, - "syria": 384, - "taiwan": 386, - "tajikistan": 262, - "tanzania": 470, - "temotu province": 507, - "thailand": 387, - "the gambia": 422, - "timor - leste": 511, - "togo": 483, - "tokelau islands": 270, - "tonga": 160, - "trindade & martim vaz": 273, - "trinidad & tobago": 90, + "san marino": 278, + "sao tome & principe": 219, + "sardinia": 225, + "saudi arabia": 378, + "scarborough reef": 506, + "scotland": 279, + "senegal": 456, + "serbia": 296, + "seychelles": 379, + "shetland islands": 279, + "sierra leone": 458, + "singapore": 381, + "sint maarten": 518, + "slovak republic": 504, + "slovenia": 499, + "solomon islands": 185, + "somalia": 232, + "south africa": 462, + "south cook islands": 234, + "south georgia island": 235, + "south orkney islands": 238, + "south sandwich islands": 240, + "south shetland islands": 241, + "sov mil order of malta": 246, + "spain": 281, + "spratly islands": 247, + "sri lanka": 315, + "st. barthelemy": 516, + "st. helena": 250, + "st. kitts & nevis": 249, + "st. lucia": 97, + "st. martin": 213, + "st. paul island": 252, + "st. peter & st. paul": 253, + "st. pierre & miquelon": 277, + "st. vincent": 98, + "sudan": 466, + "suriname": 140, + "svalbard": 259, + "swains island": 515, + "sweden": 284, + "switzerland": 287, + "syria": 384, + "taiwan": 386, + "tajikistan": 262, + "tanzania": 470, + "temotu province": 507, + "thailand": 387, + "the gambia": 422, + "timor - leste": 511, + "togo": 483, + "tokelau islands": 270, + "tonga": 160, + "trindade & martim vaz": 273, + "trinidad & tobago": 90, "tristan da cunha & gough": 274, - "tromelin island": 276, - "tunisia": 474, - "turkmenistan": 280, - "turks & caicos islands": 89, - "tuvalu": 282, - "uganda": 286, - "uk base areas on cyprus": 283, - "ukraine": 288, - "united arab emirates": 391, - "united nations hq": 289, - "united states": 291, - "uruguay": 144, - "us virgin islands": 285, - "uzbekistan": 292, - "vanuatu": 158, - "vatican city": 295, - "venezuela": 148, - "vienna intl ctr": 206, - "vietnam": 293, - "wake island": 297, - "wales": 294, - "wallis & futuna islands": 298, - "west malaysia": 299, // hand-fixed: generation joined it to 155 by mistake (9M2 = ADIF 299) - "western kiribati": 301, - "western sahara": 302, - "willis island": 303, - "yemen": 492, - "zambia": 482, - "zimbabwe": 452, + "tromelin island": 276, + "tunisia": 474, + "turkmenistan": 280, + "turks & caicos islands": 89, + "tuvalu": 282, + "uganda": 286, + "uk base areas on cyprus": 283, + "ukraine": 288, + "united arab emirates": 391, + "united nations hq": 289, + "united states": 291, + "uruguay": 144, + "us virgin islands": 285, + "uzbekistan": 292, + "vanuatu": 158, + "vatican city": 295, + "venezuela": 148, + "vienna intl ctr": 206, + "vietnam": 293, + "wake island": 297, + "wales": 294, + "wallis & futuna islands": 298, + "west malaysia": 299, // hand-fixed: generation joined it to 155 by mistake (9M2 = ADIF 299) + "western kiribati": 301, + "western sahara": 302, + "willis island": 303, + "yemen": 492, + "zambia": 482, + "zimbabwe": 452, } diff --git a/internal/dxcc/dxcc_test.go b/internal/dxcc/dxcc_test.go index 4935f9f..0b1f69d 100644 --- a/internal/dxcc/dxcc_test.go +++ b/internal/dxcc/dxcc_test.go @@ -178,9 +178,9 @@ Sicily: 15: 28: EU: 37.50: -14.00: -1.0: *IT9: t.Fatalf("load: %v", err) } cases := map[string]string{ - "IW9EZO": "Italy", // Sicily (*IT9) → Italy + "IW9EZO": "Italy", // Sicily (*IT9) → Italy "IT9CLY": "Italy", - "IG9A": "Italy", // African Italy (*IG9) → Italy + "IG9A": "Italy", // African Italy (*IG9) → Italy "IK0ABC": "Italy", "IS0XYZ": "Sardinia", // real DXCC entity — must stay Sardinia "IM0ABC": "Sardinia", @@ -210,25 +210,25 @@ Sicily: 15: 28: EU: 37.50: -14.00: -1.0: *IT9: func TestNormalize(t *testing.T) { cases := map[string]string{ - "F4BPO": "F4BPO", - "f4bpo": "F4BPO", - " F4BPO ": "F4BPO", - "F4BPO/P": "F4BPO", - "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/M": "F4BPO", // plain mobile keeps the home entity - "F4BPO/5": "F5BPO", // "/5" re-homes to call area 5 - "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) - "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 - "DL/F4BPO": "DL", - "MM/KA9P": "MM", // leading MM = Scotland operating prefix - "MM/LY3X/P": "MM", - "F4BPO/W6": "W6", - "VK9/F4BPO": "VK9", - "4U1UN/B": "4U1UN", // trailing /B = beacon → strip, keep the base call - "B/F4BPO": "B", // leading B = China operating prefix, NOT a beacon + "F4BPO": "F4BPO", + "f4bpo": "F4BPO", + " F4BPO ": "F4BPO", + "F4BPO/P": "F4BPO", + "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/M": "F4BPO", // plain mobile keeps the home entity + "F4BPO/5": "F5BPO", // "/5" re-homes to call area 5 + "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) + "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 + "DL/F4BPO": "DL", + "MM/KA9P": "MM", // leading MM = Scotland operating prefix + "MM/LY3X/P": "MM", + "F4BPO/W6": "W6", + "VK9/F4BPO": "VK9", + "4U1UN/B": "4U1UN", // trailing /B = beacon → strip, keep the base call + "B/F4BPO": "B", // leading B = China operating prefix, NOT a beacon } for in, want := range cases { if got := normalizeCallsign(in); got != want { diff --git a/internal/dxcc/maritime_test.go b/internal/dxcc/maritime_test.go new file mode 100644 index 0000000..f130287 --- /dev/null +++ b/internal/dxcc/maritime_test.go @@ -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) + } + } +}