diff --git a/changelog.json b/changelog.json index 8ca3b31..33a347c 100644 --- a/changelog.json +++ b/changelog.json @@ -11,7 +11,8 @@ "Band-opening detection now has the data it needs. Switching on \"Watch for band openings\" (Settings › DX Cluster) subscribes to the PSK Reporter feed — every station on the air reporting what it decodes, rather than the handful of VHF spots a cluster carries — and adds the two RBN nodes if they are missing. Pick the bands to watch: 12, 10, 6, 4 and 2 m. An MQTT chip in the status bar, beside the rig and amplifier, shows the feed is alive.", "Opening detection now covers 12 and 10 m, not just 6, 4 and 2 m. The feed was already subscribed to them while the detector still threw them away, so a busy 10 m could never be announced. An opening under way now shows as a blinking badge in the status bar, beside the clock, and stays there until the band goes quiet — the toast that announced it was gone in seconds, while an opening lasts hours.", "Test connection now actually tests. Club Log checked that the three fields were not empty and reported success without contacting anyone, so a wrong password looked exactly like a right one; it now signs in for real, through the read-only endpoint so a test can never add a record. LoTW reports its two credentials separately: TQSL signs uploads and never uses the website password, so a wrong one used to break nothing until the day confirmations were downloaded.", - "Opening detection finds an opening through the everyday noise. It used to require every station heard to fit inside one 90° sector, which suits a sporadic-E cloud and nothing else: with 10 and 12 m open the reports come from all round the compass, so the busier the band, the less likely it was to be announced. It now looks for the busiest sector instead. The direction reported was also wrong whenever a sector crossed north — an opening to the NE was announced as SW, the exact opposite." + "Opening detection finds an opening through the everyday noise. It used to require every station heard to fit inside one 90° sector, which suits a sporadic-E cloud and nothing else: with 10 and 12 m open the reports come from all round the compass, so the busier the band, the less likely it was to be announced. It now looks for the busiest sector instead. The direction reported was also wrong whenever a sector crossed north — an opening to the NE was announced as SW, the exact opposite.", + "Openings are now about YOUR region. A PSK Reporter message says \"X was heard BY Y\", and only the receiver being near you shows that signals are arriving here — a station heard in Japan proves a path that is not yours. Reports are kept when the receiving station is within 300 km, which borrows the ears of a whole region instead of waiting for a decode at your own antenna. 12 m has been dropped from the watched bands: at this point in the cycle it is open often enough that announcing it is a notification rather than news." ], "fr": [ "Décodes digitaux : le carré locator d une station pouvait être enregistré comme son indicatif quand le texte du message sortait de l ordinaire. Un grid à la place de l indicatif est maintenant refusé.", @@ -22,7 +23,8 @@ "La détection d ouverture dispose enfin des données qu il lui faut. Activer « Surveiller les ouvertures de bande » (Paramètres › Cluster DX) souscrit au flux PSK Reporter — toutes les stations en l air qui rapportent ce qu elles décodent, au lieu des quelques spots VHF que porte un cluster — et ajoute les deux nœuds RBN s ils manquent. Les bandes surveillées se choisissent : 12, 10, 6, 4 et 2 m. Une pastille MQTT dans la barre d état, à côté du rig et de l ampli, montre que le flux est vivant.", "La détection d ouverture couvre désormais le 12 et le 10 m, plus seulement le 6, 4 et 2 m. Le flux y était déjà abonné alors que le détecteur les jetait encore, donc un 10 m très actif ne pouvait jamais être annoncé. Une ouverture en cours s affiche désormais en pastille clignotante dans la barre d état, à côté de l heure, et y reste jusqu à ce que la bande se taise — le toast qui l annonçait disparaissait en quelques secondes, alors qu une ouverture dure des heures.", "Le bouton Tester la connexion teste vraiment. Club Log vérifiait que les trois champs n étaient pas vides et annonçait la réussite sans contacter personne : un mauvais mot de passe ressemblait exactement à un bon. Il s authentifie maintenant pour de vrai, via le point d accès en lecture seule pour qu un test ne puisse jamais ajouter un enregistrement. LoTW annonce ses deux identifiants séparément : TQSL signe les envois et n utilise jamais le mot de passe du site, donc un mauvais ne cassait rien jusqu au jour du téléchargement des confirmations.", - "La détection d ouverture trouve désormais une ouverture au milieu du bruit ordinaire. Elle exigeait que toutes les stations entendues tiennent dans un même secteur de 90°, ce qui convient à un nuage d Es et à rien d autre : avec le 10 et le 12 m ouverts, les reports viennent de tout l horizon, donc plus la bande était ouverte moins elle pouvait être annoncée. Elle cherche maintenant le secteur le plus dense. La direction annoncée était par ailleurs fausse dès qu un secteur traversait le nord — une ouverture au NE était annoncée au SO, soit l opposé exact." + "La détection d ouverture trouve désormais une ouverture au milieu du bruit ordinaire. Elle exigeait que toutes les stations entendues tiennent dans un même secteur de 90°, ce qui convient à un nuage d Es et à rien d autre : avec le 10 et le 12 m ouverts, les reports viennent de tout l horizon, donc plus la bande était ouverte moins elle pouvait être annoncée. Elle cherche maintenant le secteur le plus dense. La direction annoncée était par ailleurs fausse dès qu un secteur traversait le nord — une ouverture au NE était annoncée au SO, soit l opposé exact.", + "Les ouvertures concernent désormais TA région. Un message PSK Reporter dit « X a été entendu PAR Y », et seul un récepteur proche de toi prouve que les signaux arrivent ici — une station entendue au Japon prouve un chemin qui n est pas le tien. Les reports sont retenus quand la station réceptrice est à moins de 300 km, ce qui emprunte les oreilles de toute une région au lieu d attendre un décodage sur ta propre antenne. Le 12 m sort des bandes surveillées : à ce stade du cycle il est ouvert assez souvent pour que l annoncer soit une notification, pas une nouvelle." ] }, { diff --git a/internal/bandopen/bandopen.go b/internal/bandopen/bandopen.go index aa805da..7180299 100644 --- a/internal/bandopen/bandopen.go +++ b/internal/bandopen/bandopen.go @@ -68,14 +68,15 @@ func DefaultConfig() Config { // Bands watched. // -// 10 and 12 m are in, and they are HF. The line is not "HF versus VHF" but -// "is an opening here an event": 20 m being open is the normal state of the -// band and saying so is noise, while 10 and 12 m spend most of a solar cycle -// shut and open sharply when they go — which is exactly what an operator wants -// interrupting them for. They also carry the Es that reaches 6 m, often first. +// 10 m is in, and it is HF. The line is not "HF versus VHF" but "is an opening +// here an event": 20 m being open is the normal state of the band and saying so +// is noise, while 10 m spends most of a solar cycle shut and opens sharply when +// it goes — which is what an operator wants interrupting them for. // -// Anything below 12 m stays out for the reason above. -var watched = map[string]bool{"12m": true, "10m": true, "6m": true, "4m": true, "2m": true} +// 12 m was tried and dropped. It behaves too much like 20 m at this point in the +// cycle: open often enough that announcing it is a notification rather than +// news, and every band that cries wolf costs the ones that do not. +var watched = map[string]bool{"10m": true, "6m": true, "4m": true, "2m": true} // Watched reports whether a band is one the detector looks at. func Watched(band string) bool { return watched[strings.ToLower(strings.TrimSpace(band))] } diff --git a/internal/pskr/pskr.go b/internal/pskr/pskr.go index a5bfb4f..6bba928 100644 --- a/internal/pskr/pskr.go +++ b/internal/pskr/pskr.go @@ -36,10 +36,17 @@ import ( // DefaultBroker is PSK Reporter's public MQTT endpoint, TLS. const DefaultBroker = "tls://mqtt.pskreporter.info:1884" -// Bands offered. HF below 12 m is deliberately absent: an "opening" on 20 m is -// the normal state of the band and announcing it says nothing. These are the -// bands where an opening is an event worth interrupting an operator for. -var Bands = []string{"12m", "10m", "6m", "4m", "2m"} +// DefaultNearKm is the radius that counts as "around here" for a receiver. +const DefaultNearKm = 300 + +// Bands offered. Anything below 10 m is deliberately absent, 12 m included: an +// "opening" on 20 m is the normal state of the band and announcing it says +// nothing, and 12 m is close enough to that at this point in the cycle to be the +// same problem. These are the bands where an opening is an event. +// +// Fewer subscriptions is also less traffic, which matters here: the operator's +// PC pays for every message the broker sends, whether or not anything comes of it. +var Bands = []string{"10m", "6m", "4m", "2m"} // Spot is one decode, already reduced to what a detector needs. type Spot struct { @@ -59,6 +66,14 @@ type Config struct { // OpLat/OpLon are the operator's position: every spot is measured from it, // so with no position there is nothing to measure and the watcher stays down. OpLat, OpLon float64 + // NearKm is how close a RECEIVER must be to count as "around here". A report + // collected further away proves a path that is not the operator's. + // + // 300 km by default: far enough to borrow the ears of a whole region, which + // is the point — an opening reaches an area, not a postcode, and waiting for + // a decode at one's own station is just working the band. Close enough that + // the ionosphere doing something there is the ionosphere doing it here. + NearKm int // Geo turns two grids into distance and bearing. Injected rather than // implemented here so it stays the SAME arithmetic the cluster path uses — // two answers for one question is how a bearing quietly becomes wrong. @@ -91,6 +106,9 @@ func New(cfg Config) *Watcher { if len(cfg.Bands) == 0 { cfg.Bands = Bands } + if cfg.NearKm <= 0 { + cfg.NearKm = DefaultNearKm + } if cfg.Logf == nil { cfg.Logf = func(string, ...any) {} } @@ -183,12 +201,28 @@ func (w *Watcher) handle(_ mqtt.Client, m mqtt.Message) { } call := strings.ToUpper(strings.TrimSpace(p.TxCall)) grid := strings.ToUpper(strings.TrimSpace(p.TxGrid)) - if call == "" || len(grid) < 4 { + rxGrid := strings.ToUpper(strings.TrimSpace(p.RxGrid)) + if call == "" || len(grid) < 4 || len(rxGrid) < 4 { return } - // The TRANSMITTER is the station on the air; the receiver is whoever - // happened to be listening. An opening is described by where the signals are - // coming from, so it is the transmitter's grid that is measured. + + // THE RECEIVER HAS TO BE NEAR THE OPERATOR. This is the whole difference + // between a useful feed and a world map. + // + // A PSK Reporter message says "X was heard BY Y". Without this check, a + // station 1400 km from here heard by somebody in Japan counted as evidence + // of an opening — it proves the path from X to JAPAN, and says nothing at all + // about whether anything reaches this station. That is how a "2 m opening" + // came to be announced from a KX9X in the United States. + // + // Only reports collected by a receiver in the operator's own region show that + // signals are actually arriving HERE, which is the only question worth asking. + if rxDist, _, ok := w.cfg.Geo(rxGrid[:4]); !ok || rxDist > w.cfg.NearKm { + return + } + + // The TRANSMITTER is the station on the air, so it is the transmitter's grid + // that gives the direction and length of the path that just proved itself. dist, brg, ok := w.cfg.Geo(grid[:4]) if !ok { return