fix(lookup): a zone is not a property of the country
Reported with real callsigns: every Asiatic Russia contact logged CQ 17 / ITU 30, whatever the operator's real zone. RU0LL and RA0FF are 19/34, UA0SDX is 18/32, and QRZ.com had all three right. Measured before touching anything: cty.dat answers 17/30 for RU0, RA0, UA0 and UA9 alike — one representative pair for a country eight CQ zones wide — while ClubLog's prefix table gives 19, 19, 18 and 17. The reporter's instinct that no UA0 sits in CQ 17 was exactly right. fillFromDXCC overrode the callbook's zones on purpose, and the reason holds only for the country: QRZ returns the political nation where cty.dat returns the DXCC entity. A zone answers a different question — not what the callsign IS but where the station SITS — and there the per-station page beats a country default. Zones now FILL rather than override; the entity is untouched. The cache made it worse by remembering our conclusion as though the page had said it, so the wrong zones would have outlived this fix. A lookup is now cached as the callbook returned it and the country file is applied on read, which also lets a cty.dat update reach old rows.
This commit is contained in:
@@ -167,9 +167,14 @@ func (m *Manager) Lookup(ctx context.Context, callsign string) (Result, error) {
|
||||
r.Callsign = call
|
||||
r.Source = p.Name()
|
||||
r.FetchedAt = time.Now().UTC()
|
||||
fillFromDXCC(&r, dxcc)
|
||||
normalizeNames(&r)
|
||||
// Cached BEFORE the cty.dat pass, so the row is a copy of the
|
||||
// callbook page rather than of our conclusions about it. Every
|
||||
// read runs the pass again (see the cache-hit path above), so a
|
||||
// later cty.dat update reaches old rows — and a value we derived
|
||||
// can never come back looking like something the page said.
|
||||
_ = m.cache.Put(ctx, r)
|
||||
fillFromDXCC(&r, dxcc)
|
||||
return r, nil
|
||||
}
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
@@ -208,9 +213,9 @@ func (m *Manager) Lookup(ctx context.Context, callsign string) (Result, error) {
|
||||
if !saysNothingAboutLocation(call) {
|
||||
clearHomeLocation(&r)
|
||||
}
|
||||
fillFromDXCC(&r, dxcc) // entity/zones/lat-lon from the FULL (slashed) call
|
||||
normalizeNames(&r)
|
||||
_ = m.cache.Put(ctx, r)
|
||||
_ = m.cache.Put(ctx, r) // the page as it was; cty.dat is applied on read
|
||||
fillFromDXCC(&r, dxcc) // entity/zones/lat-lon from the FULL (slashed) call
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
@@ -441,11 +446,23 @@ func fillFromDXCC(r *Result, dxcc DXCCResolver) bool {
|
||||
r.Continent = cont
|
||||
filled = true
|
||||
}
|
||||
if cqz != 0 {
|
||||
// Zones FILL, they do not override.
|
||||
//
|
||||
// The rule above is right for the country and wrong for the zones, because
|
||||
// they answer different questions. An entity is what a callsign IS, and
|
||||
// cty.dat is the authority on that. A zone is where the station SITS, and a
|
||||
// large entity has many: Asiatic Russia spans CQ 16 to 23 and ITU 20 to 34,
|
||||
// and cty.dat carries one representative pair for the whole country. Stamping
|
||||
// it on every UA0 threw away the callbook's per-station answer and recorded a
|
||||
// WAZ credit for a zone the operator had not worked — RU0LL is CQ 19, ITU 34,
|
||||
// and was logged 17/30.
|
||||
//
|
||||
// So the callbook wins where it spoke, and cty.dat fills the silence.
|
||||
if cqz != 0 && r.CQZ == 0 {
|
||||
r.CQZ = cqz
|
||||
filled = true
|
||||
}
|
||||
if ituz != 0 {
|
||||
if ituz != 0 && r.ITUZ == 0 {
|
||||
r.ITUZ = ituz
|
||||
filled = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user