Proposal for both halves is up as a branch: docs/logbook-ham-layout-proposal — a new Logbook and Ham Radio Layout section in docs/Planned-Features.md, in the same shape as the Recorder plan. Summary, and the three decisions I would like your call on.
What exists to build on
The operator layouts (compact, broadcast, digital, full) already gate on rig capability, seed the disclosure sections and persist per rig. "Ham radio" is a fifth entry, gated on RigCapabilities.tx, preferring a new Logbook tab.
Nothing logs a QSO today. The map's "QSO summary" describes contacts between other stations reconstructed from decodes; bookmarks are frequencies. Neither is the operator's log.
Frequency → band, station callsign, rig locator and rig state are all already plumbed to the frontend, so a log entry can open pre-filled without new plumbing.
Three decisions worth taking now
1. A decode is not a QSO. The decoders are receive-only — a heard callsign is the start of a log entry, not a contact. So a decode row offers a pre-filled entry and never writes one. Digital QSOs made in WSJT-X come in the way every logger takes them: ADIF import.
2. Append-only storage, not the bookmark store. Bookmarks use PickleDb with AutoDump, which rewrites the whole file per write. Fine for dozens of bookmarks; wrong for a log — 40 000 QSOs means rewriting megabytes to log one contact, and losing all of it if the power goes mid-dump. The log is JSON Lines (the shape trx-decode-log already uses), appended one record per write, edits appended as revisions, compacted when superseded records pile up.
3. ADIF in-repo. ADI is <FIELD:length>value with <EOR> between records — small enough to implement exactly, which this project already prefers for its decoders, and it keeps the dependency list where it is. Lenient reader (lowercase tags, CRLF, missing header, unknown fields), strict writer, and unknown fields carried through import to export so a round trip does not quietly strip what another logger wrote. ADX (XML) out of scope.
Phases
trx-logbook crate: the Qso record, ADI reader/writer, round-trip tests against files from other loggers
One station log, or one per rig? Proposal assumes one, with the rig id on each QSO.
Multiple operators: OPERATOR per QSO, or per session?
Clock: server's (the machine at the radio) or the browser's? Proposal takes the server's.
Log file location: fixed beside the bookmarks, or configurable?
Import collision key: callsign + band + mode + time-to-the-minute. That lets a contest operator work the same station twice in a minute on different bands, and treats a same-band repeat inside a minute as one QSO. Right call?
No code yet — say which way you want the open questions answered and I will start on phase 1.
Proposal for both halves is up as a branch: `docs/logbook-ham-layout-proposal` — a new **Logbook and Ham Radio Layout** section in `docs/Planned-Features.md`, in the same shape as the Recorder plan. Summary, and the three decisions I would like your call on.
## What exists to build on
- The **operator layouts** (`compact`, `broadcast`, `digital`, `full`) already gate on rig capability, seed the disclosure sections and persist per rig. "Ham radio" is a fifth entry, gated on `RigCapabilities.tx`, preferring a new Logbook tab.
- Nothing logs a QSO today. The map's "QSO summary" describes contacts *between other stations* reconstructed from decodes; bookmarks are frequencies. Neither is the operator's log.
- Frequency → band, station callsign, rig locator and rig state are all already plumbed to the frontend, so a log entry can open pre-filled without new plumbing.
## Three decisions worth taking now
**1. A decode is not a QSO.** The decoders are receive-only — a heard callsign is the start of a log entry, not a contact. So a decode row *offers* a pre-filled entry and never writes one. Digital QSOs made in WSJT-X come in the way every logger takes them: ADIF import.
**2. Append-only storage, not the bookmark store.** Bookmarks use PickleDb with AutoDump, which rewrites the whole file per write. Fine for dozens of bookmarks; wrong for a log — 40 000 QSOs means rewriting megabytes to log one contact, and losing all of it if the power goes mid-dump. The log is JSON Lines (the shape `trx-decode-log` already uses), appended one record per write, edits appended as revisions, compacted when superseded records pile up.
**3. ADIF in-repo.** ADI is `<FIELD:length>value` with `<EOR>` between records — small enough to implement exactly, which this project already prefers for its decoders, and it keeps the dependency list where it is. Lenient reader (lowercase tags, CRLF, missing header, unknown fields), strict writer, and unknown fields carried through import to export so a round trip does not quietly strip what another logger wrote. ADX (XML) out of scope.
## Phases
1. `trx-logbook` crate: the `Qso` record, ADI reader/writer, round-trip tests against files from other loggers
2. Store, dedupe, HTTP API behind the control role
3. Logbook tab: entry, table, filters, import, export
4. Ham layout, pre-filled entry from a decode row or the map, worked-before
5. Optional: QSL/LoTW/eQSL fields, contest exchange, per-band worked/confirmed statistics
## Open questions for you
- One station log, or one per rig? Proposal assumes one, with the rig id on each QSO.
- Multiple operators: `OPERATOR` per QSO, or per session?
- Clock: server's (the machine at the radio) or the browser's? Proposal takes the server's.
- Log file location: fixed beside the bookmarks, or configurable?
- Import collision key: callsign + band + mode + time-to-the-minute. That lets a contest operator work the same station twice in a minute on different bands, and treats a same-band repeat inside a minute as one QSO. Right call?
No code yet — say which way you want the open questions answered and I will start on phase 1.
Revised on docs/logbook-ham-layout-proposal (1794f89). All three points folded in.
Panel and layout
The logbook is a panel of its own — a logbook tab beside Bookmarks — and it stands in every layout, so a log can be kept without adopting the ham layout and read while another one is selected. Three parts: the station line (own callsign, locator, the rig the QSO would be logged against), the entry form, then the log.
The ham layout is then the arrangement that starts from that panel, with the radio controls around it rather than the other way round. Fifth entry in the existing operator layouts, gated on RigCapabilities.tx.
Prefill: exactly six, and no more
Field
From
ADIF
Frequency
the rig's dial
FREQ, BAND derived
Mode
the rig
MODE, SUBMODE where implied
Rig name
the rig's display name
MY_RIG
Time
the clock when the entry opens
QSO_DATE, TIME_ON
Callsign
the decode row or map station it was started from, else empty
CALL
Locator
that station's grid where the decode carried one, else empty
GRIDSQUARE
Everything else stays empty. A signal report especially: an FT8 SNR is not what was sent, and pre-filling one would put a number in the log that nobody exchanged.
One consequence worth naming — your own callsign and locator are not per-entry fields at all. They are station identity, taken from configuration when the QSO is written (STATION_CALLSIGN, OPERATOR, MY_GRIDSQUARE) and shown once at the top of the panel, rather than typed into every row.
Formats: ADIF stays, Cabrillo joins it
You left the format to me, so here is the choice and the reasoning.
ADIF is not one option among several. LoTW, eQSL, Club Log, QRZ.com and every other logger read it and nothing else. A log that cannot write .adi cannot be uploaded, confirmed, or moved to another program — that is a one-way door, and interoperability is most of why a log is kept. So it stays. Note nothing on disk is ADI anyway: the store is JSON Lines, and ADIF is what comes out of an export. If the tagged text was the objection, it never touches your filesystem unless you export.
The second format is Cabrillo 3.0, which ADIF genuinely cannot replace: contest logs are submitted to sponsors in Cabrillo and rejected in anything else. Header lines (CALLSIGN:, CONTEST:, CATEGORY-*, CLAIMED-SCORE:) then one fixed-column QSO: line per contact — frequency in kHz, a mode code, UTC date and time, both calls, reports and exchanges. Export-only, and it lands in phase 5 with the contest exchange fields, since without a serial or a zone to put in the exchange there is nothing for it to write.
I considered ADX (ADIF's XML form) as the alternative and rejected it: same data model, different syntax, and almost nothing reads it — the cost of ADI without the interop.
Still open
The earlier questions stand: one station log or one per rig (proposal: one, rig id per QSO), operator per QSO or per session, server clock or browser clock (proposal: server — it is the machine at the radio), log file fixed beside the bookmarks or configurable, and the import collision key.
Answer those and I will start phase 1 — the trx-logbook crate with the ADI reader and writer and round-trip tests against files from other loggers.
Revised on `docs/logbook-ham-layout-proposal` (1794f89). All three points folded in.
## Panel and layout
The logbook is **a panel of its own** — a `logbook` tab beside Bookmarks — and it stands in every layout, so a log can be kept without adopting the ham layout and read while another one is selected. Three parts: the station line (own callsign, locator, the rig the QSO would be logged against), the entry form, then the log.
The **ham layout** is then the arrangement that *starts* from that panel, with the radio controls around it rather than the other way round. Fifth entry in the existing operator layouts, gated on `RigCapabilities.tx`.
## Prefill: exactly six, and no more
| Field | From | ADIF |
|---|---|---|
| Frequency | the rig's dial | `FREQ`, `BAND` derived |
| Mode | the rig | `MODE`, `SUBMODE` where implied |
| Rig name | the rig's display name | `MY_RIG` |
| Time | the clock when the entry opens | `QSO_DATE`, `TIME_ON` |
| Callsign | the decode row or map station it was started from, else empty | `CALL` |
| Locator | that station's grid where the decode carried one, else empty | `GRIDSQUARE` |
Everything else stays empty. A signal report especially: an FT8 SNR is not what was sent, and pre-filling one would put a number in the log that nobody exchanged.
One consequence worth naming — your own callsign and locator are not per-entry fields at all. They are station identity, taken from configuration when the QSO is written (`STATION_CALLSIGN`, `OPERATOR`, `MY_GRIDSQUARE`) and shown once at the top of the panel, rather than typed into every row.
## Formats: ADIF stays, Cabrillo joins it
You left the format to me, so here is the choice and the reasoning.
**ADIF is not one option among several.** LoTW, eQSL, Club Log, QRZ.com and every other logger read it and nothing else. A log that cannot write `.adi` cannot be uploaded, confirmed, or moved to another program — that is a one-way door, and interoperability is most of why a log is kept. So it stays. Note nothing on disk is ADI anyway: the store is JSON Lines, and ADIF is what comes *out* of an export. If the tagged text was the objection, it never touches your filesystem unless you export.
**The second format is Cabrillo 3.0**, which ADIF genuinely cannot replace: contest logs are submitted to sponsors in Cabrillo and rejected in anything else. Header lines (`CALLSIGN:`, `CONTEST:`, `CATEGORY-*`, `CLAIMED-SCORE:`) then one fixed-column `QSO:` line per contact — frequency in kHz, a mode code, UTC date and time, both calls, reports and exchanges. Export-only, and it lands in phase 5 with the contest exchange fields, since without a serial or a zone to put in the exchange there is nothing for it to write.
I considered ADX (ADIF's XML form) as the alternative and rejected it: same data model, different syntax, and almost nothing reads it — the cost of ADI without the interop.
## Still open
The earlier questions stand: one station log or one per rig (proposal: one, rig id per QSO), operator per QSO or per session, server clock or browser clock (proposal: server — it is the machine at the radio), log file fixed beside the bookmarks or configurable, and the import collision key.
Answer those and I will start phase 1 — the `trx-logbook` crate with the ADI reader and writer and round-trip tests against files from other loggers.
All questions closed out on docs/logbook-ham-layout-proposal (83b4531). Server clock as you said; the rest decided against how logging is actually done, with the reasoning in the doc so it can be argued with later.
One station log, not one per rig. DXCC, WAS and LoTW count the callsign, not the radio, and a station worked on the second rig is still worked. The rig goes on the QSO as MY_RIG. Station location does follow the rig though — trx-rs rigs can be in different places, so MY_GRIDSQUARE comes from the one that made the contact, which is also what LoTW's station locations expect.
Operator is a per-QSO field, set once per session. ADIF separates STATION_CALLSIGN (the call on the air) from OPERATOR (the person at the key), and multi-op stations rotate people through one station callsign — which is why contest loggers record it per QSO. Defaults from the configured callsign so a single operator never touches it. It can't come from the session: the auth roles are control and rx, with nobody's name on them.
Server clock, and the panel says when yours disagrees. UTC from the machine at the radio. If the browser's clock is off by more than a second the panel shows it, rather than logging a time you didn't expect.
Log file configurable, defaulting to the data directory. Bookmarks sit in the config directory because they're settings; decode logs in the cache directory because they're disposable. A QSO log is irreplaceable and cache directories get swept by cleaners — so [logbook].path, defaulting to dirs::data_dir()/trx-rs/logbook.jsonl, for stations that keep the log on a synced or backed-up volume.
Import collisions: callsign + band + mode + a two-minute window. Loggers rarely agree to the second on the same QSO — one stamps the contact, the other the entry — so the exact-minute key I first proposed would duplicate half of what it's asked to merge. Two minutes absorbs that without swallowing a legitimate re-work, since contest rules forbid a second contact on the same band and mode. Times compare as instants so a QSO either side of midnight matches, and modes are normalised first or an imported SSB would miss our USB.
That last point turned up something worth writing down separately: a rig mode is not an ADIF mode.USB/LSB are SSB with a submode; CWR is CW; SAM is AM. DIG is the one the rig cannot answer at all — a rig in DIG is in FT8 or FT4 depending on which decoder is running, so an entry started from an FT8 row logs FT8. And WSPR never opens an entry: hearing a beacon is not a contact. The mapping table is in the doc, to be checked against the ADIF enumeration when it's written, with anything unrecognised left to the operator rather than guessed into the log.
Ready to start phase 1 — the trx-logbook crate: the Qso record, the ADI reader and writer, and round-trip tests against files from other loggers. Say go and I'll open it as its own PR.
All questions closed out on `docs/logbook-ham-layout-proposal` (83b4531). Server clock as you said; the rest decided against how logging is actually done, with the reasoning in the doc so it can be argued with later.
**One station log, not one per rig.** DXCC, WAS and LoTW count the callsign, not the radio, and a station worked on the second rig is still worked. The rig goes on the QSO as `MY_RIG`. Station *location* does follow the rig though — trx-rs rigs can be in different places, so `MY_GRIDSQUARE` comes from the one that made the contact, which is also what LoTW's station locations expect.
**Operator is a per-QSO field, set once per session.** ADIF separates `STATION_CALLSIGN` (the call on the air) from `OPERATOR` (the person at the key), and multi-op stations rotate people through one station callsign — which is why contest loggers record it per QSO. Defaults from the configured callsign so a single operator never touches it. It can't come from the session: the auth roles are `control` and `rx`, with nobody's name on them.
**Server clock, and the panel says when yours disagrees.** UTC from the machine at the radio. If the browser's clock is off by more than a second the panel shows it, rather than logging a time you didn't expect.
**Log file configurable, defaulting to the data directory.** Bookmarks sit in the config directory because they're settings; decode logs in the cache directory because they're disposable. A QSO log is irreplaceable and cache directories get swept by cleaners — so `[logbook].path`, defaulting to `dirs::data_dir()/trx-rs/logbook.jsonl`, for stations that keep the log on a synced or backed-up volume.
**Import collisions: callsign + band + mode + a two-minute window.** Loggers rarely agree to the second on the same QSO — one stamps the contact, the other the entry — so the exact-minute key I first proposed would duplicate half of what it's asked to merge. Two minutes absorbs that without swallowing a legitimate re-work, since contest rules forbid a second contact on the same band and mode. Times compare as instants so a QSO either side of midnight matches, and modes are normalised first or an imported `SSB` would miss our `USB`.
That last point turned up something worth writing down separately: **a rig mode is not an ADIF mode.** `USB`/`LSB` are `SSB` with a submode; `CWR` is `CW`; `SAM` is `AM`. `DIG` is the one the rig cannot answer at all — a rig in `DIG` is in FT8 or FT4 depending on which decoder is running, so an entry started from an FT8 row logs `FT8`. And WSPR never opens an entry: hearing a beacon is not a contact. The mapping table is in the doc, to be checked against the ADIF enumeration when it's written, with anything unrecognised left to the operator rather than guessed into the log.
Ready to start phase 1 — the `trx-logbook` crate: the `Qso` record, the ADI reader and writer, and round-trip tests against files from other loggers. Say go and I'll open it as its own PR.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Proposal for both halves is up as a branch:
docs/logbook-ham-layout-proposal— a new Logbook and Ham Radio Layout section indocs/Planned-Features.md, in the same shape as the Recorder plan. Summary, and the three decisions I would like your call on.What exists to build on
compact,broadcast,digital,full) already gate on rig capability, seed the disclosure sections and persist per rig. "Ham radio" is a fifth entry, gated onRigCapabilities.tx, preferring a new Logbook tab.Three decisions worth taking now
1. A decode is not a QSO. The decoders are receive-only — a heard callsign is the start of a log entry, not a contact. So a decode row offers a pre-filled entry and never writes one. Digital QSOs made in WSJT-X come in the way every logger takes them: ADIF import.
2. Append-only storage, not the bookmark store. Bookmarks use PickleDb with AutoDump, which rewrites the whole file per write. Fine for dozens of bookmarks; wrong for a log — 40 000 QSOs means rewriting megabytes to log one contact, and losing all of it if the power goes mid-dump. The log is JSON Lines (the shape
trx-decode-logalready uses), appended one record per write, edits appended as revisions, compacted when superseded records pile up.3. ADIF in-repo. ADI is
<FIELD:length>valuewith<EOR>between records — small enough to implement exactly, which this project already prefers for its decoders, and it keeps the dependency list where it is. Lenient reader (lowercase tags, CRLF, missing header, unknown fields), strict writer, and unknown fields carried through import to export so a round trip does not quietly strip what another logger wrote. ADX (XML) out of scope.Phases
trx-logbookcrate: theQsorecord, ADI reader/writer, round-trip tests against files from other loggersOpen questions for you
OPERATORper QSO, or per session?No code yet — say which way you want the open questions answered and I will start on phase 1.
Revised on
docs/logbook-ham-layout-proposal(1794f89). All three points folded in.Panel and layout
The logbook is a panel of its own — a
logbooktab beside Bookmarks — and it stands in every layout, so a log can be kept without adopting the ham layout and read while another one is selected. Three parts: the station line (own callsign, locator, the rig the QSO would be logged against), the entry form, then the log.The ham layout is then the arrangement that starts from that panel, with the radio controls around it rather than the other way round. Fifth entry in the existing operator layouts, gated on
RigCapabilities.tx.Prefill: exactly six, and no more
FREQ,BANDderivedMODE,SUBMODEwhere impliedMY_RIGQSO_DATE,TIME_ONCALLGRIDSQUAREEverything else stays empty. A signal report especially: an FT8 SNR is not what was sent, and pre-filling one would put a number in the log that nobody exchanged.
One consequence worth naming — your own callsign and locator are not per-entry fields at all. They are station identity, taken from configuration when the QSO is written (
STATION_CALLSIGN,OPERATOR,MY_GRIDSQUARE) and shown once at the top of the panel, rather than typed into every row.Formats: ADIF stays, Cabrillo joins it
You left the format to me, so here is the choice and the reasoning.
ADIF is not one option among several. LoTW, eQSL, Club Log, QRZ.com and every other logger read it and nothing else. A log that cannot write
.adicannot be uploaded, confirmed, or moved to another program — that is a one-way door, and interoperability is most of why a log is kept. So it stays. Note nothing on disk is ADI anyway: the store is JSON Lines, and ADIF is what comes out of an export. If the tagged text was the objection, it never touches your filesystem unless you export.The second format is Cabrillo 3.0, which ADIF genuinely cannot replace: contest logs are submitted to sponsors in Cabrillo and rejected in anything else. Header lines (
CALLSIGN:,CONTEST:,CATEGORY-*,CLAIMED-SCORE:) then one fixed-columnQSO:line per contact — frequency in kHz, a mode code, UTC date and time, both calls, reports and exchanges. Export-only, and it lands in phase 5 with the contest exchange fields, since without a serial or a zone to put in the exchange there is nothing for it to write.I considered ADX (ADIF's XML form) as the alternative and rejected it: same data model, different syntax, and almost nothing reads it — the cost of ADI without the interop.
Still open
The earlier questions stand: one station log or one per rig (proposal: one, rig id per QSO), operator per QSO or per session, server clock or browser clock (proposal: server — it is the machine at the radio), log file fixed beside the bookmarks or configurable, and the import collision key.
Answer those and I will start phase 1 — the
trx-logbookcrate with the ADI reader and writer and round-trip tests against files from other loggers.All questions closed out on
docs/logbook-ham-layout-proposal(83b4531). Server clock as you said; the rest decided against how logging is actually done, with the reasoning in the doc so it can be argued with later.One station log, not one per rig. DXCC, WAS and LoTW count the callsign, not the radio, and a station worked on the second rig is still worked. The rig goes on the QSO as
MY_RIG. Station location does follow the rig though — trx-rs rigs can be in different places, soMY_GRIDSQUAREcomes from the one that made the contact, which is also what LoTW's station locations expect.Operator is a per-QSO field, set once per session. ADIF separates
STATION_CALLSIGN(the call on the air) fromOPERATOR(the person at the key), and multi-op stations rotate people through one station callsign — which is why contest loggers record it per QSO. Defaults from the configured callsign so a single operator never touches it. It can't come from the session: the auth roles arecontrolandrx, with nobody's name on them.Server clock, and the panel says when yours disagrees. UTC from the machine at the radio. If the browser's clock is off by more than a second the panel shows it, rather than logging a time you didn't expect.
Log file configurable, defaulting to the data directory. Bookmarks sit in the config directory because they're settings; decode logs in the cache directory because they're disposable. A QSO log is irreplaceable and cache directories get swept by cleaners — so
[logbook].path, defaulting todirs::data_dir()/trx-rs/logbook.jsonl, for stations that keep the log on a synced or backed-up volume.Import collisions: callsign + band + mode + a two-minute window. Loggers rarely agree to the second on the same QSO — one stamps the contact, the other the entry — so the exact-minute key I first proposed would duplicate half of what it's asked to merge. Two minutes absorbs that without swallowing a legitimate re-work, since contest rules forbid a second contact on the same band and mode. Times compare as instants so a QSO either side of midnight matches, and modes are normalised first or an imported
SSBwould miss ourUSB.That last point turned up something worth writing down separately: a rig mode is not an ADIF mode.
USB/LSBareSSBwith a submode;CWRisCW;SAMisAM.DIGis the one the rig cannot answer at all — a rig inDIGis in FT8 or FT4 depending on which decoder is running, so an entry started from an FT8 row logsFT8. And WSPR never opens an entry: hearing a beacon is not a contact. The mapping table is in the doc, to be checked against the ADIF enumeration when it's written, with anything unrecognised left to the operator rather than guessed into the log.Ready to start phase 1 — the
trx-logbookcrate: theQsorecord, the ADI reader and writer, and round-trip tests against files from other loggers. Say go and I'll open it as its own PR.