[feat](trx-logbook): work a contest, and record what came back #60
+68
-16
@@ -351,6 +351,7 @@ frequencies, not contacts. Neither is the operator's own log.
|
|||||||
| REQ-FMT-002 | The system shall import ADIF `.adi` files produced by other logging software, preserving fields it does not itself use. |
|
| REQ-FMT-002 | The system shall import ADIF `.adi` files produced by other logging software, preserving fields it does not itself use. |
|
||||||
| REQ-FMT-003 | When importing, the system shall identify QSOs already held and shall not duplicate them. |
|
| REQ-FMT-003 | When importing, the system shall identify QSOs already held and shall not duplicate them. |
|
||||||
| REQ-FMT-004 | The system shall export a filtered selection of the log as a Cabrillo 3.0 file for contest submission. |
|
| REQ-FMT-004 | The system shall export a filtered selection of the log as a Cabrillo 3.0 file for contest submission. |
|
||||||
|
| REQ-LOG-009 | The system shall stamp QSO times from the server's clock in UTC, and shall tell the operator when the browser's clock disagrees with it by more than one second. |
|
||||||
| REQ-LAY-001 | The system shall present the logbook in a panel of its own, reachable whatever layout is selected. |
|
| REQ-LAY-001 | The system shall present the logbook in a panel of its own, reachable whatever layout is selected. |
|
||||||
| REQ-LAY-002 | The system shall offer a "Ham radio" operator layout presenting the transceiver controls and that panel together. |
|
| REQ-LAY-002 | The system shall offer a "Ham radio" operator layout presenting the transceiver controls and that panel together. |
|
||||||
| REQ-LAY-003 | Where the selected rig cannot transmit, the system shall not offer the ham layout. |
|
| REQ-LAY-003 | Where the selected rig cannot transmit, the system shall not offer the ham layout. |
|
||||||
@@ -371,7 +372,7 @@ Six fields, and no more (REQ-LOG-002, REQ-LOG-003):
|
|||||||
| Frequency | the selected rig's dial | `FREQ`, with `BAND` derived from it |
|
| Frequency | the selected rig's dial | `FREQ`, with `BAND` derived from it |
|
||||||
| Mode | the selected rig | `MODE`, and `SUBMODE` where the mode implies one |
|
| Mode | the selected rig | `MODE`, and `SUBMODE` where the mode implies one |
|
||||||
| Rig name | the rig's display name | `MY_RIG` |
|
| Rig name | the rig's display name | `MY_RIG` |
|
||||||
| Time | the clock, at the moment the entry opens | `QSO_DATE`, `TIME_ON` |
|
| Time | the server's clock, UTC, when the entry opens | `QSO_DATE`, `TIME_ON` |
|
||||||
| Callsign | the decode row or map station the entry was started from, else empty | `CALL` |
|
| Callsign | the decode row or map station the entry was started from, else empty | `CALL` |
|
||||||
| Locator | that station's grid, where the decode carried one, else empty | `GRIDSQUARE` |
|
| Locator | that station's grid, where the decode carried one, else empty | `GRIDSQUARE` |
|
||||||
|
|
||||||
@@ -409,9 +410,8 @@ is right for a few dozen bookmarks and wrong for a log: a station with 40 000 QS
|
|||||||
rewrite several megabytes to log one contact, and lose the lot if the power went during the
|
rewrite several megabytes to log one contact, and lose the lot if the power went during the
|
||||||
dump.
|
dump.
|
||||||
|
|
||||||
The log is instead a JSON Lines file — the shape `trx-decode-log` already uses — at
|
The log is instead a JSON Lines file — the shape `trx-decode-log` already uses — appended one
|
||||||
`~/.config/trx-rs/logbook.jsonl`, appended one record per write and read into an in-memory
|
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
|
||||||
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
|
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
|
threshold. Appending is O(1) and atomic per line, so a crash costs at most the line being
|
||||||
written.
|
written.
|
||||||
@@ -451,8 +451,9 @@ put in the exchange there is nothing for it to write.
|
|||||||
| Source | What it gives the log | How |
|
| 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 |
|
| `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 |
|
| Client config `general.callsign` | `STATION_CALLSIGN`, and the default `OPERATOR` | already surfaced as `owner_callsign` in frontend meta |
|
||||||
| Rig latitude/longitude | `MY_GRIDSQUARE` | `latLonToMaidenhead`, already in the frontend |
|
| The QSO's own rig, and its position | `MY_GRIDSQUARE` | per-rig latitude and longitude already carried in the rig list |
|
||||||
|
| Server clock | `QSO_DATE`, `TIME_ON` in UTC | new `GET /logbook/now`, which also feeds the browser-clock check |
|
||||||
| Decoder panels and map | a pre-filled entry: callsign, grid, and the report to offer | existing decode history; no new plumbing |
|
| 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 |
|
| `bandForHz` | `BAND` from a frequency | exists in `map-core.ts`; move to a shared module |
|
||||||
|
|
||||||
@@ -468,6 +469,7 @@ put in the exchange there is nothing for it to write.
|
|||||||
| `GET` | `/logbook/export.cbr` | Cabrillo export of a contest selection |
|
| `GET` | `/logbook/export.cbr` | Cabrillo export of a contest selection |
|
||||||
| `POST` | `/logbook/import` | Import, answering with counts: added, duplicate, rejected |
|
| `POST` | `/logbook/import` | Import, answering with counts: added, duplicate, rejected |
|
||||||
| `GET` | `/logbook/worked/{call}` | Worked-before: bands and modes |
|
| `GET` | `/logbook/worked/{call}` | Worked-before: bands and modes |
|
||||||
|
| `GET` | `/logbook/now` | The server's UTC clock, for stamping entries and checking the browser's |
|
||||||
|
|
||||||
Writes require the control role, as the rig endpoints do.
|
Writes require the control role, as the rig endpoints do.
|
||||||
|
|
||||||
@@ -511,14 +513,64 @@ task and the rig is the instrument.
|
|||||||
| 4 | Ham layout, pre-filled entry from a decode row or the map, worked-before |
|
| 4 | Ham layout, pre-filled entry from a decode row or the map, worked-before |
|
||||||
| 5 | Contest exchange fields and Cabrillo export; QSL and LoTW/eQSL fields; per-band worked/confirmed statistics |
|
| 5 | Contest exchange fields and Cabrillo export; QSL and LoTW/eQSL fields; per-band worked/confirmed statistics |
|
||||||
|
|
||||||
### Open questions
|
### Decisions
|
||||||
|
|
||||||
- One station log, or one per rig? The proposal assumes one, with the rig id recorded on each
|
**One station log, not one per rig.** Awards and uploads are per station callsign — DXCC, WAS
|
||||||
QSO, since a callsign worked on the second rig is still worked.
|
and LoTW all count the callsign, not the radio — and a station worked on the second rig is
|
||||||
- Multiple operators at one station: `OPERATOR` per QSO, or per session?
|
still worked. The rig is recorded on the QSO (`MY_RIG`) rather than dividing the log by it.
|
||||||
- Clock: the client's or the server's? The server's is proposed — it is the machine at the
|
The station *location* fields do follow the rig, though: trx-rs rigs can be in different
|
||||||
radio — with the offset shown if the browser disagrees by more than a second.
|
places, so `MY_GRIDSQUARE` is taken from the rig that made the QSO rather than from one global
|
||||||
- Should the log file be configurable, or fixed beside the bookmarks?
|
setting, which is also what LoTW's station locations expect.
|
||||||
- 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;
|
**The operator is a per-QSO field, set once per session.** ADIF separates `STATION_CALLSIGN`
|
||||||
a same-band dupe inside a minute is treated as the same QSO.
|
(the call used on the air) from `OPERATOR` (the person at the key); multi-operator stations
|
||||||
|
rotate operators through one station callsign, which is why contest loggers record it per QSO.
|
||||||
|
It is stored per QSO, defaulted from the configured callsign so a single operator never touches
|
||||||
|
it, and changed on the station line at the top of the panel where it sticks for the session.
|
||||||
|
It cannot be taken from the session's identity: the auth roles are `control` and `rx`, with no
|
||||||
|
notion of who is logged in.
|
||||||
|
|
||||||
|
**Server clock, and the log says so.** The server is the machine at the radio; the browser may
|
||||||
|
be on a phone in another timezone with a clock nobody has checked. QSO times are UTC from the
|
||||||
|
server, and when a browser's clock disagrees by more than a second the panel says so rather
|
||||||
|
than silently logging a time the operator did not expect.
|
||||||
|
|
||||||
|
**The log file is configurable, and defaults to the user's data directory.** Bookmarks live in
|
||||||
|
the config directory because they are settings; decode logs live in the cache directory because
|
||||||
|
they are disposable. A QSO log is neither — it is irreplaceable, and cache directories are
|
||||||
|
swept by cleaners. `[logbook].path` in the client config, defaulting to
|
||||||
|
`dirs::data_dir()/trx-rs/logbook.jsonl`, so a station that keeps its log on a synced or
|
||||||
|
backed-up volume can say so.
|
||||||
|
|
||||||
|
**Import collisions: callsign, band, mode and a two-minute window.** Two loggers rarely agree
|
||||||
|
to the second on the same QSO — one records the time the contact started, another the time it
|
||||||
|
was entered — so an exact-minute key duplicates half of what it is asked to merge. Two minutes
|
||||||
|
absorbs that. It does not swallow legitimate re-works: contest rules forbid a second contact
|
||||||
|
with the same station on the same band and mode, so a repeat inside two minutes is the same
|
||||||
|
QSO. Times are compared as instants rather than date and time strings, so a QSO either side of
|
||||||
|
midnight matches. Modes are normalised before comparison, or a log that stored `SSB` would fail
|
||||||
|
to match ours that stored `USB`.
|
||||||
|
|
||||||
|
### Rig modes to ADIF modes
|
||||||
|
|
||||||
|
The rig reports what it is demodulating; ADIF wants what the contact was made on, which is not
|
||||||
|
always the same word:
|
||||||
|
|
||||||
|
| Rig mode | ADIF `MODE` | ADIF `SUBMODE` |
|
||||||
|
|----------|-------------|----------------|
|
||||||
|
| `USB`, `LSB` | `SSB` | `USB` / `LSB` |
|
||||||
|
| `CW`, `CWR` | `CW` | — |
|
||||||
|
| `AM`, `SAM` | `AM` | — |
|
||||||
|
| `FM`, `WFM` | `FM` | — |
|
||||||
|
| `PKT` | `PKT` | — |
|
||||||
|
| `DIG` | decided by the decoder in use, not by the rig | |
|
||||||
|
| `AIS`, `VDES` | none — not amateur modes, and these rigs do not log | |
|
||||||
|
| `Other(..)` | passed through when it names an ADIF mode, else left for the operator | |
|
||||||
|
|
||||||
|
`DIG` is the one that cannot come from the rig: a rig in `DIG` is in FT8, FT4 or something else
|
||||||
|
depending on which decoder is running, and an entry started from an FT8 row logs `FT8` rather
|
||||||
|
than the rig's word for it. WSPR never opens an entry at all — it is a beacon mode, and hearing
|
||||||
|
a beacon is not a contact.
|
||||||
|
|
||||||
|
The table is data in `qso.rs`, checked against the ADIF enumeration when it is written, with
|
||||||
|
anything unrecognised left to the operator rather than guessed into the log.
|
||||||
|
|||||||
Reference in New Issue
Block a user