@meri-imperiumi/signalk-dead-reckoning 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +180 -0
- package/README.md +37 -1
- package/SPEC.md +11 -3
- package/package.json +3 -2
- package/plugin/index.js +480 -64
- package/plugin/plotterext.js +194 -0
- package/plugin/polar.js +85 -0
- package/plugin/shadow-vessel.js +179 -0
- package/plugin/statustilesexamples.js +109 -0
- package/public/dr-app.js +55 -32
- package/public/dr-ext-model.js +84 -0
- package/public/dr-ext-widget.html +29 -0
- package/public/dr-ext-widget.js +275 -0
- package/public/dr-map-view.js +250 -36
- package/public/dr-sight-panel.js +11 -3
- package/public/dr-viewmodel.js +469 -17
- package/public/index.html +5 -0
- package/public/vendor/maplibre-gl/LICENSE-leaflet-maplibre-gl.txt +15 -0
- package/public/vendor/maplibre-gl/LICENSE-maplibre-gl.txt +116 -0
- package/public/vendor/maplibre-gl/README.md +19 -0
- package/public/vendor/maplibre-gl/leaflet-maplibre-gl.js +234 -0
- package/public/vendor/maplibre-gl/maplibre-gl.css +1 -0
- package/public/vendor/maplibre-gl/maplibre-gl.js +59 -0
- package/public/vendor/plotterext-bus/LICENSE +21 -0
- package/public/vendor/plotterext-bus/README.md +12 -0
- package/public/vendor/plotterext-bus/chunk-4W6N34SD.js +318 -0
- package/public/vendor/plotterext-bus/chunk-7XRFPDQL.js +267 -0
- package/public/vendor/plotterext-bus/extension.js +31 -0
- package/status-tiles-examples.json +120 -0
- package/tests/dr-current.test.js +2 -1
- package/tests/dr-ext-model.test.js +96 -0
- package/tests/dr-viewmodel.test.js +78 -4
- package/tests/fake-app.js +21 -0
- package/tests/plotterext.test.js +188 -0
- package/tests/plugin.test.js +426 -34
- package/tests/polar.test.js +106 -0
- package/tests/shadow-vessel-integration.test.js +201 -0
- package/tests/shadow-vessel.test.js +209 -0
- package/tests/statustilesexamples.test.js +174 -0
- package/tests/tile-paths.test.js +258 -0
- package/tests/vector-charts.test.js +516 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,9 +5,189 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.0] - 2026-08-28
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Example Status Tiles set (work doc #22)** — the plugin now ships a
|
|
12
|
+
ready-made Status Tiles set (`status-tiles-examples.json`) that a
|
|
13
|
+
boat owner copies into their panel with one tap — no JSON editing,
|
|
14
|
+
no hand-authoring predicates. The set exposes the underway DR
|
|
15
|
+
integrity tile: the sensor-health and divergence-advisory
|
|
16
|
+
notifications, the DR-override (`navigation.deadReckoning.active`)
|
|
17
|
+
state, and banded checks on the uncertainty radius (1 nm warn / 3 nm
|
|
18
|
+
crit) and time-since-fix (3 h / 6 h), with footer readouts for
|
|
19
|
+
uncertainty, DR–GPS divergence, and the DR method. It is advertised
|
|
20
|
+
through the standard resources API as a read-only
|
|
21
|
+
`statusTileExamples` provider (keyed by the plugin id, returning `{}`
|
|
22
|
+
when stopped so a disabled plugin contributes no stale sets),
|
|
23
|
+
which the signalk-status-tiles webapp discovers and offers as a
|
|
24
|
+
one-tap "Add" that merges into the user's config (skipping existing
|
|
25
|
+
ids — idempotent, never overwrites user edits). Mirrors the existing
|
|
26
|
+
plotter-extension provider pattern; no running server is required to
|
|
27
|
+
ship or consume it.
|
|
28
|
+
- **Shadow vessel on chart plotters (work doc #21)** — the DR
|
|
29
|
+
shadow-boat position is now published as a synthetic vessel on a
|
|
30
|
+
stable `vessels.<uuid>` context, so chart plotters that source
|
|
31
|
+
vessels from the Signal K stream (Freeboard-SK's AIS layer) render
|
|
32
|
+
it on the chart alongside the own vessel and AIS traffic, with a
|
|
33
|
+
projected COG line. Freeboard-SK draws it as a green `ais_buddy`
|
|
34
|
+
target — the only per-target data-driven visual lever in its AIS
|
|
35
|
+
style decision tree is the `buddy` flag, which the plugin sets on the
|
|
36
|
+
root-value delta. The gap between the shadow's heading (bow) and its
|
|
37
|
+
COG line visualizes set + leeway, mirroring how the plotter treats
|
|
38
|
+
the own vessel. Opt-in via *Shadow Vessel* in the plugin settings
|
|
39
|
+
(off by default); the configurable label defaults to "DR Shadow".
|
|
40
|
+
The plugin stays the source of truth — the shadow is an additional
|
|
41
|
+
chart-visible target, not a navigational authority. The context UUID
|
|
42
|
+
is generated once and persisted so the plotter's target id is
|
|
43
|
+
continuous across restarts. While DR is idle (moored, no speed or
|
|
44
|
+
heading) the shadow keeps publishing the last position at SOG 0 so
|
|
45
|
+
it doesn't vanish or go stale, and the COG line clears. No
|
|
46
|
+
Freeboard-SK change is required.
|
|
47
|
+
- **Vector chart tiles in the webapp map (work doc #20)** — charts
|
|
48
|
+
with `format: 'pbf'` in the server's configured charts resource
|
|
49
|
+
(NOAA ENC-derived S-57 MBTiles, an Open Waters passage cache, any
|
|
50
|
+
vector MBTiles served by `signalk-charts-provider-simple`) now
|
|
51
|
+
render instead of failing silently as broken image tiles. MapLibre
|
|
52
|
+
GL JS 5.24.0 and the official Leaflet bridge are vendored under
|
|
53
|
+
`public/vendor/maplibre-gl/` (same policy as Leaflet — no CDN, no
|
|
54
|
+
build step) and mounted via `L.maplibreGL`, so all DR overlays stay
|
|
55
|
+
plain Leaflet. When the corridor downloader's asset manifest
|
|
56
|
+
(`/plugins/signalk-corridor-tile-downloader/assets/manifest.json`)
|
|
57
|
+
advertises a mirrored Open Waters `style` URL, that style is mounted
|
|
58
|
+
wholesale — full marine symbology (buoys by shape and colour,
|
|
59
|
+
lights, restricted-area hatching), bathymetry contours and soundings
|
|
60
|
+
with labels, base map and hillshade, online and offline alike —
|
|
61
|
+
while terrarium-DEM (`webp`) stores stay mirror internals and never
|
|
62
|
+
appear as overlays. For sources without a manifest the style is
|
|
63
|
+
composed client-side by the pure `maplibreStyleFor` (view-model):
|
|
64
|
+
one vector source on the chart's absolute tile URL (MapLibre's
|
|
65
|
+
blob-URL workers can't resolve relative paths) with native max zoom
|
|
66
|
+
— vector charts keep rendering past the raster maxNativeZoom
|
|
67
|
+
ceiling — plus a dark sea background and geometry-only
|
|
68
|
+
fill/line/circle layers with marine styling for known source-layer
|
|
69
|
+
families (S-57 LNDARE/DEPARE/DEPCNT/COALNE/SOUNDG ids and the
|
|
70
|
+
OSM-marine names the corridor cache carries: seamark, waterway,
|
|
71
|
+
wetland, sea_area, light as amber dots). Chart-mount failures are
|
|
72
|
+
logged instead of vanishing. Right-clicking a charted symbol
|
|
73
|
+
(lighthouse, seamark, peak, island) resolves its charted name and
|
|
74
|
+
seeds the sight form's *Object* field — bearings are taken to
|
|
75
|
+
identified objects, not bare coordinates.
|
|
76
|
+
- **Plotter-extension status tile for Freeboard-SK (work doc #19)** —
|
|
77
|
+
the plugin now registers a `plotterExtensions` resource provider
|
|
78
|
+
(Plotter Extensions API v1) so chart plotters that host the
|
|
79
|
+
mechanism (Freeboard-SK ≥ 3.0) offer a "Dead Reckoning" widget:
|
|
80
|
+
a 2×1 glanceable tile showing GPS↔DR divergence, the DR uncertainty
|
|
81
|
+
radius, fix cadence and DR health (tracking / maneuver / fouled /
|
|
82
|
+
stale / warm), colored by severity like the webapp status line, with
|
|
83
|
+
an OVERRIDE badge when DR is authoritative and an amber night-mode
|
|
84
|
+
palette following the host's `nightMode` capability. Live values
|
|
85
|
+
arrive through the host's multiplexed Signal K relay (the tile opens
|
|
86
|
+
no connection of its own). The iframe assets are served from a new
|
|
87
|
+
publicly readable `/plotterext/signalk-dead-reckoning/` route (a
|
|
88
|
+
minimal in-repo static handler — the plugin stays zero-dependency);
|
|
89
|
+
discovery is the authenticated resources API, enablement is the
|
|
90
|
+
plugin's own enable switch. The bus client
|
|
91
|
+
(`signalk-plotterext-bus` 0.11.0, MIT) is vendored under
|
|
92
|
+
`public/vendor/plotterext-bus/` like Leaflet. The standalone webapp
|
|
93
|
+
is unaffected.
|
|
94
|
+
|
|
95
|
+
## [0.3.0] - 2026-08-27
|
|
96
|
+
|
|
97
|
+
### Added
|
|
98
|
+
- **`inertial-polar` DR speed fallback (SPEC §3.1, work doc #18)** —
|
|
99
|
+
when the paddlewheel is unusable (`navigation.speedThroughWater`
|
|
100
|
+
missing, or the debounced §6.3 fouling verdict active), DR integrates
|
|
101
|
+
speed from the polar performance plugin's `performance.polarSpeed`
|
|
102
|
+
delta instead of freezing: requires `signalk-polar-performance-plugin`
|
|
103
|
+
installed and configured with its polar speed output enabled; the
|
|
104
|
+
super-jittery raw delta is running-averaged (default 60 s window,
|
|
105
|
+
30 s staleness cutoff) before integration. Gated to underway+sailing
|
|
106
|
+
(no wind-on-mast drift at the dock, no meaningless polar under
|
|
107
|
+
power); no matrix corrections while on polar (bins were trained on
|
|
108
|
+
real STW — a model estimate is circular input); uncertainty grows at
|
|
109
|
+
the fallback rate; Training Mode and maneuver detection suspended;
|
|
110
|
+
the divergence advisory keeps watching. `navigation.speedThroughWater`
|
|
111
|
+
stays silent while on polar (a model estimate is not a measurement).
|
|
112
|
+
The DR state value gains `speedSource: "paddlewheel"|"polar"`, and a
|
|
113
|
+
§3.1 sensor-health alert names the switch (paddlewheel
|
|
114
|
+
unavailable/fouled — DR on polar-derived speed).
|
|
115
|
+
- Scalar sibling paths `navigation.deadReckoning.uncertainty.radius`
|
|
116
|
+
and `navigation.deadReckoning.divergence.distance` (metres, with
|
|
117
|
+
nautical-mile display-unit meta) for rule/display engines
|
|
118
|
+
(signalk-status-tiles threshold checks) that cannot read subfields of
|
|
119
|
+
object-valued paths — subscribing to a subfield path never sees a
|
|
120
|
+
delta.
|
|
121
|
+
|
|
122
|
+
### Changed
|
|
123
|
+
- **All published deltas now follow the Signal K SI unit conventions**
|
|
124
|
+
(breaking):
|
|
125
|
+
- `navigation.deadReckoning.log` / `trip.log` publish metres (was
|
|
126
|
+
nautical miles), with `value/1852` NM display-unit meta.
|
|
127
|
+
- The `environment.current` object is replaced by the standard
|
|
128
|
+
`environment.current.setTrue` (radians) and
|
|
129
|
+
`environment.current.drift` (m/s) paths; the DR-specific
|
|
130
|
+
tier/source enrichment rides REST `/status` instead of the bus.
|
|
131
|
+
- The uncertainty object field `radius_nm` becomes `radius_m`; the
|
|
132
|
+
divergence object fields `distance_nm`/`bearing_true` (deg) become
|
|
133
|
+
`distance_m`/`bearing_true` (rad).
|
|
134
|
+
- `navigation.deadReckoning.elapsedSinceFix` gains duration
|
|
135
|
+
display-unit meta so glance consumers render "3h 05m", not
|
|
136
|
+
seconds.
|
|
137
|
+
- All display-unit meta declares `category: "custom"` — the
|
|
138
|
+
server's unit-preference system rewrites category-less
|
|
139
|
+
`displayUnits` to the user's global preference (a seconds path
|
|
140
|
+
rendered as "0.0 hour"); custom keeps the nautical styling.
|
|
141
|
+
- **Inbound sensor deltas are now interpreted per the Signal K unit
|
|
142
|
+
conventions** (breaking for feeds that were publishing non-SI):
|
|
143
|
+
`speedThroughWater`/`speedApparent` are read as m/s and heading paths
|
|
144
|
+
as radians, converting to the engine's internal knots/degrees at the
|
|
145
|
+
boundary. Previously m/s and radian values were treated as knots and
|
|
146
|
+
degrees — DR under-travelled ~5× and mis-steered on standard feeds.
|
|
147
|
+
The standard-path passthroughs (`navigation.speedThroughWater`,
|
|
148
|
+
`navigation.headingTrue`) publish what they received, unchanged.
|
|
149
|
+
- Logbook fix/tack entries convert SOG/COG/heading at the boundary
|
|
150
|
+
(`_kn`/`_deg` REST fields previously received raw m/s/radians).
|
|
151
|
+
- The webapp converts SI bus values (m, m/s, rad) to nautical displays
|
|
152
|
+
centrally in the view model (`metresToNm`/`msToKn`/`radToDeg`).
|
|
153
|
+
- REST `/status` and `/current/manual` keep the plugin's internal
|
|
154
|
+
nautical units (`logNm`, `current.setTrue` deg, `drift` kn) — they
|
|
155
|
+
are the plugin's own API, not the Signal K bus.
|
|
156
|
+
- `navigation.deadReckoning.method` now reflects the actual speed
|
|
157
|
+
source every tick, completing the SPEC §3.1 enum: the idle branch
|
|
158
|
+
(no usable speed at all) publishes `fallback-zero` instead of
|
|
159
|
+
inheriting the constructor's `inertial-paddlewheel` — the `Polar`/
|
|
160
|
+
`Zero` headline labels added earlier are now driven by real values.
|
|
161
|
+
SPEC §3.1/§3.2/§6.1 aligned with the implemented fallback hierarchy
|
|
162
|
+
(polar first, fault-based selection, speed output silent on polar).
|
|
163
|
+
- The "Active method" headline shows a short watchkeeper-sized label
|
|
164
|
+
(`STW` for `inertial-paddlewheel`, `Polar`/`Zero` for the spec's
|
|
165
|
+
reserved methods) with the full token on hover — one of five figures,
|
|
166
|
+
it previously gave a full spec token permanent large-type real estate
|
|
167
|
+
despite having exactly one possible value today.
|
|
168
|
+
- The DR status no longer claims the vessel is `underway` while
|
|
169
|
+
moored: `navigation.deadReckoning.state.status` gains a `"warm"`
|
|
170
|
+
value (engine integrating on a tied-up boat, SPEC §5 runs it warm for
|
|
171
|
+
instant OVERRIDE handoff — that is not being under way) alongside
|
|
172
|
+
`"underway"` and `"idle"`. The webapp subscribes to the vessel's own
|
|
173
|
+
`navigation.state` (already in the system — nothing is republished
|
|
174
|
+
inside our deltas) and words the line accordingly:
|
|
175
|
+
`DR warm — moored/anchored, integrating sensors` vs
|
|
176
|
+
`Dead reckoning active`. Text logic moved to the pure `drStatusText()`
|
|
177
|
+
view-model helper.
|
|
178
|
+
|
|
8
179
|
## [0.2.0] - 2026-08-27
|
|
9
180
|
|
|
10
181
|
### Fixed
|
|
182
|
+
- **Map no longer opens blank — defaults to the first chart provider.**
|
|
183
|
+
The webapp previously kept its tile-less offline-first default unless
|
|
184
|
+
the server had *configured* charts, so on servers without any (the
|
|
185
|
+
common case: the layers control showed only "OpenStreetMap (online)",
|
|
186
|
+
unselected) the plot opened on an empty dark canvas. The first entry
|
|
187
|
+
of the chart list — first configured chart when the server has them,
|
|
188
|
+
otherwise the OSM online fallback — is now auto-selected on load, and
|
|
189
|
+
duplicate chart names no longer clobber each other's entry in the
|
|
190
|
+
layers control.
|
|
11
191
|
- **Dockside false positives while moored/anchored** — three alerts
|
|
12
192
|
that misfire on a tied-up boat are now suppressed in the moored/
|
|
13
193
|
anchored regime (consistent with the divergence monitor's existing
|
package/README.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Signal K Dead Reckoning
|
|
2
2
|
|
|
3
|
-
An offline-first dead reckoning and sensor fusion engine for Signal K that maintains a continuously computed "shadow boat" position from water-track sensors (speed through water, compass heading, and learned leeway and current corrections), so you always have a navigational fallback when GPS becomes unreliable — whether from jamming, spoofing, or plain receiver failure. While GPS is trusted, the engine learns vessel-specific calibration corrections against ground truth and watches for GPS anomalies; when it isn't, the same learned model keeps the dead-reckoned position, its uncertainty polygon, and a water-track log going. Fixes from celestial sights, compass bearings, and vertical angles are entered through a unified pipeline and can snap dead reckoning back on track, with optional write-through to `signalk-logbook`.
|
|
3
|
+
An offline-first dead reckoning and sensor fusion engine for Signal K that maintains a continuously computed "shadow boat" position from water-track sensors (speed through water, compass heading, and learned leeway and current corrections), so you always have a navigational fallback when GPS becomes unreliable — whether from jamming, spoofing, or plain receiver failure. While GPS is trusted, the engine learns vessel-specific calibration corrections against ground truth and watches for GPS anomalies; when it isn't, the same learned model keeps the dead-reckoned position, its uncertainty polygon, and a water-track log going. Fixes from celestial sights, compass bearings, and vertical angles are entered through a unified pipeline and can snap dead reckoning back on track, with optional write-through to `signalk-logbook`.
|
|
4
4
|
|
|
5
5
|
**Note:** This is just a toy. Make your own navigation calculations and decisions.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
* This plugin installed and configured
|
|
10
|
+
* Some chart source available to Signal K
|
|
11
|
+
* Hand bearing compass and/or sextant
|
|
12
|
+
|
|
13
|
+
## Getting started
|
|
14
|
+
|
|
15
|
+
1. Install the plugin from the Signal K Appstore (or `npm install @meri-imperiumi/signalk-dead-reckoning`) and enable it. The defaults are sane — you only need to revisit the configuration if you want to change intervals or integrations.
|
|
16
|
+
2. Make sure the required data feeds are available on the vessel network:
|
|
17
|
+
* `navigation.position` (GPS) — the training baseline and anomaly reference
|
|
18
|
+
* `navigation.speedThroughWater` (paddlewheel log) and `navigation.headingMagnetic` (compass + variation) — the water-track inputs
|
|
19
|
+
* Wind data — used by the calibration model and the polar speed fallback
|
|
20
|
+
3. Just sail. While GPS is reliable and you're under sail (propulsion stopped), the engine continuously learns vessel-specific leeway and speed-loss corrections against GPS ground truth. There is nothing to activate: the "shadow boat" runs at all times, and the uncertainty polygon tightens over a season as the calibration bins fill up. Early on, expect conservative uncertainty estimates.
|
|
21
|
+
4. Optional integrations, configured in the plugin settings:
|
|
22
|
+
* `signalk-logbook` — write confirmed fixes and maneuvers to the vessel's logbook
|
|
23
|
+
* `signalk-polar-performance-plugin` — polar-derived speed when the paddlewheel is fouled or silent
|
|
24
|
+
* `@meri-imperiumi/signalk-autostate` — keeps the navigation state (anchored, sailing, motoring) accurate, which gates training
|
|
25
|
+
* Signal K Weather API — point-forecast current vectors for the DR solution
|
|
26
|
+
|
|
27
|
+
## Daily use
|
|
28
|
+
|
|
29
|
+
Open the webapp from your Signal K server's *Web apps* menu. It shows the vessel and the shadow boat on the chart, GPS↔DR divergence, the uncertainty polygon, and the water-track log. A status tile is also available for Freeboard-SK ≥ 3.0.
|
|
30
|
+
|
|
31
|
+
**Entering fixes.** Open *⊕ Sight / LOP* and pick the method:
|
|
32
|
+
|
|
33
|
+
* **Bearing** — compass bearing to a known object (lighthouse, tower) gives a line of position
|
|
34
|
+
* **Vert. Angle** — vertical angle to a known object of known height gives a circular position line
|
|
35
|
+
* **Celestial** — sextant sight of the Sun, Moon, or an almanac star
|
|
36
|
+
|
|
37
|
+
You can right-click an object on the map to pre-fill its coordinates. Sights can be entered with a stopwatch delay ("minutes ago") instead of clock time. Observations accumulate in the pending list; select two or more, *Preview selected* to resolve the candidate fix, then *Confirm fix* to snap dead reckoning to it. A single line of position can also be advanced to a later sight (running fix).
|
|
38
|
+
|
|
39
|
+
**When GPS looks wrong.** The plugin raises notifications for GPS anomalies, growing DR divergence, and paddlewheel fouling — but it never switches navigational authority by itself. If you decide to trust DR over GPS, press *Engage OVERRIDE*; the shadow boat takes over `navigation.position` instantly (it has been running all along). Release it when position is re-established, e.g. after confirming a fix.
|
|
40
|
+
|
|
41
|
+
**Other controls.** *≋ Current* sets a manual set-and-drift override with a TTL, which outranks the automatic current sources while it lasts.
|
package/SPEC.md
CHANGED
|
@@ -38,10 +38,10 @@ The three motivations are causally linked: (1) generates the fix data and crew e
|
|
|
38
38
|
|---|---|---|
|
|
39
39
|
| `navigation.deadReckoning.position` | `{ latitude, longitude, altitude }` | **Always-on** 1Hz inertial "shadow boat" position — computed continuously regardless of mode. |
|
|
40
40
|
| `navigation.deadReckoning.active` | boolean | Whether DR is currently the *authoritative* source feeding `navigation.position` (i.e. OVERRIDE engaged), as distinct from merely running in the background. |
|
|
41
|
-
| `navigation.deadReckoning.method` | string: `inertial-polar` \| `inertial-paddlewheel` \| `fallback-zero` | Active
|
|
41
|
+
| `navigation.deadReckoning.method` | string: `inertial-polar` \| `inertial-paddlewheel` \| `fallback-zero` | Active speed-source mode, re-published every tick: `inertial-paddlewheel` while the paddlewheel serves (raw STW present, not fouled); `inertial-polar` when it doesn't and the polar fallback can engage (§6.1); `fallback-zero` when no usable speed source exists (DR holds position). Selection is fault-based, never merit-based — a working measurement always outranks a model. |
|
|
42
42
|
| `navigation.deadReckoning.log` | number (nm) | Cumulative **water-track** distance, integrated from STW. Independent of GPS. |
|
|
43
43
|
| `navigation.deadReckoning.trip.log` | number (nm) | Same, reset at trip boundaries (see §9.2). |
|
|
44
|
-
| `navigation.speedThroughWater` | number | Calibrated STW output (matrix-corrected). |
|
|
44
|
+
| `navigation.speedThroughWater` | number | Calibrated STW output (matrix-corrected). Silent while the polar fallback is active — a model estimate must not masquerade as a measurement. |
|
|
45
45
|
| `navigation.headingTrue` | number | Calibrated true heading (corrected for dynamic deviation). |
|
|
46
46
|
| `environment.current` | `{ setTrue, drift, meta: { source, expiresAt } }` | Current vector broadcast, per §6.2 hierarchy. |
|
|
47
47
|
| `notifications.navigation.gpsSpoofed` | alarm state | High-severity: sudden position discontinuity inconsistent with DR/physics, or at-anchor/moored displacement beyond plausible bound. See §7. |
|
|
@@ -55,6 +55,7 @@ The three motivations are causally linked: (1) generates the fix data and crew e
|
|
|
55
55
|
| `navigation.position` | GPS baseline for training mode and anomaly detection. |
|
|
56
56
|
| `navigation.speedThroughWater`, `navigation.headingMagnetic`, `navigation.attitude` | Raw sensor inputs (heel/pitch). |
|
|
57
57
|
| `environment.wind.angleApparent`, `environment.wind.speedApparent` | Wind inputs for leeway/upwash modeling. |
|
|
58
|
+
| `performance.polarSpeed` | Polar-derived boat speed (m/s) from `signalk-polar-performance-plugin` — requirement for the §6.1 `inertial-polar` fallback: that plugin installed and configured with its polar-speed output enabled. The raw delta is a step-function polar lookup driven by gusty wind and is **running-averaged** before integration; sustained nulls (wind out-of-table) age the average out to staleness rather than decaying it toward zero. |
|
|
58
59
|
| `navigation.sails` | Active sail configuration, from `signalk-logbook`. |
|
|
59
60
|
| `environment.seaState` | Sea state tier, from logbook watch entries. |
|
|
60
61
|
| `navigation.state` | `anchored` \| `moored` \| `sailing` \| `motoring` \| ... — trip boundaries, and the anchored/moored anomaly-detection gates. |
|
|
@@ -273,7 +274,14 @@ Worker Thread (DR Physics)
|
|
|
273
274
|
|
|
274
275
|
**Inference Mode** — active when `isGpsReliable = false` OR OVERRIDE is manually engaged:
|
|
275
276
|
- Freezes matrix learning. Reads raw sensors, looks up matching bins, applies corrections, integrates the resolved current vector, publishes `navigation.deadReckoning.position` as authoritative (`navigation.deadReckoning.active = true`).
|
|
276
|
-
|
|
277
|
+
|
|
278
|
+
**Speed-source fallback** — mode-independent (the shadow boat integrates regardless of GPS trust, §5, so a sensor lost in NORMAL mode is handled the same way as one lost in Inference/OVERRIDE). A fouled or silent paddlewheel degrades the speed source by preference:
|
|
279
|
+
|
|
280
|
+
1. **Polar-derived speed** — the running-averaged `performance.polarSpeed` (§3.2). Gated to underway + sailing: wind on a moored mast must not sail the shadow boat off the dock, and a polar is meaningless under power. While on this source: no matrix corrections (leeway/speed-loss bins were trained on real paddlewheel STW — feeding them a model estimate is circular), uncertainty grows at the fallback rate, and matrix training / maneuver detection are suspended (never train on synthetic input — same principle as the §6.3/§6.4 write gates). The divergence advisory (§7.3) keeps watching: model drift must be detected even though it never re-selects the method.
|
|
281
|
+
2. **GPS-SOG-derived speed** — if no polar source is available and GPS is at least partially available.
|
|
282
|
+
3. **Hold last-known-good STW** — with explicitly faster-growing uncertainty, if neither.
|
|
283
|
+
|
|
284
|
+
The paddlewheel-failure fallback is a distinct branch from the "GPS unreliable" case, since the two can occur independently or together.
|
|
277
285
|
|
|
278
286
|
### 6.2 Current Hierarchy of Truth
|
|
279
287
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meri-imperiumi/signalk-dead-reckoning",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Offline-first dead reckoning and sensor fusion engine for Signal K",
|
|
5
5
|
"main": "plugin/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
],
|
|
34
34
|
"recommends": [
|
|
35
35
|
"@meri-imperiumi/signalk-autostate",
|
|
36
|
-
"@meri-imperiumi/signalk-logbook"
|
|
36
|
+
"@meri-imperiumi/signalk-logbook",
|
|
37
|
+
"signalk-polar-performance-plugin"
|
|
37
38
|
],
|
|
38
39
|
"screenshots": [
|
|
39
40
|
"./doc/dr-map.png",
|