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) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
-- HamLog initial schema
|
||||
-- QSO table: core of the logbook. Field names stay close to ADIF.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS qso (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
callsign TEXT NOT NULL,
|
||||
qso_date TEXT NOT NULL, -- ISO 8601 UTC: YYYY-MM-DDTHH:MM:SSZ
|
||||
band TEXT NOT NULL, -- e.g. 20m, 40m, 2m
|
||||
mode TEXT NOT NULL, -- e.g. SSB, CW, FT8
|
||||
freq_hz INTEGER, -- frequency in Hz (integer, avoids floats)
|
||||
rst_sent TEXT,
|
||||
rst_rcvd TEXT,
|
||||
name TEXT,
|
||||
qth TEXT,
|
||||
grid TEXT,
|
||||
country TEXT,
|
||||
dxcc INTEGER,
|
||||
cont TEXT,
|
||||
cqz INTEGER,
|
||||
ituz INTEGER,
|
||||
iota TEXT,
|
||||
sota_ref TEXT,
|
||||
pota_ref TEXT,
|
||||
-- Operator context (multi-callsign / multi-location)
|
||||
station_callsign TEXT,
|
||||
operator TEXT,
|
||||
my_grid TEXT,
|
||||
my_country TEXT,
|
||||
my_sota_ref TEXT,
|
||||
my_pota_ref TEXT,
|
||||
-- Misc
|
||||
tx_pwr REAL,
|
||||
comment TEXT,
|
||||
notes TEXT,
|
||||
qsl_sent TEXT DEFAULT 'N',
|
||||
qsl_rcvd TEXT DEFAULT 'N',
|
||||
lotw_sent TEXT DEFAULT 'N',
|
||||
lotw_rcvd TEXT DEFAULT 'N',
|
||||
eqsl_sent TEXT DEFAULT 'N',
|
||||
eqsl_rcvd TEXT DEFAULT 'N',
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_callsign ON qso(callsign);
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_date ON qso(qso_date DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_band_mode ON qso(band, mode);
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_dxcc ON qso(dxcc);
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_grid ON qso(grid);
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_station ON qso(station_callsign);
|
||||
@@ -0,0 +1,21 @@
|
||||
-- Key/value app settings (lookup credentials, preferences, …)
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||
);
|
||||
|
||||
-- Local cache of QRZ/HamQTH lookups to avoid re-querying for the same call.
|
||||
CREATE TABLE IF NOT EXISTS callsign_cache (
|
||||
callsign TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
qth TEXT,
|
||||
country TEXT,
|
||||
grid TEXT,
|
||||
dxcc INTEGER,
|
||||
cqz INTEGER,
|
||||
ituz INTEGER,
|
||||
cont TEXT,
|
||||
source TEXT NOT NULL, -- 'qrz' | 'hamqth'
|
||||
fetched_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||
);
|
||||
@@ -0,0 +1,81 @@
|
||||
-- Expand the QSO table to cover the rest of the common ADIF fields.
|
||||
-- SQLite ALTER TABLE ADD COLUMN is metadata-only, so this is fast even on
|
||||
-- large logbooks. Anything not promoted to a column lives in extras_json.
|
||||
|
||||
-- --- Times / frequencies / mode ---
|
||||
ALTER TABLE qso ADD COLUMN qso_date_off TEXT; -- ISO UTC end datetime
|
||||
ALTER TABLE qso ADD COLUMN freq_rx_hz INTEGER; -- RX frequency for split operation
|
||||
ALTER TABLE qso ADD COLUMN band_rx TEXT;
|
||||
ALTER TABLE qso ADD COLUMN submode TEXT; -- USB, LSB, USB-DATA, ...
|
||||
|
||||
-- --- Contacted station extras ---
|
||||
ALTER TABLE qso ADD COLUMN state TEXT; -- US state, JA prefecture, etc.
|
||||
ALTER TABLE qso ADD COLUMN cnty TEXT;
|
||||
ALTER TABLE qso ADD COLUMN address TEXT;
|
||||
ALTER TABLE qso ADD COLUMN email TEXT;
|
||||
ALTER TABLE qso ADD COLUMN web TEXT;
|
||||
ALTER TABLE qso ADD COLUMN age INTEGER;
|
||||
ALTER TABLE qso ADD COLUMN lat REAL;
|
||||
ALTER TABLE qso ADD COLUMN lon REAL;
|
||||
ALTER TABLE qso ADD COLUMN gridsquare_ext TEXT; -- 8/10-char extension
|
||||
ALTER TABLE qso ADD COLUMN vucc_grids TEXT;
|
||||
ALTER TABLE qso ADD COLUMN rig TEXT; -- contacted station's rig
|
||||
ALTER TABLE qso ADD COLUMN ant TEXT; -- contacted station's antenna
|
||||
|
||||
-- --- QSL bureau / direct / LoTW / eQSL / Clublog / HRDLog ---
|
||||
ALTER TABLE qso ADD COLUMN qsl_via TEXT;
|
||||
ALTER TABLE qso ADD COLUMN qsl_msg TEXT;
|
||||
ALTER TABLE qso ADD COLUMN qslmsg_rcvd TEXT;
|
||||
ALTER TABLE qso ADD COLUMN qsl_sent_date TEXT;
|
||||
ALTER TABLE qso ADD COLUMN qsl_rcvd_date TEXT;
|
||||
ALTER TABLE qso ADD COLUMN lotw_sent_date TEXT;
|
||||
ALTER TABLE qso ADD COLUMN lotw_rcvd_date TEXT;
|
||||
ALTER TABLE qso ADD COLUMN eqsl_sent_date TEXT;
|
||||
ALTER TABLE qso ADD COLUMN eqsl_rcvd_date TEXT;
|
||||
ALTER TABLE qso ADD COLUMN clublog_qso_upload_date TEXT;
|
||||
ALTER TABLE qso ADD COLUMN clublog_qso_upload_status TEXT;
|
||||
ALTER TABLE qso ADD COLUMN hrdlog_qso_upload_date TEXT;
|
||||
ALTER TABLE qso ADD COLUMN hrdlog_qso_upload_status TEXT;
|
||||
|
||||
-- --- Contest ---
|
||||
ALTER TABLE qso ADD COLUMN contest_id TEXT;
|
||||
ALTER TABLE qso ADD COLUMN srx INTEGER;
|
||||
ALTER TABLE qso ADD COLUMN stx INTEGER;
|
||||
ALTER TABLE qso ADD COLUMN srx_string TEXT;
|
||||
ALTER TABLE qso ADD COLUMN stx_string TEXT;
|
||||
ALTER TABLE qso ADD COLUMN check_field TEXT; -- ADIF CHECK (reserved word in SQL)
|
||||
ALTER TABLE qso ADD COLUMN precedence TEXT;
|
||||
ALTER TABLE qso ADD COLUMN arrl_sect TEXT;
|
||||
|
||||
-- --- Satellite / propagation ---
|
||||
ALTER TABLE qso ADD COLUMN prop_mode TEXT;
|
||||
ALTER TABLE qso ADD COLUMN sat_name TEXT;
|
||||
ALTER TABLE qso ADD COLUMN sat_mode TEXT;
|
||||
ALTER TABLE qso ADD COLUMN ant_az REAL;
|
||||
ALTER TABLE qso ADD COLUMN ant_el REAL;
|
||||
ALTER TABLE qso ADD COLUMN ant_path TEXT;
|
||||
|
||||
-- --- My station extras (per-QSO overrides of the active profile) ---
|
||||
ALTER TABLE qso ADD COLUMN my_state TEXT;
|
||||
ALTER TABLE qso ADD COLUMN my_cnty TEXT;
|
||||
ALTER TABLE qso ADD COLUMN my_iota TEXT;
|
||||
ALTER TABLE qso ADD COLUMN my_dxcc INTEGER;
|
||||
ALTER TABLE qso ADD COLUMN my_cq_zone INTEGER;
|
||||
ALTER TABLE qso ADD COLUMN my_itu_zone INTEGER;
|
||||
ALTER TABLE qso ADD COLUMN my_lat REAL;
|
||||
ALTER TABLE qso ADD COLUMN my_lon REAL;
|
||||
ALTER TABLE qso ADD COLUMN my_street TEXT;
|
||||
ALTER TABLE qso ADD COLUMN my_city TEXT;
|
||||
ALTER TABLE qso ADD COLUMN my_postal_code TEXT;
|
||||
ALTER TABLE qso ADD COLUMN my_rig TEXT;
|
||||
ALTER TABLE qso ADD COLUMN my_antenna TEXT;
|
||||
ALTER TABLE qso ADD COLUMN my_gridsquare_ext TEXT;
|
||||
|
||||
-- --- Catch-all for ADIF fields we don't promote to columns ---
|
||||
-- JSON object: { "FIELD_NAME": "value", ... } (keys uppercase as in ADIF).
|
||||
ALTER TABLE qso ADD COLUMN extras_json TEXT;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_state ON qso(state);
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_contest_id ON qso(contest_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_sat_name ON qso(sat_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_qso_prop_mode ON qso(prop_mode);
|
||||
@@ -0,0 +1,8 @@
|
||||
-- Extra columns captured from QRZ/HamQTH lookups, mapped onto F2 Info fields.
|
||||
ALTER TABLE callsign_cache ADD COLUMN address TEXT;
|
||||
ALTER TABLE callsign_cache ADD COLUMN state TEXT;
|
||||
ALTER TABLE callsign_cache ADD COLUMN cnty TEXT;
|
||||
ALTER TABLE callsign_cache ADD COLUMN lat REAL;
|
||||
ALTER TABLE callsign_cache ADD COLUMN lon REAL;
|
||||
ALTER TABLE callsign_cache ADD COLUMN email TEXT;
|
||||
ALTER TABLE callsign_cache ADD COLUMN qsl_via TEXT;
|
||||
@@ -0,0 +1,30 @@
|
||||
-- station_profiles: one row per operating configuration (home, portable,
|
||||
-- SOTA, /MM, contest…). The user picks one as active; every QSO stamps
|
||||
-- the active profile's MY_* fields. Place reserved for per-profile creds
|
||||
-- (LoTW, Clublog, QRZ.com) in a later migration once those exports land.
|
||||
CREATE TABLE station_profiles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL, -- "Home", "Portable", "SOTA"…
|
||||
callsign TEXT NOT NULL DEFAULT '',
|
||||
operator TEXT NOT NULL DEFAULT '',
|
||||
my_grid TEXT NOT NULL DEFAULT '',
|
||||
my_country TEXT NOT NULL DEFAULT '',
|
||||
my_state TEXT NOT NULL DEFAULT '',
|
||||
my_cnty TEXT NOT NULL DEFAULT '',
|
||||
my_street TEXT NOT NULL DEFAULT '',
|
||||
my_city TEXT NOT NULL DEFAULT '',
|
||||
my_postal_code TEXT NOT NULL DEFAULT '',
|
||||
my_sota_ref TEXT NOT NULL DEFAULT '',
|
||||
my_pota_ref TEXT NOT NULL DEFAULT '',
|
||||
my_rig TEXT NOT NULL DEFAULT '',
|
||||
my_antenna TEXT NOT NULL DEFAULT '',
|
||||
tx_pwr REAL, -- nullable: not always known
|
||||
is_active INTEGER NOT NULL DEFAULT 0, -- 1 for the currently-selected profile
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now'))
|
||||
);
|
||||
|
||||
-- Only one profile can be active at a time. Enforced lazily — the Go side
|
||||
-- clears all then sets one before each switch.
|
||||
CREATE INDEX idx_station_profiles_active ON station_profiles(is_active);
|
||||
Reference in New Issue
Block a user