fix(import): a deleted DXCC entity is not re-stamped from cty.dat
Reported with a Logger32 export: the record said <DXCC:3>151 and OpsLog stored 54. cty.dat answers one question — where is this callsign TODAY — and that is the wrong question for a log. R1MVI was Malyj Vysotskij Island, entity 151, until the ARRL deleted it in 2012; the same call resolves to European Russia today. Forcing the lookup onto a 2004 contact therefore destroys a credit that can NEVER be worked again, because the place no longer counts. So the ADIF's number wins whenever it names a deleted entity, and nothing is corrected on those records at all — the country name and the zones of a deleted entity are equally beyond what a present-day prefix table knows. The list is the ADIF specification's own enumeration, sixty-odd entities that only change when the ARRL deletes another one. The guard sits in the forced path, so it also covers the grid's 'Update from cty.dat' on a selection — the other way an operator could have lost the same credits, one right-click at a time.
This commit is contained in:
@@ -12739,6 +12739,21 @@ func (a *App) enrichContactedFromCtyForce(q *qso.QSO) bool {
|
|||||||
if a.dxcc == nil || q.Callsign == "" {
|
if a.dxcc == nil || q.Callsign == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
// A DELETED ENTITY IS LEFT ALONE, whatever cty.dat says about the callsign
|
||||||
|
// today.
|
||||||
|
//
|
||||||
|
// cty.dat answers "where is this call now". For a contact made before an
|
||||||
|
// entity was deleted that is the wrong question: R1MVI was Malyj Vysotskij
|
||||||
|
// (151) until 2012 and resolves to European Russia (54) today, so forcing
|
||||||
|
// the lookup onto a 2004 QSO destroys a credit that can never be worked
|
||||||
|
// again — the place does not exist to be worked. Reported from a Logger32
|
||||||
|
// export whose <DXCC:3>151 came back as 54.
|
||||||
|
//
|
||||||
|
// Nothing is corrected here rather than only the number: the country name
|
||||||
|
// and the zones of a deleted entity are equally beyond cty.dat's knowledge.
|
||||||
|
if q.DXCC != nil && dxcc.IsDeleted(*q.DXCC) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
m, ok := a.dxcc.Lookup(q.Callsign)
|
m, ok := a.dxcc.Lookup(q.Callsign)
|
||||||
if !ok || m.Entity == nil {
|
if !ok || m.Entity == nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
+4
-2
@@ -12,7 +12,8 @@
|
|||||||
"The Elecraft console can be docked as a main-view pane. It always could; the setting simply never listed it.",
|
"The Elecraft console can be docked as a main-view pane. It always could; the setting simply never listed it.",
|
||||||
"Spot alerts name the right mode. A 30 m FT8 spot raised an alert saying SSB while the cluster list beside it said DATA: the alert engine had its own, coarser band plan, which called everything above 10.130 phone. Both now read the same table, and a frequency the plan does not cover is left unnamed rather than guessed at.",
|
"Spot alerts name the right mode. A 30 m FT8 spot raised an alert saying SSB while the cluster list beside it said DATA: the alert engine had its own, coarser band plan, which called everything above 10.130 phone. Both now read the same table, and a frequency the plan does not cover is left unnamed rather than guessed at.",
|
||||||
"Awards: editing an award now refreshes its statistics as well as its grid. Changing which confirmations count — LoTW only, LoTW plus cards — left the whole matrix showing the previous rule's numbers, with nothing to say they were stale.",
|
"Awards: editing an award now refreshes its statistics as well as its grid. Changing which confirmations count — LoTW only, LoTW plus cards — left the whole matrix showing the previous rule's numbers, with nothing to say they were stale.",
|
||||||
"The DXCC statistics show the Challenge, and say what the two totals mean. The Challenge counts confirmed band-slots on ten bands and 60 m is not one of them — which is the whole of the gap against LoTW's figure. The Total column is entities, the last column is band-slots, and both now say so when hovered."
|
"The DXCC statistics show the Challenge, and say what the two totals mean. The Challenge counts confirmed band-slots on ten bands and 60 m is not one of them — which is the whole of the gap against LoTW's figure. The Total column is entities, the last column is band-slots, and both now say so when hovered.",
|
||||||
|
"Deleted DXCC entities survive an import. A QSO carrying an entity the ARRL has since deleted — Malyj Vysotskij, Czechoslovakia, the German DR and the rest — was being re-stamped from cty.dat, which only knows where a callsign is TODAY: a 2004 R1MVI came back as European Russia and the credit was gone, for a place that can never be worked again. The number in the file is now left alone for those entities."
|
||||||
],
|
],
|
||||||
"fr": [
|
"fr": [
|
||||||
"Diplômes : la matrice des bandes affiche les bandes sur lesquelles on a des contacts — pas davantage, et sans s'arrêter au 70cm. Elle listait toutes les bandes autorisées par le diplôme, si bien que le DDFM restait avec des colonnes 6m et 70cm vides, et ne pouvait pas afficher le 23cm quoi que dise le diplôme.",
|
"Diplômes : la matrice des bandes affiche les bandes sur lesquelles on a des contacts — pas davantage, et sans s'arrêter au 70cm. Elle listait toutes les bandes autorisées par le diplôme, si bien que le DDFM restait avec des colonnes 6m et 70cm vides, et ne pouvait pas afficher le 23cm quoi que dise le diplôme.",
|
||||||
@@ -24,7 +25,8 @@
|
|||||||
"La console Elecraft peut être ancrée comme volet de la vue principale. Elle le pouvait depuis toujours ; le réglage ne la proposait tout simplement pas.",
|
"La console Elecraft peut être ancrée comme volet de la vue principale. Elle le pouvait depuis toujours ; le réglage ne la proposait tout simplement pas.",
|
||||||
"Les alertes de spot nomment le bon mode. Un spot FT8 sur 30 m déclenchait une alerte annonçant SSB alors que la liste du cluster juste à côté affichait DATA : le moteur d'alertes avait son propre plan de bandes, plus grossier, qui appelait « phonie » tout ce qui dépassait 10,130. Les deux lisent désormais la même table, et une fréquence hors plan reste sans mode plutôt que devinée.",
|
"Les alertes de spot nomment le bon mode. Un spot FT8 sur 30 m déclenchait une alerte annonçant SSB alors que la liste du cluster juste à côté affichait DATA : le moteur d'alertes avait son propre plan de bandes, plus grossier, qui appelait « phonie » tout ce qui dépassait 10,130. Les deux lisent désormais la même table, et une fréquence hors plan reste sans mode plutôt que devinée.",
|
||||||
"Diplômes : modifier un diplôme actualise désormais ses statistiques et pas seulement sa grille. Changer les confirmations qui comptent — LoTW seul, LoTW plus cartes — laissait toute la matrice afficher les chiffres de la règle précédente, sans rien pour dire qu'ils étaient périmés.",
|
"Diplômes : modifier un diplôme actualise désormais ses statistiques et pas seulement sa grille. Changer les confirmations qui comptent — LoTW seul, LoTW plus cartes — laissait toute la matrice afficher les chiffres de la règle précédente, sans rien pour dire qu'ils étaient périmés.",
|
||||||
"Les statistiques DXCC affichent le Challenge et disent ce que sont les deux totaux. Le Challenge compte les créneaux bande confirmés sur dix bandes, dont le 60 m ne fait pas partie — c'est là tout l'écart avec le chiffre de LoTW. La colonne Total, ce sont les entités ; la dernière, les créneaux bande ; les deux le disent au survol."
|
"Les statistiques DXCC affichent le Challenge et disent ce que sont les deux totaux. Le Challenge compte les créneaux bande confirmés sur dix bandes, dont le 60 m ne fait pas partie — c'est là tout l'écart avec le chiffre de LoTW. La colonne Total, ce sont les entités ; la dernière, les créneaux bande ; les deux le disent au survol.",
|
||||||
|
"Les entités DXCC supprimées survivent à un import. Un QSO portant une entité depuis supprimée par l'ARRL — Malyj Vysotskij, la Tchécoslovaquie, la RDA et les autres — était réécrit d'après cty.dat, qui ne sait que où se trouve un indicatif AUJOURD'HUI : un R1MVI de 2004 revenait en Russie d'Europe et le crédit disparaissait, pour un endroit qu'on ne pourra plus jamais contacter. Le numéro du fichier est désormais conservé pour ces entités."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package dxcc
|
||||||
|
|
||||||
|
// The DELETED DXCC entities, and why a lookup table needs to know about them.
|
||||||
|
//
|
||||||
|
// cty.dat answers one question: what entity is this callsign in TODAY. That is
|
||||||
|
// the right answer for a spot and the wrong one for a log. R1MVI was Malyj
|
||||||
|
// Vysotskij Island, entity 151, until the ARRL deleted it in February 2012;
|
||||||
|
// today the same callsign resolves to European Russia, 54. A 2004 contact
|
||||||
|
// re-stamped from cty.dat therefore loses the entity it was made with — and
|
||||||
|
// with it a DXCC credit that cannot be worked again, because the place no
|
||||||
|
// longer counts.
|
||||||
|
//
|
||||||
|
// Reported by an operator importing a Logger32 export: his ADIF said
|
||||||
|
// <DXCC:3>151 and OpsLog stored 54.
|
||||||
|
//
|
||||||
|
// So this list exists to be able to say "that number belongs to something that
|
||||||
|
// no longer exists, leave it alone". It is the ADIF specification's own
|
||||||
|
// enumeration of deleted entities, and it does not change except when the ARRL
|
||||||
|
// deletes another one.
|
||||||
|
var deletedEntities = map[int]string{
|
||||||
|
2: "Abu Ail Is.",
|
||||||
|
8: "Aldabra",
|
||||||
|
19: "Bajo Nuevo",
|
||||||
|
23: "Blenheim Reef",
|
||||||
|
25: "British North Borneo",
|
||||||
|
26: "British Somaliland",
|
||||||
|
28: "Canal Zone",
|
||||||
|
30: "Celebe & Molucca Is.",
|
||||||
|
39: "Comoros",
|
||||||
|
42: "Damao, Diu",
|
||||||
|
44: "Desroches",
|
||||||
|
55: "Farquhar",
|
||||||
|
57: "French Equatorial Africa",
|
||||||
|
58: "French Indo-China",
|
||||||
|
59: "French West Africa",
|
||||||
|
67: "French India",
|
||||||
|
68: "Kuwait/Saudi Arabia Neutral Zone",
|
||||||
|
81: "Germany",
|
||||||
|
85: "Bonaire, Curacao",
|
||||||
|
93: "Geyser Reef",
|
||||||
|
101: "Goa",
|
||||||
|
102: "Gold Coast, Togoland",
|
||||||
|
113: "Ifni",
|
||||||
|
115: "Italian Somaliland",
|
||||||
|
119: "Java",
|
||||||
|
127: "Kamaran Is.",
|
||||||
|
128: "Karelo-Finnish Republic",
|
||||||
|
134: "Kingman Reef",
|
||||||
|
139: "Kuria Muria I.",
|
||||||
|
151: "Malyj Vysotskij I.",
|
||||||
|
154: "Yemen Arab Republic",
|
||||||
|
155: "Malaya",
|
||||||
|
164: "Manchuria",
|
||||||
|
178: "Minerva Reef",
|
||||||
|
183: "Netherlands Borneo",
|
||||||
|
184: "Netherlands New Guinea",
|
||||||
|
186: "Newfoundland, Labrador",
|
||||||
|
193: "Okinawa (Ryukyu Is.)",
|
||||||
|
194: "Okino Tori-shima",
|
||||||
|
196: "Palestine",
|
||||||
|
200: "Portuguese Timor",
|
||||||
|
208: "Ruanda-Urundi",
|
||||||
|
210: "Saar",
|
||||||
|
218: "Czechoslovakia",
|
||||||
|
220: "Sarawak",
|
||||||
|
226: "Saudi Arabia/Iraq Neutral Zone",
|
||||||
|
228: "Serrana Bank & Roncador Cay",
|
||||||
|
229: "German Democratic Republic",
|
||||||
|
231: "Sikkim",
|
||||||
|
243: "People's Democratic Rep. of Yemen",
|
||||||
|
244: "Southern Sudan",
|
||||||
|
255: "St. Maarten, Saba, St. Eustatius",
|
||||||
|
258: "Sumatra",
|
||||||
|
261: "Swan Is.",
|
||||||
|
264: "Tangier",
|
||||||
|
267: "Territory of New Guinea",
|
||||||
|
268: "Tibet",
|
||||||
|
271: "Trieste",
|
||||||
|
307: "Zanzibar",
|
||||||
|
488: "Walvis Bay",
|
||||||
|
493: "Penguin Is.",
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDeleted reports whether a DXCC entity number names an entity the ARRL has
|
||||||
|
// deleted.
|
||||||
|
//
|
||||||
|
// The one thing every caller does with a true answer is the same: stop. A
|
||||||
|
// deleted entity cannot be looked up from a callsign, cannot be worked again,
|
||||||
|
// and cannot be corrected by anything that only knows about today.
|
||||||
|
func IsDeleted(n int) bool {
|
||||||
|
_, ok := deletedEntities[n]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletedName is what entity n was called, or "" if it is not a deleted entity.
|
||||||
|
// Used to say WHICH entity was preserved rather than only that one was.
|
||||||
|
func DeletedName(n int) string { return deletedEntities[n] }
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package dxcc
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// The contact that reported this: R1MVI, worked in 2004 on Malyj Vysotskij
|
||||||
|
// Island, entity 151, deleted in February 2012. cty.dat resolves that callsign
|
||||||
|
// to European Russia (54) today, so anything that re-stamps an old QSO from a
|
||||||
|
// callsign has to know to stop here.
|
||||||
|
func TestMalyjVysotskijIsKnownDeleted(t *testing.T) {
|
||||||
|
if !IsDeleted(151) {
|
||||||
|
t.Fatal("151 (Malyj Vysotskij I.) must be known as deleted — the whole guard hangs on it")
|
||||||
|
}
|
||||||
|
if got := DeletedName(151); got == "" {
|
||||||
|
t.Error("a deleted entity should be able to say what it was called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The guard must not fire on entities that are alive, or every log would freeze
|
||||||
|
// at whatever DXCC number it was imported with — including the wrong ones.
|
||||||
|
func TestLiveEntitiesAreNotTreatedAsDeleted(t *testing.T) {
|
||||||
|
live := map[int]string{
|
||||||
|
54: "European Russia — what R1MVI resolves to today",
|
||||||
|
227: "France",
|
||||||
|
291: "United States",
|
||||||
|
339: "Japan",
|
||||||
|
1: "Canada",
|
||||||
|
}
|
||||||
|
for n, what := range live {
|
||||||
|
if IsDeleted(n) {
|
||||||
|
t.Errorf("%d (%s) must not be listed as deleted", n, what)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got := DeletedName(54); got != "" {
|
||||||
|
t.Errorf("DeletedName(54) = %q, want empty", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A few more from the ADIF enumeration, so a careless edit to the table is
|
||||||
|
// caught rather than discovered by an operator whose credits moved.
|
||||||
|
func TestSomeOtherDeletedEntities(t *testing.T) {
|
||||||
|
for _, n := range []int{218 /* Czechoslovakia */, 229 /* German DR */, 255 /* St. Maarten, Saba, St. Eustatius */, 28 /* Canal Zone */} {
|
||||||
|
if !IsDeleted(n) {
|
||||||
|
t.Errorf("%d should be a deleted entity", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user