From b9b005ea36ac8406b5b911a15aace729fd0a21e1 Mon Sep 17 00:00:00 2001 From: rouggy Date: Tue, 7 Jul 2026 21:54:53 +0200 Subject: [PATCH] fix: Ultrabeam was not synchronizing after click on a spot. --- app.go | 45 +++++++++++++++++++++ frontend/src/components/qsl/CardPreview.tsx | 9 +++-- frontend/src/components/qsl/EditorPanel.tsx | 10 +++++ frontend/src/components/qsl/qslTypes.ts | 1 + internal/qslcard/template.go | 1 + 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/app.go b/app.go index 0001ac3..86ea61b 100644 --- a/app.go +++ b/app.go @@ -7762,9 +7762,54 @@ func (a *App) SetCATFrequency(hz int64) error { if err != nil { applog.Printf("cat: SetFrequency(%d Hz) dispatch error: %v", hz, err) } + // Re-tune the Ultrabeam right away on a DELIBERATE freq set (spot click, band + // button, memory recall) instead of waiting for the ~1.5 s follow poll + the + // CAT echo — the reason it used to only follow after you nudged the VFO. + go a.ultrabeamFollowNow(hz) return err } +// ultrabeamFollowNow re-tunes the Ultrabeam to freqHz at once (best-effort), +// honouring the same enabled/follow/in-range/step-deadband rules as the follow +// loop. Called from SetCATFrequency so a spot click moves the antenna instantly. +func (a *App) ultrabeamFollowNow(freqHz int64) { + c := a.ultrabeam + if c == nil || freqHz <= 0 { + return + } + s, err := a.GetUltrabeamSettings() + if err != nil || !s.Enabled || !s.Follow { + return + } + step := s.StepKHz + if step <= 0 { + step = 50 + } + st, err := c.GetStatus() + if err != nil || st == nil || !st.Connected { + return + } + if st.FreqMin > 0 && st.FreqMax > 0 { + mhz := freqHz / 1_000_000 + if mhz < int64(st.FreqMin) || mhz > int64(st.FreqMax) { + return // outside the antenna's tunable range + } + } + khz := int(freqHz / 1000) + diff := khz - st.Frequency + if diff < 0 { + diff = -diff + } + if st.Frequency > 0 && diff < step { + return // within the deadband — don't chase a tiny QSY + } + if err := c.SetFrequency(khz, st.Direction); err != nil { + applog.Printf("ultrabeam: immediate re-tune to %d kHz failed: %v", khz, err) + } else { + applog.Printf("ultrabeam: re-tuned on freq set → %d kHz (dir %d)", khz, st.Direction) + } +} + // SetCATMode sets the rig's mode. ADIF mode names (SSB / CW / FT8 / …) are // translated to backend-specific values by the backend itself. func (a *App) SetCATMode(mode string) error { diff --git a/frontend/src/components/qsl/CardPreview.tsx b/frontend/src/components/qsl/CardPreview.tsx index 6f5eb1c..4740ad3 100644 --- a/frontend/src/components/qsl/CardPreview.tsx +++ b/frontend/src/components/qsl/CardPreview.tsx @@ -292,6 +292,7 @@ const QSO_FIELD_WEIGHT: Record = { // QSOBoxView renders the confirmation box with per-QSO values. function QSOBoxView({ box, values }: { box: QSOBox; values: Record }) { + const fg = box.fg || '#14243a'; // text colour (field labels use it at 0.6 opacity) const avail = box.w - 56; const total = box.fields.reduce((s, f) => s + (QSO_FIELD_WEIGHT[f] ?? 1), 0) || 1; let cursor = 28; @@ -304,24 +305,24 @@ function QSOBoxView({ box, values }: { box: QSOBox; values: Record - {box.title} {cols.map(({ f, x }) => ( - {(QSO_FIELD_LABELS[f] ?? f).toUpperCase()} - {values[f] ?? ''} ))} {box.footer && ( - {box.footer} diff --git a/frontend/src/components/qsl/EditorPanel.tsx b/frontend/src/components/qsl/EditorPanel.tsx index 5632568..c0d067b 100644 --- a/frontend/src/components/qsl/EditorPanel.tsx +++ b/frontend/src/components/qsl/EditorPanel.tsx @@ -182,6 +182,16 @@ export function EditorPanel({ template, sel, presets, fontFamilies, onPatchEleme onChange={(ev) => onPatchBox({ bg: ev.target.value })} /> +
+ +
+ onPatchBox({ fg: ev.target.value })} /> + onPatchBox({ fg: ev.target.value })} /> +
+
onPatchBox({ radius })} />
diff --git a/frontend/src/components/qsl/qslTypes.ts b/frontend/src/components/qsl/qslTypes.ts index 06a8542..2151eb1 100644 --- a/frontend/src/components/qsl/qslTypes.ts +++ b/frontend/src/components/qsl/qslTypes.ts @@ -118,6 +118,7 @@ export interface QSOBox { h: number; bg: string; bg_opacity: number; + fg?: string; // text colour (default dark); set it when using a dark background radius: number; title: string; fields: string[]; diff --git a/internal/qslcard/template.go b/internal/qslcard/template.go index 17e13e1..833dc63 100644 --- a/internal/qslcard/template.go +++ b/internal/qslcard/template.go @@ -194,6 +194,7 @@ type QSOBox struct { H float64 `json:"h"` BG string `json:"bg"` BGOpacity float64 `json:"bg_opacity"` + FG string `json:"fg,omitempty"` // text colour (default dark) Radius float64 `json:"radius"` Title string `json:"title"` Fields []string `json:"fields"`