9 Commits
Author SHA1 Message Date
rouggyandClaude Opus 5 8b1dff581b feat(linux): the Go half of OpsLog builds for Linux
Measured rather than guessed: the whole repository was cross-compiled for
linux/amd64 and the gaps closed one by one. There were fewer than expected.

Flex and TCI were never Windows-specific — they carried //go:build windows by
inheritance and import nothing but net and gorilla/websocket. Untagged, no code
change. The two backends a Linux operator is most likely to own were already
portable.

Audio was 560 lines, not 2287: only devices.go and engine.go touch WASAPI, while
manager.go, recorder.go, wav.go and mp3.go were pure Go wearing the tag by
association. The whole platform surface is seven functions, now implemented a
second time on PulseAudio through github.com/jfreymuth/pulse — pure Go over the
server socket, so the no-cgo rule survives, and PipeWire answers the same
protocol. The fixed 16 kHz mono format and the server-side resampling mirror
what AUTOCONVERTPCM does on Windows, for the same reason.

OmniRig is the only real loss, and its backend still EXISTS off Windows rather
than being compiled out of app.go: a settings database is portable, so an
operator moving a profile across keeps "omnirig" saved and must be told to pick
a native backend instead of meeting a nil one.

The parts where Linux is not Windows, and where a compile-only stub would have
been a silent bug:

  - data dir: still beside the binary, but ~/.local/share/OpsLog/data when that
    folder belongs to the system — decided by trying the write, because /opt and
    /usr/local are writable on some stations and not others.
  - single instance: an flock, not a pid file. The kernel drops it however the
    process dies, so a crash leaves nothing to delete by hand. This is the guard
    that stops two instances fighting over the rig frequency.
  - update: simpler here. Unix renames over a running binary, so the deferred
    swap the Windows path needs a detached helper for is unreachable.
  - tasklist/taskkill become /proc and SIGTERM; the boot log moves out of /tmp,
    which is wiped exactly when the evidence is wanted.
  - serial ports sorted naturally: /dev/ttyUSB10 was landing between USB1 and
    USB2, the same trap COM10 fell into.

release.ps1 now cross-builds and vets for linux before it builds the exe, and
refuses the release if that fails — a port rots one unguarded x/sys/windows call
at a time.

Nothing has been executed on Linux yet: Wails needs webkit2gtk and cgo there, so
the binary must be built on Linux. scripts/linux-setup.sh checks the machine and
does it; BUILDING-LINUX.md is the manual version.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 10:21:27 +02:00
rouggy 2d71351080 feat(sat): the sky engine — elements, look angles, passes, Doppler
The foundation of the satellite branch, and nothing above it yet: where a
satellite is (SGP4 from akhenakh/sgp4, Apache-2.0 and pure Go, so the
no-cgo rule holds), where it will be (passes with an elevation floor,
because a three-degree scrape is a line in a table that will never be a
QSO), and what its motion does to a frequency.

The Doppler pair is the part worth being careful about: the two
corrections go in OPPOSITE directions. The downlink arrives shifted and
we tune to meet it; the uplink must LEAVE shifted the other way to land
on the transponder's nominal input. A test pins the signs and the size —
7 km/s on 2 m is about 3.4 kHz.

Elements keep their raw lines beside the parsed form: that is what the
cache stores and what an operator pastes by hand for a bird no feed
carries yet, which is exactly when everyone wants to hear it.
2026-09-07 10:36:56 +02:00
rouggy 8afba2c4e8 feat(bandopen): read PSK Reporter, and arrange the sources the watch needs
The detection shipped reading whatever the operator's cluster nodes happened to
carry. On VHF that is a few hundred skimmers, nearly all of them on HF: a 6 m
opening carrying 869 stations reached OpsLog as a handful of spots or none, and
Nexus flagged it on the same PC while OpsLog stayed silent.

internal/pskr subscribes to pskr/filter/v2/<band>/# on PSK Reporter's MQTT
broker. Every ordinary station running WSJT-X reports what it decodes, so the
difference is two orders of magnitude rather than a threshold. The feed's shape
suits us exactly: BOTH grids are in each message, so distance and bearing are
arithmetic — no lookup, and no DXCC-centre approximation, which is what made the
cluster path's bearings coarse. The geometry is injected from app.go so it stays
the same arithmetic the cluster path uses; two answers to one question is how a
bearing quietly becomes wrong.

