fix(spot): spot the references worth chasing, not the derived ones
The comment came out "SSB WAC EU WAZ 15 WPX HA5" — the QSO's materialised award_refs hold both kinds, and I took them all. Those four are what every reader of the spot derives from the callsign in their head. They spent the whole 30-character comment saying nothing and buried SOTA HA/KM-018, the one reference anyone would have acted on. Computed awards are now filtered out with the same rule the QSO editor uses (isComputedAwardField), which is why they are the ones it lists on the left and not in its read-only panel. The field map moves from a ref to state as well: a ref left the first spot of a session computing before GetAwardDefs had answered. Also opens 0.25.2, since 0.25.1 shipped before the spot work, and sets the default mode list to SSB, CW, FT8, FT4, FT2, RTTY, PSK31, FM. AM and DIGITALVOICE stay in the catalogue but are no longer selected on a fresh install. FT2 joins the digital family everywhere its siblings are listed — report list, RST defaults, Flex power class — so it gets dB reports rather than the 59 an unknown mode falls back to.
This commit is contained in:
@@ -486,16 +486,20 @@ var defaultBands = []string{
|
||||
"160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m",
|
||||
"12m", "10m", "6m", "2m", "70cm", "23cm",
|
||||
}
|
||||
// defaultModes is what a fresh install starts with — the modes most operators
|
||||
// actually use, in the order they are offered. AM and DIGITALVOICE are NOT here
|
||||
// on purpose: they stay in the Available catalogue for whoever wants them, but
|
||||
// a list that opens with nine entries when two are never touched costs a scroll
|
||||
// on every mode change.
|
||||
var defaultModes = []ModePreset{
|
||||
{Name: "SSB", DefaultRSTSent: "59", DefaultRSTRcvd: "59"},
|
||||
{Name: "CW", DefaultRSTSent: "599", DefaultRSTRcvd: "599"},
|
||||
{Name: "FT8", DefaultRSTSent: "+00", DefaultRSTRcvd: "+00"},
|
||||
{Name: "FT4", DefaultRSTSent: "+00", DefaultRSTRcvd: "+00"},
|
||||
{Name: "FT2", DefaultRSTSent: "+00", DefaultRSTRcvd: "+00"},
|
||||
{Name: "RTTY", DefaultRSTSent: "599", DefaultRSTRcvd: "599"},
|
||||
{Name: "PSK31", DefaultRSTSent: "599", DefaultRSTRcvd: "599"},
|
||||
{Name: "AM", DefaultRSTSent: "59", DefaultRSTRcvd: "59"},
|
||||
{Name: "FM", DefaultRSTSent: "59", DefaultRSTRcvd: "59"},
|
||||
{Name: "DIGITALVOICE", DefaultRSTSent: "59", DefaultRSTRcvd: "59"},
|
||||
}
|
||||
|
||||
// Default RST report lists, editable in Settings → Modes. Phone carries the
|
||||
@@ -13359,7 +13363,7 @@ func flexPowerClass(mode string) string {
|
||||
return "phone"
|
||||
case "CW", "CWR":
|
||||
return "cw"
|
||||
case "FT8", "FT4", "JT65", "JT9", "JS8", "RTTY", "PSK", "PSK31", "PSK63",
|
||||
case "FT8", "FT4", "FT2", "JT65", "JT9", "JS8", "RTTY", "PSK", "PSK31", "PSK63",
|
||||
"BPSK", "QPSK", "MFSK", "OLIVIA", "CONTESTI", "DATA", "DIGITALVOICE",
|
||||
"DIGU", "DIGL", "Q65", "MSK144", "WSPR", "FST4", "FST4W", "ARDOP", "VARA":
|
||||
return "digi"
|
||||
|
||||
+14
-4
@@ -1,4 +1,16 @@
|
||||
[
|
||||
{
|
||||
"version": "0.25.2",
|
||||
"date": "",
|
||||
"en": [
|
||||
"Send Spot: the comment now carries the award references after the mode — the ones you assigned (POTA, SOTA, IOTA…), not the DXCC, zone and prefix every reader works out from the callsign. A self-spot carries your OWN activation references instead.",
|
||||
"Modes: a fresh install now starts with SSB, CW, FT8, FT4, FT2, RTTY, PSK31 and FM. AM and DIGITALVOICE stay in the available list but are no longer selected by default."
|
||||
],
|
||||
"fr": [
|
||||
"Envoi de spot : le commentaire porte désormais les références de diplôme après le mode — celles que vous avez attribuées (POTA, SOTA, IOTA…), pas le DXCC, la zone et le préfixe que chacun déduit de l’indicatif. Un auto-spot porte VOS références d’activation.",
|
||||
"Modes : une installation neuve démarre avec SSB, CW, FT8, FT4, FT2, RTTY, PSK31 et FM. AM et DIGITALVOICE restent dans la liste disponible mais ne sont plus sélectionnés par défaut."
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.25.1",
|
||||
"date": "",
|
||||
@@ -9,8 +21,7 @@
|
||||
"Appearance: new Sahara theme, warm sand tones for long sessions in daylight.",
|
||||
"US counties: Connecticut returned the census planning regions, and San Francisco, Doña Ana, Baltimore city and several Alaska boroughs matched nothing at all. The cluster now shows the same county as the Info panel. Re-download the county database.",
|
||||
"QSL: the manager (QSL_VIA) and the sending method (QSL_SENT_VIA / QSL_RCVD_VIA) are separate fields at last, as ADIF defines them. Imports keep both, exports write both, and a log where they were mixed is offered a correction at startup.",
|
||||
"WinKeyer: the opening handshake follows K1EL’s own sequence (RTS off, parser resync, echo test), which wakes keyers that stayed silent, and a port where nothing answers now says so instead of showing “connected”. A keyer that reboots when the port opens — K3NG and other Arduino builds — is waited out and remembered, so it opens directly next time.",
|
||||
"Send Spot: the comment now carries the award references after the mode — the ones on the QSO being entered, or on the logged QSO you pick from the list. A self-spot carries your OWN activation references (my POTA/SOTA/WWFF/IOTA) instead."
|
||||
"WinKeyer: the opening handshake follows K1EL’s own sequence (RTS off, parser resync, echo test), which wakes keyers that stayed silent, and a port where nothing answers now says so instead of showing “connected”. A keyer that reboots when the port opens — K3NG and other Arduino builds — is waited out and remembered, so it opens directly next time."
|
||||
],
|
||||
"fr": [
|
||||
"Amplificateurs : cochez ceux qui partagent un combineur et ON, OFF et OPERATE agissent sur tous à la fois. Chacun garde ses propres mesures.",
|
||||
@@ -19,8 +30,7 @@
|
||||
"Apparence : nouveau thème Sahara, tons sable chauds pour les longues sessions en plein jour.",
|
||||
"Comtés US : le Connecticut renvoyait les planning regions du recensement, et San Francisco, Doña Ana, Baltimore city et plusieurs districts d’Alaska ne correspondaient à rien. Le cluster affiche maintenant le même comté que le panneau Info. Rechargez la base des comtés.",
|
||||
"QSL : le manager (QSL_VIA) et le mode d’envoi (QSL_SENT_VIA / QSL_RCVD_VIA) sont enfin deux champs distincts, comme le veut l’ADIF. Les imports gardent les deux, les exports les écrivent, et un log où ils étaient mélangés se voit proposer une correction au démarrage.",
|
||||
"WinKeyer : la séquence d’ouverture suit celle de K1EL (RTS bas, resynchronisation, test d’écho), ce qui réveille les manipulateurs muets, et un port où rien ne répond le dit au lieu d’afficher « connecté ». Un manipulateur qui redémarre à l’ouverture du port — K3NG et autres montages Arduino — est attendu puis mémorisé, pour s’ouvrir directement ensuite.",
|
||||
"Envoi de spot : le commentaire porte désormais les références de diplôme après le mode — celles du QSO en cours de saisie, ou du QSO choisi dans la liste. Un auto-spot porte VOS références d’activation (mes POTA/SOTA/WWFF/IOTA)."
|
||||
"WinKeyer : la séquence d’ouverture suit celle de K1EL (RTS bas, resynchronisation, test d’écho), ce qui réveille les manipulateurs muets, et un port où rien ne répond le dit au lieu d’afficher « connecté ». Un manipulateur qui redémarre à l’ouverture du port — K3NG et autres montages Arduino — est attendu puis mémorisé, pour s’ouvrir directement ensuite."
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+26
-18
@@ -141,7 +141,9 @@ type CATState = Omit<catModels.RigState, 'convertValues'>;
|
||||
// threshold is bypassed while filtering — see buildActiveFilter).
|
||||
const FILTERED_LIMIT_CAP = 10000;
|
||||
const DEFAULT_BANDS = ['160m','80m','60m','40m','30m','20m','17m','15m','12m','10m','6m','2m','70cm','23cm'];
|
||||
const DEFAULT_MODES = ['SSB','CW','FT8','FT4','RTTY','PSK31','AM','FM','DIGITALVOICE'];
|
||||
// Mirrors defaultModes in app.go — the fallback used before the backend answers.
|
||||
// AM and DIGITALVOICE stay in the catalogue but are not offered by default.
|
||||
const DEFAULT_MODES = ['SSB','CW','FT8','FT4','FT2','RTTY','PSK31','FM'];
|
||||
// Modes the QSO recorder captures (phone only). Mirrors recordableMode() in
|
||||
// app.go — digital modes carry no useful audio, and CW has no DAX audio on Flex,
|
||||
// so neither is recorded (no REC badge / timer for them).
|
||||
@@ -311,7 +313,7 @@ function modeAccent(mode?: string): string {
|
||||
type RSTLists = { phone: string[]; cw: string[]; digital: string[] };
|
||||
function rstCategory(mode: string): keyof RSTLists {
|
||||
const m = (mode || '').toUpperCase();
|
||||
const digital = ['FT8', 'FT4', 'JT65', 'JT9', 'JS8', 'Q65', 'MSK144', 'FST4', 'FST4W', 'MFSK', 'OLIVIA', 'JT4', 'WSPR'];
|
||||
const digital = ['FT8', 'FT4', 'FT2', 'JT65', 'JT9', 'JS8', 'Q65', 'MSK144', 'FST4', 'FST4W', 'MFSK', 'OLIVIA', 'JT4', 'WSPR'];
|
||||
if (digital.includes(m)) return 'digital';
|
||||
if (['CW', 'RTTY', 'PSK31', 'PSK63', 'PSK', 'PSK125'].includes(m)) return 'cw';
|
||||
return 'phone';
|
||||
@@ -2086,6 +2088,24 @@ export default function App() {
|
||||
const [showScp, setShowScp] = useState(() => localStorage.getItem('opslog.showScp') !== '0');
|
||||
const [showBeamOnMap, setShowBeamOnMap] = useState(() => localStorage.getItem('opslog.showBeamOnMap') !== '0');
|
||||
|
||||
// Award code → scanned field (e.g. POTA→pota_ref, WWFF→wwff). Used to route
|
||||
// picked award references to the QSO field/extras each award actually reads.
|
||||
const awardFieldRef = useRef<Record<string, string>>({});
|
||||
// The same map as state, because the spot comment has to be recomputed once it
|
||||
// arrives — a ref would leave the first Send Spot of a session with no
|
||||
// references at all, or with the computed ones it is meant to filter out.
|
||||
const [awardFields, setAwardFields] = useState<Record<string, string>>({});
|
||||
useEffect(() => {
|
||||
GetAwardDefs()
|
||||
.then((defs) => {
|
||||
const m: Record<string, string> = {};
|
||||
for (const d of (defs ?? []) as any[]) m[String(d.code).toUpperCase()] = String(d.field || '').toLowerCase();
|
||||
awardFieldRef.current = m;
|
||||
setAwardFields(m);
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
// Award references offered to the Send Spot window, as cluster-ready strings.
|
||||
//
|
||||
// The entry panel holds them as "CODE@REF;CODE@REF" while the QSO is being
|
||||
@@ -2094,23 +2114,11 @@ export default function App() {
|
||||
// the station still in the field — and fall back to the last logged QSO, which
|
||||
// is where the callsign and frequency defaults come from too.
|
||||
const spotEntryRefs = useMemo(() => {
|
||||
const entry = spotRefList(parseManualRefs(details.award_refs ?? ''));
|
||||
const entry = spotRefList(parseManualRefs(details.award_refs ?? ''), awardFields);
|
||||
if (entry.length) return entry;
|
||||
return spotRefList(parseAwardRefs(qsos[0]?.award_refs));
|
||||
}, [details.award_refs, qsos]);
|
||||
return spotRefList(parseAwardRefs(qsos[0]?.award_refs), awardFields);
|
||||
}, [details.award_refs, qsos, awardFields]);
|
||||
|
||||
// Award code → scanned field (e.g. POTA→pota_ref, WWFF→wwff). Used to route
|
||||
// picked award references to the QSO field/extras each award actually reads.
|
||||
const awardFieldRef = useRef<Record<string, string>>({});
|
||||
useEffect(() => {
|
||||
GetAwardDefs()
|
||||
.then((defs) => {
|
||||
const m: Record<string, string> = {};
|
||||
for (const d of (defs ?? []) as any[]) m[String(d.code).toUpperCase()] = String(d.field || '').toLowerCase();
|
||||
awardFieldRef.current = m;
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
// === Clock ===
|
||||
const [utcNow, setUtcNow] = useState('');
|
||||
@@ -7242,7 +7250,7 @@ export default function App() {
|
||||
freqKHz: q.freq_hz ? Math.round((q.freq_hz / 1000) * 10) / 10 : 0,
|
||||
mode: q.mode ?? '',
|
||||
band: q.band,
|
||||
refs: spotRefList(parseAwardRefs(q.award_refs)),
|
||||
refs: spotRefList(parseAwardRefs(q.award_refs), awardFields),
|
||||
}))}
|
||||
onSend={async (call, freqKHz, comment) => {
|
||||
await SendClusterSpot(call, freqKHz, comment);
|
||||
|
||||
@@ -109,6 +109,7 @@ const MODE_CATALOG: Array<{ name: string; sent: string; rcvd: string }> = [
|
||||
{ name: 'DIGITALVOICE', sent: '59', rcvd: '59' },
|
||||
{ name: 'FT8', sent: '-10', rcvd: '-10' },
|
||||
{ name: 'FT4', sent: '-10', rcvd: '-10' },
|
||||
{ name: 'FT2', sent: '-10', rcvd: '-10' },
|
||||
{ name: 'JS8', sent: '-10', rcvd: '-10' },
|
||||
{ name: 'MSK144', sent: '+00', rcvd: '+00' },
|
||||
{ name: 'JT65', sent: '-15', rcvd: '-15' },
|
||||
|
||||
@@ -155,16 +155,37 @@ export function buildAwardRefs(qso: any, pickable: Array<{ code: string; field:
|
||||
return out.join(';');
|
||||
}
|
||||
|
||||
// spotRefList turns a code→ref map into the strings a DX-cluster comment wants:
|
||||
// ["POTA FR-11553", "IOTA EU-064"]. A code carrying several references (a
|
||||
// two-fer park activation) yields one entry per reference, so a spot can carry
|
||||
// the one that fits and drop the rest rather than emitting a truncated pair.
|
||||
// COMPUTED_AWARD_FIELDS mirrors isComputedAwardField in app.go: awards whose
|
||||
// reference is derived from the callsign and cty.dat, never assigned by hand.
|
||||
//
|
||||
// Shortest first: cluster comments are 30 characters, and when they do not all
|
||||
// fit, more references beat fewer.
|
||||
export function spotRefList(byCode: Record<string, string>): string[] {
|
||||
// State and county are deliberately absent, there as here — an operator sets
|
||||
// those themselves and they drive real predefined-list awards (WAS, WAJA, JCC).
|
||||
const COMPUTED_AWARD_FIELDS = new Set([
|
||||
'dxcc', 'cqz', 'ituz', 'prefix', 'callsign', 'cont', 'country', 'grid', 'grid4',
|
||||
]);
|
||||
|
||||
// spotRefList turns a QSO's code→ref map into the strings a DX-cluster comment
|
||||
// wants: ["SOTA HA/KM-018"].
|
||||
//
|
||||
// COMPUTED awards are dropped. A QSO's materialised references hold both kinds,
|
||||
// and the derived ones — DXCC, WAC, WAZ, WPX — are exactly what every reader of
|
||||
// the spot works out from the callsign anyway. Announcing "WAC EU WAZ 15 WPX HA5"
|
||||
// spends the whole 30-character comment saying nothing, and buries the one
|
||||
// reference worth chasing. Same rule as the QSO editor, which is why these are
|
||||
// the references it lists on the LEFT and not in its read-only panel.
|
||||
//
|
||||
// fieldOf is award code → scanned field (from GetAwardDefs). A code missing from
|
||||
// it is dropped rather than guessed: showing a reference on the air matters more
|
||||
// than showing all of them.
|
||||
//
|
||||
// A code carrying several references (a two-fer park) yields one entry each, so
|
||||
// a spot can take the one that fits instead of emitting a truncated pair.
|
||||
// Shortest first: when they cannot all fit, more references beat fewer.
|
||||
export function spotRefList(byCode: Record<string, string>, fieldOf: Record<string, string>): string[] {
|
||||
const out: string[] = [];
|
||||
for (const [code, refs] of Object.entries(byCode ?? {})) {
|
||||
const field = fieldOf?.[code.toUpperCase()];
|
||||
if (!field || COMPUTED_AWARD_FIELDS.has(field)) continue;
|
||||
for (const ref of String(refs).split(',').map((s) => s.trim()).filter(Boolean)) {
|
||||
out.push(`${code.toUpperCase()} ${ref.toUpperCase()}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user