The logbook of issue #54, in the shape the proposal settled on. A new crate, trx-logbook, holds the contact, the ADIF reader and writer, the file, and the rules for telling one contact from two. ADIF because it is the only thing the ecosystem reads: LoTW, eQSL, Club Log, QRZ and every other logger take it and nothing else, so a log that cannot write .adi cannot be uploaded, confirmed or moved. The reader is forgiving in the ways real files are irregular -- lowercase tags, CRLF, missing header, unknown fields, a declared length that is the only thing ending a value -- and carries what it does not model through to the export, so a round trip does not strip what another program wrote. The file is JSON Lines, appended one line per contact. A log is the one thing here that cannot be regenerated, and the bookmark store's whole-file dump would rewrite megabytes to log one contact and lose all of them if the power went halfway; an append costs the record being written and no more, which a test tears a line in half to prove. Edits append revisions, deletes append tombstones, and the file compacts when the superseded outnumber the live. The panel is its own tab and stands in every layout. An entry opens with six fields and no more -- frequency, mode, rig name, time, and the callsign and locator of whatever decode it was started from. A report stays empty: an FT8 SNR is not what was sent. Times come from the server, because the browser may be a phone in another timezone, and the panel says so when the two disagree by more than a second. Worked-before answers as a callsign is typed. A decode is not a contact, so the Log button on an FT8 or APRS row opens an entry and logs nothing by itself. The ham layout is the fifth operator layout, opening on the logbook with the radio controls around it, offered only where the rig can transmit. Two bugs found on the way, both in code written here: a frequency of a whole number of megahertz ending in a zero rendered as a tenth of itself, in Rust and in TypeScript alike, because trimming trailing zeros from "20.000000" walks back through the point. The API also sits under /api/logbook rather than /logbook, so it cannot shadow its own page the way /bookmarks does. Closes #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>
253 lines
6.0 KiB
TOML
253 lines
6.0 KiB
TOML
# trx-rs example configuration
|
|
#
|
|
# Generated from the config structs; regenerate with:
|
|
# cargo run -p trx-config --example generate_example
|
|
#
|
|
# Both sections are optional: trx-server reads [trx-server], trx-client reads
|
|
# [trx-client], and either may live in its own file with the section header
|
|
# omitted. Any string may use ${ENV_VAR}, and credentials may be moved out of
|
|
# this file with the matching *_file keys.
|
|
#
|
|
# Check a config without starting anything:
|
|
# trx-server --check-config --config trx-rs.toml
|
|
# trx-client --check-config --config trx-rs.toml
|
|
|
|
# Server: drives the radio hardware.
|
|
[trx-server]
|
|
rigs = []
|
|
|
|
# Station identity. Coordinates feed PSKReporter and the map.
|
|
[trx-server.general]
|
|
callsign = "N0CALL"
|
|
log_level = "info"
|
|
latitude = 52.2297
|
|
longitude = 21.0122
|
|
|
|
# Single-rig layout. For several radios, delete this and use [[rigs]].
|
|
[trx-server.rig]
|
|
model = "ft817"
|
|
initial_freq_hz = 144300000
|
|
initial_mode = "USB"
|
|
|
|
# How to reach the radio: serial, tcp, or sdr.
|
|
[trx-server.rig.access]
|
|
type = "serial"
|
|
port = "/dev/ttyUSB0"
|
|
baud = 9600
|
|
|
|
# CAT polling and retry behaviour.
|
|
[trx-server.behavior]
|
|
poll_interval_ms = 500
|
|
poll_interval_tx_ms = 100
|
|
max_retries = 3
|
|
retry_base_delay_ms = 100
|
|
vfo_prime = true
|
|
|
|
# JSON control listener that trx-client connects to.
|
|
[trx-server.listen]
|
|
enabled = true
|
|
listen = "127.0.0.1"
|
|
port = 4530
|
|
|
|
# Tokens clients must present. Empty means no authentication.
|
|
# Use tokens_file = "/etc/trx-rs/tokens" to keep them out of this file.
|
|
[trx-server.listen.auth]
|
|
tokens = []
|
|
|
|
# Opus audio stream for trx-client.
|
|
[trx-server.audio]
|
|
enabled = true
|
|
listen = "127.0.0.1"
|
|
port = 4531
|
|
rx_enabled = true
|
|
tx_enabled = true
|
|
sample_rate = 48000
|
|
channels = 2
|
|
frame_duration_ms = 20
|
|
bitrate_bps = 256000
|
|
|
|
# Report FT8/FT4/WSPR spots to pskreporter.info.
|
|
[trx-server.pskreporter]
|
|
enabled = false
|
|
host = "report.pskreporter.info"
|
|
port = 4739
|
|
|
|
# Forward received APRS frames to APRS-IS.
|
|
[trx-server.aprsfi]
|
|
enabled = false
|
|
host = "rotate.aprs.net"
|
|
port = 14580
|
|
passcode = -1
|
|
beacon = false
|
|
beacon_interval_secs = 1200
|
|
beacon_symbol_table = "/"
|
|
beacon_symbol_code = "-"
|
|
|
|
# Write decodes to JSON Lines files.
|
|
[trx-server.decode_logs]
|
|
enabled = false
|
|
dir = "/var/lib/trx-rs/decoders"
|
|
aprs_file = "TRXRS-APRS-%YYYY%-%MM%-%DD%.log"
|
|
cw_file = "TRXRS-CW-%YYYY%-%MM%-%DD%.log"
|
|
ft8_file = "TRXRS-FT8-%YYYY%-%MM%-%DD%.log"
|
|
wspr_file = "TRXRS-WSPR-%YYYY%-%MM%-%DD%.log"
|
|
wefax_file = "TRXRS-WEFAX-%YYYY%-%MM%-%DD%.log"
|
|
|
|
# Which decoders run. Trimming this list saves real CPU on small boxes.
|
|
# Valid names: aprs, aprs_hf, ais, cw, ft2, ft4, ft8, lrpt, sstv, vdes, wefax, wspr.
|
|
# output_dir sets where sstv/wefax/lrpt write images (default: user cache dir).
|
|
[trx-server.decoders]
|
|
enabled = [
|
|
"aprs",
|
|
"aprs_hf",
|
|
"ais",
|
|
"cw",
|
|
"ft2",
|
|
"ft4",
|
|
"ft8",
|
|
"lrpt",
|
|
"sstv",
|
|
"vdes",
|
|
"wefax",
|
|
"wspr",
|
|
]
|
|
|
|
# SoapySDR pipeline; used when [rig.access] type = "sdr".
|
|
[trx-server.sdr]
|
|
sample_rate = 1920000
|
|
bandwidth = 1500000
|
|
wfm_deemphasis_us = 50
|
|
center_offset_hz = 100000
|
|
channels = []
|
|
max_virtual_channels = 4
|
|
spectrum_fft_size = 1024
|
|
spectrum_interval_ms = 50
|
|
|
|
# "auto" for hardware AGC, or "manual".
|
|
[trx-server.sdr.gain]
|
|
mode = "auto"
|
|
value = 30.0
|
|
|
|
# Software squelch on demodulated audio.
|
|
[trx-server.sdr.squelch]
|
|
enabled = false
|
|
threshold_db = -65.0
|
|
hysteresis_db = 3.0
|
|
tail_ms = 180
|
|
|
|
# Impulse-noise suppression on the IQ stream.
|
|
[trx-server.sdr.noise_blanker]
|
|
enabled = false
|
|
threshold = 10.0
|
|
|
|
# Timeout and buffer tuning. The defaults suit most setups.
|
|
[trx-server.timeouts]
|
|
command_exec_timeout_ms = 10000
|
|
poll_refresh_timeout_ms = 8000
|
|
io_timeout_ms = 10000
|
|
request_timeout_ms = 12000
|
|
rig_task_channel_buffer = 32
|
|
|
|
# Client: exposes the radio to users.
|
|
[trx-client]
|
|
|
|
# Labels shown in the web UI.
|
|
[trx-client.general]
|
|
callsign = "N0CALL"
|
|
website_url = "https://haxx.space"
|
|
website_name = "haxx.space"
|
|
ais_vessel_url_base = "https://www.vesselfinder.com/?mmsi="
|
|
log_level = "info"
|
|
|
|
# Legacy single-remote form; prefer [[remotes]] below.
|
|
[trx-client.remote]
|
|
poll_interval_ms = 750
|
|
spectrum_interval_ms = 50
|
|
|
|
[trx-client.remote.auth]
|
|
|
|
[[trx-client.remotes]]
|
|
name = "home-hf"
|
|
url = "192.168.1.100:4530"
|
|
rig_id = "hf"
|
|
poll_interval_ms = 750
|
|
spectrum_interval_ms = 50
|
|
|
|
[trx-client.remotes.auth]
|
|
token = "my-token"
|
|
|
|
[[trx-client.remotes]]
|
|
name = "home-vhf"
|
|
url = "192.168.1.100:4530"
|
|
rig_id = "vhf"
|
|
poll_interval_ms = 750
|
|
spectrum_interval_ms = 50
|
|
|
|
[trx-client.remotes.auth]
|
|
token = "my-token"
|
|
|
|
# Web UI. default_rig_name and the per-rig maps are keyed by the
|
|
# [[remotes]] name, not the server-side rig id.
|
|
[trx-client.frontends.http]
|
|
enabled = true
|
|
listen = "127.0.0.1"
|
|
port = 8080
|
|
default_rig_name = "home-hf"
|
|
initial_map_zoom = 10
|
|
spectrum_coverage_margin_hz = 50000
|
|
spectrum_usable_span_ratio = 0.9200000166893005
|
|
show_sdr_gain_control = true
|
|
bandplan_enabled = true
|
|
bandplan_region = "iaru_r1"
|
|
decode_history_retention_min = 1440
|
|
|
|
[trx-client.frontends.http.decode_history_retention_min_by_rig]
|
|
|
|
# Passphrase login for the web UI. rx_passphrase_file and
|
|
# control_passphrase_file keep the secrets out of this file.
|
|
[trx-client.frontends.http.auth]
|
|
enabled = false
|
|
rx_passphrase = "rx-passphrase-example"
|
|
control_passphrase = "control-passphrase-example"
|
|
tx_access_control_enabled = true
|
|
session_ttl_min = 480
|
|
cookie_secure = false
|
|
cookie_same_site = "Lax"
|
|
|
|
# Hamlib-compatible TCP interface, one listener per rig.
|
|
[trx-client.frontends.rigctl]
|
|
enabled = false
|
|
listen = "127.0.0.1"
|
|
port = 4532
|
|
|
|
[trx-client.frontends.rigctl.rig_ports]
|
|
|
|
# JSON-over-TCP control interface.
|
|
[trx-client.frontends.http_json]
|
|
enabled = true
|
|
listen = "127.0.0.1"
|
|
port = 0
|
|
|
|
[trx-client.frontends.http_json.auth]
|
|
tokens = []
|
|
|
|
# Where to fetch the audio stream from.
|
|
[trx-client.frontends.audio]
|
|
enabled = true
|
|
server_port = 4531
|
|
|
|
[trx-client.frontends.audio.rig_urls]
|
|
|
|
[trx-client.frontends.audio.rig_ports]
|
|
|
|
# Play RX audio on a local sound device and capture TX from one.
|
|
[trx-client.frontends.audio.bridge]
|
|
enabled = false
|
|
bitrate_bps = 192000
|
|
rx_gain = 1.0
|
|
tx_gain = 1.0
|
|
|
|
# Station log of contacts. path defaults to logbook.jsonl in the user's
|
|
# data directory; set it to keep the log on a backed-up volume.
|
|
[trx-client.logbook]
|