feat(spot): put the award references in the spot comment

After the mode, which is the order a cluster line is read in:
"CW POTA FR-11553".

Two sources, because the QSO can be at either stage. While it is being typed
the entry panel holds the references as "CODE@REF;CODE@REF"; once logged they
live on the row as the materialised award_refs. The Send Spot window takes the
entry's when there are any and falls back to the last logged QSO — the same
fallback the callsign and frequency defaults already use — and picking a QSO
from the Latest list fills in that QSO's own.

A self-spot is the opposite case and gets its own builder: it announces OUR
station, so it carries MY_POTA_REF and friends. Using the QSO's award
references there would spot us with the park number of the station we just
worked, announcing us from somewhere we are not.

Both cap at the 30 characters a cluster node keeps, and add a reference whole
or not at all — a truncated park number is worse than none, since nobody can
act on it, and it still costs everyone who reads the spot. Only the comment we
BUILD is held to that; what the operator types is their own business.
This commit is contained in:
2026-08-14 13:22:21 +02:00
parent db87ef39c0
commit 4f9a366884
6 changed files with 159 additions and 8 deletions
+35 -1
View File
@@ -17016,6 +17016,40 @@ func (a *App) SaveSelfSpotSettings(s SelfSpotSettings) error {
// Fires on the FIRST QSO of a frequency, then at most once per configured gap.
// Called from both log paths (manual entry and UDP auto-log), off the critical
// path — a cluster that is slow or down must never hold up logging.
// spotCommentMax is the DX cluster comment field: 30 characters, and nodes
// silently drop what does not fit.
const spotCommentMax = 30
// selfSpotComment builds the comment for a spot of OUR OWN station: the mode,
// then the references WE are activating.
//
// MY_POTA_REF and friends, deliberately — not the QSO's award references, which
// belong to the station we worked. Spotting yourself with the other operator's
// park number announces you from a place you are not.
//
// A reference goes in whole or not at all: half a park number on the cluster is
// worse than none, since nobody can act on it.
func selfSpotComment(q qso.QSO) string {
out := strings.ToUpper(strings.TrimSpace(q.Mode))
for _, r := range []struct{ code, ref string }{
{"POTA", q.MyPOTARef},
{"SOTA", q.MySOTARef},
{"WWFF", q.MyWWFFRef},
{"IOTA", q.MyIOTA},
} {
ref := strings.ToUpper(strings.TrimSpace(r.ref))
if ref == "" {
continue
}
next := strings.TrimSpace(out + " " + r.code + " " + ref)
if len(next) > spotCommentMax {
continue // a shorter reference may still fit
}
out = next
}
return out
}
func (a *App) maybeSelfSpot(q qso.QSO) {
cfg, err := a.GetSelfSpotSettings()
if err != nil || !cfg.Enabled {
@@ -17051,7 +17085,7 @@ func (a *App) maybeSelfSpot(q qso.QSO) {
a.selfSpotMu.Unlock()
khz := math.Round(float64(hz)/100) / 10 // 0.1 kHz — the resolution a cluster keeps
if err := a.SendClusterSpot(call, khz, strings.ToUpper(strings.TrimSpace(q.Mode))); err != nil {
if err := a.SendClusterSpot(call, khz, selfSpotComment(q)); err != nil {
// Not sent (no enabled server, node not connected). Restore the previous
// state so the NEXT QSO tries again, instead of sitting out a gap that
// never produced a spot.