diff --git a/CHANGELOG.md b/CHANGELOG.md index 75402326..975c3a44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Features * Web/Data: Reticulum gets its own visual identity - trifoil marker on violet `#7b61ff`, MeshCore's tile moves to `#1f2937`, a four-step role color ramp, hexagon shape, and a legend group under MeshCore (SPEC RD1-RD8) * Data: Reticulum node roles are derived from the announce aspect, so a multi-aspect peer no longer flips roles (SPEC RD4/RE10) +* Data/Web: the Reticulum host appears on the map at the position set by the `latitude`, `longitude` and `height` keys of its RNS config's first RNodeInterface block (SPEC RP1-RP10); keys already in that block are published after upgrading; remove them and restart the ingestor to stop (README Reticulum) * Web: the federation table renders a Reticulum zero count as a dash instead of a tile (SPEC RD8) * Web: the map legend stacks Reticulum under MeshCore, two columns instead of three (SPEC LS1) * Web: mesh.dmz.pt joins the default federation seed nodes and potatomesh.jmrp.io leaves them diff --git a/README.md b/README.md index a319f49c..fb988b6a 100644 --- a/README.md +++ b/README.md @@ -365,7 +365,8 @@ when not already set. Reticulum support is experimental and announce-only. Set `PROTOCOL=reticulum` to file each announce on a Reticulum (RNS) network as a node. Messages, -positions and telemetry are not ingested. +telemetry and other nodes' positions are not ingested. The ingestor publishes +only its own host's position, from your RNS config (below). The ingestor sends no announces, messages or polls, so `TX_ENABLED=0` (the default) is fine. `TX_ENABLED` does not gate the RNS stack, which transmits @@ -438,13 +439,36 @@ Meshtastic mesh; otherwise it shows `SF8/BW125/CR5`. Values are read once at startup - change them in the RNS config and restart the ingestor, or the dashboard keeps showing the old ones. +The ingestor publishes the `latitude`, `longitude` and `height` (metres) keys +of the first `RNodeInterface` in your RNS config as this host's position, at +startup and every hour, and the host appears on the map. If that block already +sets them, for example for RNS interface discovery, they are published after +the upgrade. Add them to that block and restart the ingestor: + +``` + latitude = 52.5029 + longitude = 13.4042 + height = 34 +``` + +Remove the keys and restart the ingestor to stop the updates. The dashboard +keeps the last published position until you clear it; replace `27716218` +with the node id from the startup line, without its `!`: + +```bash +sqlite3 ~/.local/share/potato-mesh/mesh.db "UPDATE nodes SET latitude = NULL, longitude = NULL, altitude = NULL, position_time = NULL, location_source = NULL WHERE node_id = char(33) || '27716218'; DELETE FROM positions WHERE node_id = char(33) || '27716218';" +``` + +In Docker, prefix it with `docker compose exec web` and use the database at +`/app/.local/share/potato-mesh/mesh.db`. + A node is one row per identity - a peer running both LXMF and a nomadnet node is still one entry. Its destinations (addresses) are listed via `GET /api/destinations` and shown as sub-rows grouped under the identity in the dashboard table. -Reticulum nodes show dashes for battery and position, stay off the map, and -have no messages. +Reticulum nodes show dashes for battery and have no messages. Other than this +host, they also show dashes for position and stay off the map. ### Passive UDP transport diff --git a/data/mesh_ingestor/CONTRACTS.md b/data/mesh_ingestor/CONTRACTS.md index 3df195ea..8050e0fc 100644 --- a/data/mesh_ingestor/CONTRACTS.md +++ b/data/mesh_ingestor/CONTRACTS.md @@ -420,8 +420,8 @@ from the operator's own stack. - Source: the first `RNodeInterface` block of the shared RNS config (`frequency`, `bandwidth`, `spreadingfactor`, `codingrate`). Only those four - keys are read - the same file holds `rpc_key`, which is - never read or logged. + keys, and the host position keys below, are read - the same file holds + `rpc_key`, which is never read or logged. - RNS stores both frequencies in Hz; they are emitted as MHz and kHz. - `RETICULUM_FREQ` / `RETICULUM_PRESET` override the parsed values. - The preset is a Meshtastic preset name when the BW/SF/CR triple matches @@ -467,6 +467,50 @@ connection that never recycles. 7 days after its timestamp unless traffic flows through it), leaves the 0-hop table and is no longer refreshed. +### Reticulum host position (SPEC RP1-RP6) + +Announces carry no position, so the provider publishes one for its own host +only, from the operator's own RNS config. + +- Source: `latitude`, `longitude` and, if present, `height` (metres) in the + first `RNodeInterface` block of the shared RNS config, found by the same + block reader as the radio metadata above. Read whether or not `discoverable` + is set. `location_cmd` is never run, and no other key leaves the parser. +- Read once per connect, as RNS's own config parser reads it: a key counts + only under its exact name once one pair of quotes is stripped from it + (`Latitude` is not read), and one matching pair of single or double quotes + is stripped from each value. The radio metadata keeps its own rule: + case-insensitive keys, values as written. No coordinates means no position. An + invalid coordinate means no position and one warning: one coordinate without + the other, a latitude or longitude that is not a finite number, a latitude + beyond 90 or a longitude beyond 180 degrees, or `0, 0`. A `height` that is + not a finite number drops only the altitude, with one warning that names the + key but not the value. +- Published as written: no rounding and no `precisionBits`. +- Node records: every record keyed on the registered host id, in the connect + snapshot and in every self-node report, carries `position` = + `{latitude, longitude, altitude?, time, locationSource: "LOC_MANUAL"}`, with + `time` the report time. No other node's record carries one. +- Bare host record: when no record carries the registered host id, because + nothing on the stack announces (Docker's default volume), one record + `{nodeId, lastHeard, protocol, user: {shortName, longName, role: "PEER"}}` + named with the node's own placeholder carries the position. It has no + `destination`, and there is none without a position. +- Positions rows: each self-node report, the first at connect and then hourly, + posts one `POST /api/positions` row for the host: `id` (the first 7 bytes of + SHA-256 over `reticulum::