Volume was the design constraint, not the protocol. Six metres open is thousands
of messages a minute and this runs on some very old PCs, so nothing is kept or
persisted in the watcher: each message is parsed, measured and handed on or
dropped, and the detector's existing window does the deciding.

And the part that was the real bug: enabling the watch now ARRANGES ITS OWN
SOURCES. It adds the two RBN nodes when missing and brings the feed up. A
feature that silently depends on sources nobody can know are needed does not
look unconfigured, it looks broken. Matched on host and port, not name, so an
operator who renamed theirs does not get a duplicate — which the detector would
read as twice as many stations, and announce an opening that is not there.

Turning it off leaves the nodes alone: they may have been wanted for their own
sake, and removing a node someone is using is worse than leaving one they are not.
2026-08-11 12:48:29 +02:00
rouggy fb78b1c052 feat(webpublish): publish the log as a web page or CSV, with FTP upload
Settings -> Web publishing. Renders the last N QSOs with the columns the
operator picks, writes the file locally, and optionally uploads it by FTP or
explicit FTPS.

Local write FIRST, upload second, always. A network failure then leaves a good
file on disk that can be published another way, instead of a truncated one on
the server. The local write itself goes to a temp file and renames over the
target, so a reader — or a syncing client — never sees a half-written page.

The HTML page is fully self-contained: inline CSS, inline sort script, no font,
no CDN, no external request at all. It has to work on hosting that blocks
third-party requests, and a page about someone's hobby should not report its
readers to anyone.

Columns are a curated set, not "every ADIF field". This is published to the
public: RST and QSL status belong on it, the operator's home address does not.

Two triggers, both debounced through one path: a QSO is logged, or the optional
timer fires. Fifteen seconds of coalescing means a run of contacts produces one
upload rather than one per QSO, and nobody reading a web page can tell the
difference.

The config is one JSON blob under a single settings key, and that key is marked
sensitive: the FTP password lives inside it, so the whole blob is encrypted at
rest with the others. A locked vault reads back empty, which correctly reads as
"not configured" — publishing must not run with a password it cannot decrypt.
2026-08-10 09:07:08 +02:00
rouggy 08162fa126 mysql 2026-06-13 21:56:38 +02:00
rouggy 95fdc1ccd1 aduio mail 2026-06-05 02:29:49 +02:00
rouggy a2a29c66d2 feat: added record qso dvk 2026-06-04 00:46:35 +02:00
rouggy 2b4326b553 feat: Winkeyer 2026-06-02 01:17:26 +02:00
rouggyandClaude Opus 4.7 7ace2cc602 Initial codebase: Go + Wails amateur radio logbook
Backend (Go 1.25 / Wails v2):
- QSO storage on SQLite (modernc) with embedded migrations (0001..0005)
- Streaming ADIF import (batch insert) + WorkedBefore per callsign and DXCC
- Callsign lookup with QRZ.com + HamQTH providers (primary/failsafe routing)
  and SQLite-backed TTL cache
- DXCC resolver from cty.dat (auto-download, longest-prefix-match)
- Multi-profile operator identities (home/portable/SOTA/contest) — every
  QSO stamps MY_* from the active profile
- CAT control via OmniRig COM on a single OS-locked goroutine, with
  bidirectional sync (freq/mode/band/split/VFOs) and Rig1/Rig2 hot-swap
- Settings store (key/value), CAT debug log at %APPDATA%/HamLog/cat.log

Frontend (React 18 + TypeScript + Tailwind v4 + shadcn-style):
- Single-row entry strip with CAT-aware band/mode/freq, RST, Start/End
  UTC, per-field locks (band/mode/freq/start/end) for backdated QSOs
- Topbar: live freq (MHz.kHz.Hz dotted), live UTC, band/mode/SPLIT badges,
  CAT pill with rig selector and clickable Azimuth pill (rotor TODO)
- Settings tree: Profiles (Log4OM-style manager), Station Information
  (edits the active profile), unified Callsign Lookup with Test buttons,
  Bands/Modes lists, CAT
- Worked-before matrix (band × mode × class) with new-DXCC highlighting
- ADIF import from menu + Maintenance > Refresh cty.dat

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-26 00:16:45 +02:00