diff --git a/docs/Planned-Features.md b/docs/Planned-Features.md index 4b628f7a..7419c443 100644 --- a/docs/Planned-Features.md +++ b/docs/Planned-Features.md @@ -322,3 +322,150 @@ trx-configurator | `trx-app` | Config types and validation | Yes | | `serialport` | Serial port enumeration | Yes (transitive) | | `soapysdr` | SDR device enumeration (optional) | Yes (feature-gated) | + +--- + +## Logbook and Ham Radio Layout + +Two halves of one feature ([#54](https://git.haxx.space/sjg/trx-rs/issues/54)): a station +logbook that speaks ADIF, and an operator layout that puts a transceiver's controls and that +logbook on one screen. + +Nothing in the application records a QSO today. The map's "QSO summary" cards describe +contacts *between other stations*, reconstructed from decoded traffic; bookmarks are +frequencies, not contacts. Neither is the operator's own log. + +### Requirements + +| ID | Description | +|----|-------------| +| REQ-LOG-001 | The system shall record QSOs the operator makes, each holding at minimum callsign, date, time, band, frequency, mode and both signal reports. | +| REQ-LOG-002 | When logging a QSO, the system shall pre-fill frequency, band, mode and time from the selected rig, and the station's own callsign and locator from configuration. | +| REQ-LOG-003 | The system shall allow a logged QSO to be edited and deleted. | +| REQ-LOG-004 | The system shall survive a crash without losing a QSO that was recorded before it. | +| REQ-LOG-005 | The system shall list, search and filter the log by callsign, band, mode and date range. | +| REQ-ADIF-001 | The system shall export the log as an ADIF 3.1.x `.adi` file. | +| REQ-ADIF-002 | The system shall import ADIF `.adi` files produced by other logging software, preserving fields it does not itself use. | +| REQ-ADIF-003 | When importing, the system shall identify QSOs already held and shall not duplicate them. | +| REQ-LOG-006 | Where a decoded station is on screen, the system shall offer to start a log entry from it, pre-filled, without logging it unattended. | +| REQ-LOG-007 | The system shall show whether a callsign has been worked before, and on which bands. | +| REQ-LAY-001 | The system shall offer a "Ham radio" operator layout presenting the transceiver controls and the logbook together. | +| REQ-LAY-002 | Where the selected rig cannot transmit, the system shall not offer the ham layout. | + +### A decode is not a QSO + +The decoders are receive-only: FT8, CW, APRS and the rest report what was *heard*. A heard +callsign is the beginning of a log entry, not a contact, and the logbook must never write one +by itself — REQ-LOG-006 says offer and pre-fill, never auto-log. Digital QSOs made in +WSJT-X or similar arrive the way every other logger takes them: through ADIF import. + +### Architecture + +#### New crate: `trx-logbook` + +``` +src/trx-client/ + trx-logbook/ + src/ + lib.rs # LogbookHandle: open, add, edit, delete, query, import, export + qso.rs # Qso: the record, its ADIF field mapping, band/mode helpers + adif.rs # ADI reader and writer (pure Rust, no new dependencies) + store.rs # Append-only JSON Lines file, in-memory index, compaction + dedupe.rs # Worked-before and import-collision rules +``` + +A library crate under `src/trx-client/`, beside `trx-frontend`, consumed by +`trx-frontend-http`. The log belongs to the station rather than to a rig or a frontend, so it +sits where any frontend can reach it. + +#### Storage: append-only, not a rewritten blob + +Bookmarks use `PickleDb` with `AutoDump`, which rewrites the whole file on every write. That +is right for a few dozen bookmarks and wrong for a log: a station with 40 000 QSOs would +rewrite several megabytes to log one contact, and lose the lot if the power went during the +dump. + +The log is instead a JSON Lines file — the shape `trx-decode-log` already uses — at +`~/.config/trx-rs/logbook.jsonl`, appended one record per write and read into an in-memory +index at startup. An edit or a delete appends a new revision of that record's id; the load +keeps the last one, and a compaction pass rewrites the file when superseded records exceed a +threshold. Appending is O(1) and atomic per line, so a crash costs at most the line being +written. + +#### ADIF in-repo + +ADI is a tagged text format: `value`, records ended by ``, a header ended +by ``, everything outside a tag ignored. It is small enough to implement exactly, which +this project already prefers for its decoders, and doing so keeps the dependency list where it +is. The reader must be lenient in the ways real files are irregular — lowercase tags, CRLF, +missing header, unknown fields, type indicators — and the writer strict. Unknown fields are +carried through import to export unchanged, so a round trip through trx-rs does not quietly +strip what another logger wrote. ADX (the XML form) is out of scope. + +#### Integration points + +| Source | What it gives the log | How | +|--------|----------------------|-----| +| `RigState` | `FREQ`, `BAND`, `MODE`/`SUBMODE`, and the rig id a QSO was made on | watch channel already in the frontend context | +| Client config `general.callsign` | `STATION_CALLSIGN`, `OPERATOR` | already surfaced as `owner_callsign` in frontend meta | +| Rig latitude/longitude | `MY_GRIDSQUARE` | `latLonToMaidenhead`, already in the frontend | +| Decoder panels and map | a pre-filled entry: callsign, grid, and the report to offer | existing decode history; no new plumbing | +| `bandForHz` | `BAND` from a frequency | exists in `map-core.ts`; move to a shared module | + +#### HTTP API + +| Method | Path | Purpose | +|--------|------|---------| +| `GET` | `/logbook` | Query: filters, paging | +| `POST` | `/logbook` | Add a QSO | +| `PUT` | `/logbook/{id}` | Edit | +| `DELETE` | `/logbook/{id}` | Delete | +| `GET` | `/logbook/export.adi` | Export, honouring the current filter | +| `POST` | `/logbook/import` | Import, answering with counts: added, duplicate, rejected | +| `GET` | `/logbook/worked/{call}` | Worked-before: bands and modes | + +Writes require the control role, as the rig endpoints do. + +### Frontend + +A **Logbook tab** joins the tab order: an entry form that opens pre-filled, a table with the +filters of REQ-LOG-005, and import/export. Worked-before shows against the callsign as it is +typed. + +The **ham layout** is a fifth entry in the operator layouts (`compact`, `broadcast`, `digital`, +`full`), which already gate on capability, seed the disclosure sections and persist per rig: + +```ts +ham: { + label: "Ham radio", + unavailable: "Ham radio needs a rig that can transmit", + advanced: true, audio: true, scheduler: false, + preferredTab: "logbook", capability: "ham", +} +``` + +with the `ham` capability set from `RigCapabilities.tx`. It keeps frequency, VFO, mode, filter, +PTT, power and the meters; it hides the broadcast furniture; and it puts the log beside the +spectrum rather than a tab away. + +### Phases + +| Phase | Lands | +|-------|-------| +| 1 | `trx-logbook`: `Qso`, the ADI reader and writer, round-trip tests against files from other loggers | +| 2 | Store, dedupe, and the 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 and LoTW/eQSL fields, contest exchange fields, per-band worked/confirmed statistics | + +### Open questions + +- One station log, or one per rig? The proposal assumes one, with the rig id recorded on each + QSO, since a callsign worked on the second rig is still worked. +- Multiple operators at one station: `OPERATOR` per QSO, or per session? +- Clock: the client's or the server's? The server's is proposed — it is the machine at the + radio — with the offset shown if the browser disagrees by more than a second. +- Should the log file be configurable, or fixed beside the bookmarks? +- Import collisions: the proposed key is callsign, band, mode and time to the minute. Contest + operators work the same station twice in a minute on different bands, which that key allows; + a same-band dupe inside a minute is treated as the same QSO.