feat(cluster): feed the locator store from PSK Reporter, filtered at the broker
The store shipped without its main source. Locators came only from this station's own WSJT-X decodes, which is what the whole MQTT discussion was about. PSK Reporter now feeds it through a new OnGrid callback, fired before any geographic filtering: what the store wants is "which square is this callsign in", and that is true whoever happened to hear the report. The subscription filters on the RECEIVER's square, a level the v2 topic exposes. Measured on the live feed: the four opening bands unfiltered are 83 messages a second, of which roughly one in a hundred survived the NearKm test that already existed here — the rest was received, TLS-decrypted, JSON-parsed and discarded. One ring of squares is 0.2 to 1.2 a second. By square rather than by DXCC, which was the obvious alternative: one country measured 1.2 messages a second (OH) against 72.5 (K) on a single band, because a DXCC can be a continent. By square the same measurement is 0.2 to 1.2, so the load follows distance — what the feed is actually about — and is the same for every operator. Grid chasing subscribes with the "+" band wildcard, so one subscription per square covers every band instead of one per band per square. The store gains a source column (decode | mqtt), migrated in place on an existing file.
This commit is contained in:
@@ -67,3 +67,54 @@ func DistanceBetweenGrids(a, b string) (km float64, ok bool) {
|
||||
}
|
||||
return HaversineKm(lat1, lon1, lat2, lon2), true
|
||||
}
|
||||
|
||||
// LatLonToGrid returns the 4-character Maidenhead square for a position.
|
||||
func LatLonToGrid(lat, lon float64) string {
|
||||
lon = math.Mod(lon+180, 360)
|
||||
if lon < 0 {
|
||||
lon += 360
|
||||
}
|
||||
lat = lat + 90
|
||||
if lat < 0 {
|
||||
lat = 0
|
||||
} else if lat > 180 {
|
||||
lat = 180
|
||||
}
|
||||
return string([]byte{
|
||||
byte('A' + int(lon/20)),
|
||||
byte('A' + int(lat/10)),
|
||||
byte('0' + int(math.Mod(lon, 20)/2)),
|
||||
byte('0' + int(math.Mod(lat, 10)/1)),
|
||||
})
|
||||
}
|
||||
|
||||
// NeighbourGrids returns the square holding (lat, lon) and the ring of squares
|
||||
// around it — 9 squares for ring 1, 25 for ring 2.
|
||||
//
|
||||
// Used to filter the PSK Reporter feed at the BROKER rather than in OpsLog. A
|
||||
// square is about 111 km tall and 150 km wide at mid latitudes, so one ring is
|
||||
// roughly the 300 km "around here" the feed already meant, and the traffic that
|
||||
// used to be received and discarded is never sent.
|
||||
func NeighbourGrids(lat, lon float64, ring int) []string {
|
||||
if ring < 0 {
|
||||
ring = 0
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
out := []string{}
|
||||
for dLat := -ring; dLat <= ring; dLat++ {
|
||||
for dLon := -ring; dLon <= ring; dLon++ {
|
||||
// One square step: 1° of latitude, 2° of longitude.
|
||||
la := lat + float64(dLat)
|
||||
lo := lon + float64(dLon)*2
|
||||
if la > 90 || la < -90 {
|
||||
continue // past a pole there is no square, not a wrapped one
|
||||
}
|
||||
g := LatLonToGrid(la, lo)
|
||||
if !seen[g] {
|
||||
seen[g] = true
|
||||
out = append(out, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user