[docs](trx-rs): settle the logbook's remaining questions
The clock is the server's, as asked: it is the machine at the radio, where the browser may be a phone in another timezone with a clock nobody checked. When the two disagree by more than a second the panel says so, rather than logging a time the operator did not expect. The rest, decided against how logging is actually done: 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 — these rigs can be in different places, so MY_GRIDSQUARE comes from the one that made the contact, which is what LoTW's station locations expect too. The operator is a per-QSO field set once per session. ADIF separates the callsign used on the air from the person at the key, and multi-operator stations rotate people through one station callsign. It defaults from the configured callsign, so a single operator never touches it. It cannot come from the session: the auth roles are control and rx, with nobody's name on them. The log file is configurable, defaulting to the user's data directory. Bookmarks sit in the config directory because they are settings and decode logs in the cache directory because they are disposable; a QSO log is irreplaceable, and cache directories get swept. Import collisions match on callsign, band, mode and a two-minute window. Loggers rarely agree to the second on the same QSO — one stamps the contact, the other the entry — so an exact-minute key duplicates half of what it is 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 midnight matches, and modes are normalised or an imported SSB would miss our USB. That normalisation is now written down: a rig mode is not an ADIF mode. DIG is the one the rig cannot answer — a rig in DIG is in FT8 or FT4 depending on what is decoding — and WSPR never opens an entry at all, because hearing a beacon is not a contact. Refs #54 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7 Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
+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