Statistics — the activity chart ignored the period selector: picking a year up
top left it showing the last 7 days, two controls contradicting each other. The
buttons were four fixed rolling windows anchored on the newest QSO, not
granularities. They now choose the BUCKET SIZE over the selected period, with an
Auto default and a step-up when a bucket would be too fine (the backend drops a
series past 750 buckets rather than ship 6000 unreadable bars). The hour-of-day
histogram covered a single day, too little to read anything from; it moves to
its own card with a weekday view, over the whole period.
Band map — the CW/data/phone sub-bands were shaded with the STATUS tokens, the
same amber that means "new band" on the pills drawn on top of them. A sub-band
is an identity, not a state: categorical hues now, validated in both themes
(violet failed on the dark steps — 1.9 ΔE from blue for a protan reader) and
added to the legend, since identity must never be colour-alone.
Frameless window — the OS title bar was a dead 32px band above a window that
already has its own. Minimise/maximise/close move into the app header, which
carries the drag region and double-click-to-maximise. The drag opt-out is by
ROLE in CSS, so a future button in that bar keeps working without anyone
remembering the rule.
Fixes:
- Saving settings froze the window while a device was slow: restarting a link
waits for its poll goroutine, which can sit 45 s inside an uninterruptible COM
Connect when another program holds the rig. The restart is now off the UI
path; a slow one is logged.
- Multi-screen: a MAXIMISED window was never repositioned, so it came back on
the primary screen every launch. The corner is now recorded even when
maximised (it names the monitor) and re-applied while still hidden.
- The Callsign field is marked out at rest — operators were typing the call into
Name, which sat beside it and looked identical.
- QSL dates get a real picker (native, for the OS calendar and locale order); a
malformed old value stays in a text box rather than vanishing behind an empty
one.
Also: a Support OpsLog entry in Help, and a WinKeyer protocol trace. The WK2
report (one element then a ten-second pause, sidetone that will not switch off)
is NOT fixed here: the WK1/WK2/WK3 command sets differ and a guessed fix would
break the operators it works for today. The trace logs every byte with the
command name and spells out the firmware family, which is what will settle it.
On its CAT/AUX connector an ACOM is the MASTER: it polls a transceiver and
changes band from the reply, so nothing can be pushed to it. internal/catemu
answers those polls on a second serial port, in ACOM command set 5 (Kenwood /
Elecraft): FA;, FB;, IF; and ID;, and nothing else — a wrong-length reply is
worse than none, it desynchronises the amp's parser for the following poll.
Also offered for SPE. Some amps do not poll at all but read the radio↔PC CAT
line in parallel; those never hear a responder, so an optional unprompted send
(500/1000 ms) covers them.
The TX frequency is what is sent: in split the amp must be tuned where we
transmit. Frame lengths are pinned by tests — the failure they guard against is
silent and only shows up as an amp that mistunes.
Untested on hardware.
- SCP/N+1: new internal/scp downloads the community MASTER.SCP master list and a
docked two-column widget shows, as you type a call, the known calls containing it
(Partial) and the calls one edit away (N+1) — click to fix a busted call. Opt-in
in Settings → General; top-bar toggle; queried debounced on callsign input.
- Flex: OpsLog's GUI-client detection was too loose and could bind to "SmartSDR CAT"
(or DAX) — both carry "smartsdr" in the program name — instead of the real GUI
client, making SmartSDR CAT drop and reconnect in a loop while OpsLog was open.
Now it binds only to a real SmartSDR/Maestro GUI client (e.g. a FLEX-8600M's
integrated screen) and excludes cat/dax; dropped the risky empty-program fallback.
- Telemetry: on a fresh install the callsign isn't set at launch, so the once-a-day
heartbeat recorded the machine UUID. Now it waits (~10 min) for the operator to
enter their callsign before sending, falling back to the UUID only if none appears.
With ESM enabled (Settings → CW Keyer), the keyer active and mode CW, Enter in
the entry strip fires a macro by QSO stage instead of logging:
- callsign field empty → F1 (CQ)
- callsign entered (in call field) → F2 (report), then focus jumps to RST-sent
- Enter in RST-sent / RST-rcvd → F3 (TU), which logs via its own <LOGQSO>
Works for every keyer engine (WinKeyer / serial DTR-RTS / Flex CWX / Icom CI-V)
since it routes through the shared macro sender. Enter in other fields still logs
normally, and ESM off keeps the classic Enter-to-log behaviour.
Backend: new `esm` flag on WinkeyerSettings (winkeyer.esm). Frontend: esmHandleEnter
state machine keyed off data-esm markers on the call/RST blocks, a Settings
checkbox + hint, and i18n EN/FR.
Push the tuner control further so it mirrors the native 4O3A app and the way the
PowerGenius XL is surfaced.
Backend (internal/tunergenius):
- Status now carries both RF channels A and B (source/mode, band, frequency,
bound Flex nickname, per-channel bypass, antenna, PTT), the active channel,
the C1/L/C2 relay-network positions, and the 3-way-vs-SO2R hardware variant
(learned once from the `info` reply). Flat freq/antenna still mirror the active
channel for the compact widget.
- New TunerGeniusActivate(ch) binding → `activate ch=N` (or `ant=N` on 3-way).
Frontend:
- New shared TunerCard, styled exactly like AmpCard (PWR/SWR meter bars,
A/B channel selector with source+freq+antenna, Tune/Bypass/Operate). Used in
BOTH the FlexRadio panel (its own card, like the PGXL) and Station Control.
- Docked TunerGeniusPanel widget now shows the two channels A/B with their
source/frequency/antenna and lets you click to make one active — the missing
A/B state the user flagged.
- i18n EN/FR for the new labels (channels, antenna, in-line/bypassed, title).
Still UNTESTED on hardware — verify the per-channel field names/units and the
activate behaviour on the real box.
New internal/tunergenius client speaking the same "Genius Series" text API
as the Antenna Genius / PowerGenius XL: banner on connect (with optional
"AUTH" for remote access), "C<seq>|<cmd>\n" commands, "R<seq>|<code>|<msg>"
replies and the "S<seq>|status k=v …" snapshot; async "M|<msg>" info lines
are consumed inline. Polls status ~1.5s and exposes SWR (return-loss dB
converted to a VSWR ratio), forward power (dBm→W), and the operate/bypass/
tuning/active-channel state. Actions: autotune, global bypass, operate/standby.
Controlled directly (not via the radio) so OpsLog uses only one of the box's
four connection slots, per the device's protocol doc.
Wiring:
- app.go: tunergenius.* settings keys, Get/Save/start, GetTunerGeniusStatus,
TunerGeniusAutotune/SetBypass/SetOperate; started in the background at launch.
- Settings → Tuner Genius panel (enable + IP + optional remote code).
- Docked TunerGeniusPanel widget (SWR/power readouts + Tune/Bypass/Operate),
top-bar toggle, shown when enabled — mirrors the Antenna Genius widget.
- i18n EN/FR (sec.tunergenius, tg2.*, tgp.*).
Command verbs (operate/bypass/autotune/status/auth) come straight from the
4O3A "Tuner Genius XL — Protocol Description"; UNTESTED on hardware.
Design flaw: opslog.db held BOTH the config (settings + profiles) AND the SQLite
logbook, so the only way to a separate SQLite logbook — the whole-app "change
database location" pointer — swapped the config too, wiping the operator's setup
(reported: creating a new SQLite for a visiting op lost all profiles/settings).
Now the two are separate files:
- Settings database: settings + profiles. Fresh installs name it settings.db;
existing installs keep opslog.db.
- Logbook (QSOs): a dedicated logbook.db next to the settings db, or a
per-profile file (profile.ProfileDB.Path), or MySQL. connectLogbook opens the
logbook file (db.Open creates+migrates on first use); the settings db is used
as the logbook only if the split ever fails.
One-time, non-destructive migration at startup: if there's no logbook file yet
but the settings db already holds QSOs (legacy combined opslog.db), seed
logbook.db with a clean copy via VACUUM INTO — contacts move to the logbook, the
originals stay in the settings db as an untouched backup.
UI: the Database panel now shows the settings database and this profile's logbook
as two clearly labelled sections (+ "Open folder"), and the logbook selector is
SQLite (default logbook.db, or a dedicated file via "Choose a dedicated file…")
vs MySQL — no more confusing shared/separate choice. New binding RevealDataFolder.
Design flaw: the SQLite backend conflated the app/config database (opslog.db —
settings + profiles) with the QSO logbook. connectLogbook returned a.db for any
non-MySQL profile, so the ONLY way to get a separate SQLite logbook was the
whole-app "change database location" pointer — which swapped config + profiles
too, wiping the operator's setup (reported: creating Jerem.db lost all configs).
profile.ProfileDB gains a Path field: a non-empty path routes that profile's
logbook to its own SQLite file (db.Open creates + migrates it on first use),
while opslog.db keeps settings/profiles. connectLogbook opens the file; empty
path = shared db (unchanged default, backward compatible). MySQLSettings carries
sqlite_path; Get/Save wire it through.
UI: the Database panel gains a third backend option "SQLite — separate file"
with a file picker, and now clearly labels the shared application database
(settings + profiles) vs this profile's logbook, so the two are never confused.
A SteppIR only covers part of the spectrum (a standard one is 20 m–6 m) but
can't report its own range, so the follow loop's existing out-of-range guard
(FreqMin/FreqMax) never engaged for it. Tuning the rig to a band the antenna
can't reach (e.g. 30 m) made the loop keep trying — the immediate cause of the
stuck TX interlock addressed in the previous commit.
Add a Tunable range (MHz) setting to the Antenna panel, wired into the SteppIR
adapter's Status().FreqMin/FreqMax so the follow loop skips out-of-range
frequencies entirely: no tune command, no TX inhibit. Defaults to 13–54 MHz
(20 m–6 m, the common model) when unset; widen the low edge for a 40 m-equipped
SteppIR. Ultrabeam is unaffected (it reports its own per-band coverage).
Keeps the last-commanded-freq deadband from the previous commit as a universal
safety net for any flaky-status case within the valid range.
Opt-in (Settings → General). Shows a "MW #rank" pill next to the DXCC entity
name in the entry-strip band/slot matrix — ClubLog's Most Wanted ranking
(1 = most wanted), personalised to the operator's callsign and refreshed daily.
- internal/clublog/mostwanted.go: fetch mostwanted.php?api=1&callsign=<CALL>
(rank→DXCC JSON, real User-Agent), invert to dxcc→rank, cache to
<dataDir>/clublog_mostwanted.json; NeedsRefresh invalidates on callsign change.
- app.go: keyClublogMostWanted setting, a.clublogMW, startup refresh goroutine,
activeCallsign() helper, and Get/Set/Download bindings mirroring the cty ones.
WorkedBefore now carries MWRank (qso.WorkedBefore.MWRank) when enabled.
- BandSlotGrid.tsx: MW pill next to the entity name, colour-tiered by rank.
- SettingsModal General: toggle + download button + status, mirroring the
ClubLog cty-exceptions block.
Also reorganises the changelog: the theme-persistence fix (landed after the
v0.20.11 tag) plus this feature go under a new 0.20.12 entry; 0.20.11 keeps only
what shipped in its binary.
Multi-amp: Settings->Amplifier becomes a list (amps.json, legacy single-amp keys auto-migrate to entry #1); one client per enabled amp with per-id bindings (GetAmplifiers/SaveAmplifiers/GetAmpStatuses/AmpOperate/AmpPower/AmpPowerLevel/AmpFanMode); legacy a.pgxl/a.spe/a.acom point at the first enabled amp of each family. Amp cards in FlexPanel and Station Control gain a dropdown to pick the amp; the status bar shows one clickable chip per amp. Use case: two SPEs run in parallel.
Flex v4 DSP (8000/Aurora): NRL/ANFL (lms_nr/lms_anf), NRS (speex_nr), NRF (nrf) with level sliders, RNN (rnnoise) and ANFT on/off — keys per the FlexLib slice docs; the section only shows when the radio reports these keys (dsp_v4 flag), so 6000-series panels are unchanged.
The station-control code rebuilt a fresh driver on every status poll and every
relay set. Stateful boards (Denkovi FTDI D2XX, USB-serial) hold an OS handle only
one opener can own, so the first poll opened the board and leaked the handle, and
every poll after failed with 'device in use' — the relays greyed out a second
after Save and auto-control never switched. Drivers are now cached per device and
reused, closed on config change. Adds a Test-connection button + detect feedback
in the device editor (reported by VK4MA).
Embeds changelog.json (per-version EN + FR notes, curated from the release's
commits). GetWhatsNew compares the stored last-seen version with the running
build and returns the notes for every newer version, once, then advances the
marker. A dialog shows them in the UI language on the first launch after update.
Seeded with the 0.20.5 notes.
Day/Week/Month/Year now show the anchor day hour-by-hour, the last 7 days, the
last 30 days, and the last 12 months — anchored to the period end (else now), in
real date order. Replaces the cyclical hour-of-day/weekday/day-of-month/month
buckets, which left future days empty and ordered the weekend after Monday.
Backend adds cyclical distributions ByHour (hour-of-day), ByDOW (weekday), ByDOM
(day-of-month) and ByMonthYr (calendar month), all UTC. The Activity-over-time
card gains a selector: Timeline keeps the chronological month trend; Day/Week/
Month/Year show the hour/weekday/day/month distribution as bars.
Adds Stats.ByBandCategory (per band: CW/phone/data counts, band-plan order) via a
modeCategory() bucketing in stats.go, and a StackedBandBars chart in the Stats
panel showing the split per band with a legend.
Implements the SPE Application Programmer's Guide protocol: packets
0x55 0x55 0x55|CNT|DATA|CHK (sum%256), OPERATE key 0x0D (toggles STANDBY/OPERATE)
and STATUS request 0x90 whose 0xAA-framed reply is a 19-field CSV (mode, RX/TX,
band, power level, output W, SWR, V/I, temp, warnings, alarms). internal/spe
polls status ~1/s over USB serial (go.bug.st/serial, 8N1) or TCP (RS232-to-
Ethernet bridge) — same codec, different transport.
Wired via startPGXL (starts the SPE client for spe* types), bindings GetSPEStatus
/ SPESetOperate, and a live status card + OPERATE/STANDBY toggle in the Amplifier
settings panel. Only the two example-anchored commands are sent (safe); other
keystroke codes were ambiguous in the guide's table. Untested on hardware.
Renames the Hardware section to Amplifier and adds an amplifier-type selector
(4O3A PowerGenius XL, SPE Expert 1.3K-FA / 1.5K-FA / 2K-FA) plus a transport
choice for the SPE amps: USB (serial COM + baud) or Network (RS232-to-Ethernet
IP:port). PGXLSettings gains Type/Transport/ComPort/Baud (keys keep the pgxl.*
prefix for back-compat). PowerGenius still drives over TCP; SPE settings are
stored but control is not wired yet (needs the SPE serial protocol).
- Denkovi (FT245 D2XX) now selectable as 4 or 8 relays (a 4-relay board just uses
the low nibble); NewDenkovi takes a count, driven by the device's Channels.
- New 'usbrelay' backend: cheap CH340/LCUS USB-serial boards over a COM port
using the common A0 protocol ([0xA0][relay][state][checksum]); write-only, so
Status is a shadow. Channel count configurable (1/2/4/8/16).
- StationDevice gains Channels; deviceRelayCount() drives label/relay counts for
the variable-size types. Device editor: channel selector + COM-port picker
(usbrelay) / FTDI-serial (denkovi).
Both untested on hardware; the A0 command set / Denkovi bit order may need a tweak
after a live test.
Award refs are now computed once and stored on the QSO as a JSON column
(award_refs, e.g. {"DDFM":"74","WAJA":"12"}) instead of recomputing the whole
award engine on every grid page load. Written on log/edit/UDP-import and
bulk-recomputed when an award definition or reference list changes; a one-time
per-logbook backfill materializes pre-existing QSOs. The grid reads the column
directly like any other field, so it's fast and available everywhere (including
the shared MySQL logbook).
Also fix per-profile grid column layout: the DB copy was already per-profile,
but the localStorage cache used one global namespace and shadowed it, so a
profile switch kept the previous profile's columns/widths. gridPrefs now scopes
the cache by active profile id and the grids remount on profile:changed.
GetQSORate now returns both the active operator's rate (their own performance)
and the whole station's rate (all operators combined). RecentRateBreakdown
computes both in one scan of the recent rows (per-operator + all-operators),
replacing RecentRate.
ON AIR badge (bottom status bar, shown when live-status publishing is on): a
blinking red light + "On air" when a QSO was logged in the last 5 min, dim
"Offline" otherwise. Seeded at launch from LiveLastQSOAgeSec so it's right
immediately, flips online on qso:logged, expires via a 10s tick.
Auto-updater UI: the update check now also runs every 10 minutes, and the
update notification downloads and installs in-app — a live progress bar
(update:progress) then OpsLog restarts on the new build. Falls back to opening
the release page when there's no auto-download asset or on error (retry).
Regenerated bindings for DownloadAndApplyUpdate / LiveLastQSOAgeSec.
Automatically switches the Station Control relay boards from the rig's
current frequency / band. Each relay carries one rule: off (manual), a
frequency window (ON inside [lo,hi] kHz, OFF outside), or a set of bands
(ON on those bands, OFF elsewhere). Evaluated on every CAT frequency/band
change; a relay is only switched when its desired state actually changed,
so tuning within a range doesn't hammer the board.
A cached atomic flag keeps the CAT hot path a no-op when the feature is off
(important during FT8 slice churn). Saving re-applies from the live
frequency so a changed rule takes effect immediately.
New Settings → Hardware → Relay auto-control section: master enable plus a
per-relay mode (Off / Frequency / Band) with kHz range inputs or band
chips, per configured relay board. i18n EN + FR. Azimuth/Time modes (the
other two PstRotator tabs) are left for later.
Watches a configurable list of external ADIF files (fldigi RTTY logbook,
N1MM, VarAC…) and imports newly appended QSOs automatically. Each record
goes through the existing UDP-log path, so it gets full enrichment, ±2-minute
dedup (shared udpLogMu, can't race the UDP auto-log) and automatic upload to
the configured external services — no per-file toggles like Log4OM.
A newly added file starts at its current size (offset -1 sentinel → size on
first scan), so the QSOs already in it are NOT bulk-imported; only contacts
logged after it was added come in. Reads only up to the last complete <eor>
so a half-written record waits. Handles truncation/rotation (re-reads from 0,
dedup protects) and persists per-file offsets without clobbering a concurrent
UI edit of the file list.
New Settings → ADIF monitor section: master enable + add/remove/toggle files.
Backend emits adifmon:imported → the grid refreshes and a toast reports the
count. i18n EN + FR.