speedandcurrent 2.4.0 → 2.5.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.
Files changed (4) hide show
  1. package/CHANGELOG.md +204 -0
  2. package/README.md +12 -4
  3. package/index.js +10 -20
  4. package/package.json +4 -3
package/CHANGELOG.md ADDED
@@ -0,0 +1,204 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+
7
+ ## [2.5.0] - 2026-10-26
8
+
9
+ ### Added
10
+ - Correction-table files now use version 2 with per-cell update timestamps, capped time-based aging, and silent migration of existing version-1 files.
11
+ - The webapp now polls at the plugin's actual sample rate instead of a fixed 1s interval, reported via a new `pollIntervalMs` field on `/api/report`.
12
+ - Correction estimates now fuse mature nearby table cells using their full covariance and normalized distance, with an automatically estimated spatial variance, a two-cell search radius, and fusion-weight diagnostics in the webapp.
13
+ - The correction table now overlays the current uncorrected (raw) and corrected speed/heel as red and blue dots positioned directly on the grid, with a matching legend entry.
14
+
15
+ ### Changed
16
+ - The npm package now includes this changelog.
17
+ - Updated the `signalkutilities` dependency to `^3.2.0`, including non-finite sample protection, explicit-null smoother resets, and subscription-manager-controlled startup values.
18
+ - Requires signalk-server >=2.35.0 for the upstream metadata-first subscription fix ([SignalK/signalk-server#3216](https://github.com/SignalK/signalk-server/pull/3216)).
19
+ - Read-only Signal K users can now access live reports, status, and metadata; settings and correction-table listing or changes remain administrator-only.
20
+ - Correction-table learning no longer incorporates the estimated current, and the related setting has been removed.
21
+ - Correction-table process noise is now configured as a user-facing correction drift rate in knots/month, with a conservative default of 0.3 and an SI conversion internally. Legacy stability settings migrate silently.
22
+ - Correction interpolation now includes mature cells within 2.5 normalized grid-cell units.
23
+ - Correction learning now always uses a moving-average window of at least 5 seconds, with a one-second gap between observation windows. Older smoother selections migrate silently.
24
+ - Cells become eligible after one accepted observation; the explicit maturity gate remains in place at zero for future tuning.
25
+
26
+ ### Fixed
27
+ - Removed idle-triggered input resubscriptions, including boat-speed inputs, to avoid replaying cached values during sensor outages. Idle/stale warnings remain, and existing subscriptions receive data when the source resumes. Synchronous startup safeguards are preserved.
28
+ - The boat-speed estimation inputs now prioritize Boatspeed unless groundspeed fallback is selected, and the correction table marks its most recently updated cell with a thin black border reflected in the legend.
29
+ - The learning status Reason row is now hidden when the last observation was accepted, instead of showing a redundant "Observation recorded" message.
30
+ - `navigation.state` no longer misreports as "not ready" a few seconds after each update; it is an event path that only publishes on transition, so the generic telemetry staleness timer no longer applies to it. The Inputs tab now also displays its current value.
31
+ - Learning and leeway are now gated on the vessel's own SOG-based moving/not-moving state, rather than solely on `navigation.state` freshness. Previously, an infrequent `navigation.state` update could go stale within seconds, silently letting learning continue while anchored or moored. `navigation.state`, when known and `suspendLearningOnNavigationState` is enabled, can still override to "not moving" (anchored/moored/motoring), but can no longer force "moving" against the vessel's own SOG.
32
+ - Webapp report parsing failures now remain visible and still refresh plugin status instead of aborting the polling tick.
33
+ - Disabled current estimation no longer publishes a zero-current value during plugin startup.
34
+ - Malformed correction tables now recover safely at startup, while explicit loads reject invalid files without replacing the active table.
35
+ - The correction-table Kalman filter now uses each observation's own measurement-noise covariance (already computed from live sensor variances) instead of a fixed identity matrix, so anisotropic and rotated observation uncertainty is correctly reflected in cell covariances. Persisted tables store a serializable model descriptor rather than the runtime model; existing table files still load unchanged.
36
+
37
+ ## [2.3.4] - 2026-09-17
38
+
39
+ ### Fixed
40
+ - Boat-speed startup now preserves synchronously replayed cached values, avoids restart errors, and keeps current estimation behind the startup stabilization period regardless of table-learning settings.
41
+ - Attitude roll handling now treats incomplete or stale values as invalid during correction-table learning, preventing fabricated heel-based corrections while the input is not yet usable.
42
+ - Webapp values and warnings now share per-path lifecycle status, distinguish subscribed and computed data, report polar magnitude and angle independently, and show stale raw inputs consistently across all pages.
43
+
44
+ ## [2.3.3] - 2026-09-06
45
+
46
+ ### Fixed
47
+ - `navigation.leewayAngle` no longer reports a correction-table artifact when the vessel is stationary. The lateral component of the table correction is now suppressed below the table speed step and while `navigation.state` is `anchored` or `moored`, so leeway is published as 0 instead of an angle derived from a near-zero speed. The longitudinal speed correction, and leeway while motoring, are unaffected.
48
+ - Updated `signalkutilities` to 3.1.2, which completes the 3.1.1 recovery fix for polar-backed inputs. Smoothed heading and the ground-speed vector are built on `Polar`/`PolarSmoother`, where the idle timer was still cleared rather than re-armed on each delta, so those inputs did not recover after going silent following a healthy start.
49
+
50
+ ## [2.3.2] - 2026-09-04
51
+
52
+ ### Fixed
53
+ - Updated `signalkutilities` to 3.1.1, so an input that goes silent *after* having delivered data is now detected and resubscribed. Previously recovery only ran for subscriptions that had never received a delta, meaning an upstream outage after a healthy start could leave the plugin dead until it was restarted.
54
+
55
+ ## [2.3.1] - 2026-08-18
56
+
57
+ ### Fixed
58
+ - Signal K admin styling now loads from standalone CSS records in current Vite manifests while retaining support for entry-associated CSS and older server fallbacks.
59
+
60
+ ## [2.3.0] - 2026-08-16
61
+
62
+ ### Added
63
+ - Learning status now distinguishes between active learning and observations that were skipped, rejected, or invalid, and the webapp reports the current reason more clearly.
64
+
65
+ ### Changed
66
+ - Correction-table learning now uses automatic gates based on `navigation.state`, with `anchored` and `moored` always suspending learning when available and `motoring` optionally suspending it.
67
+ - The learning threshold for both STW and SOG now follows the active table speed-step size.
68
+ - The webapp no longer exposes a separate minimum-SOG learning setting.
69
+
70
+ ### Fixed
71
+ - When learning is off or suspended, the webapp no longer keeps showing a stale previous observation result.
72
+
73
+ ## [2.2.1] - 2026-08-14
74
+
75
+ ### Fixed
76
+ - Input subscriptions now recover automatically after idle or stale periods, and the webapp exposes lifecycle warnings for affected paths.
77
+ - Corrected output paths are now explicitly cleared with `null` when estimation is disabled or when the plugin stops, preventing stale values from lingering on the Signal K bus.
78
+ - The webapp-facing status and report endpoints now reflect the plugin's current runtime state more directly.
79
+ - SOG fallback now checks raw STW (`rawBoatSpeed`) directly when detecting a zero paddlewheel reading, avoiding dependence on the derived corrected speed value.
80
+
81
+ ## [2.1.2] - 2026-07-23
82
+
83
+ ### Fixed
84
+ - App Store icon was missing (monogram fallback shown): added `icon.png` at the package root so the App Store CDN can fetch it from the npm tarball via unpkg.com. The runtime `signalk.appIcon: ./icon.png` path is unchanged - the server still serves it from `public/icon.png` via the webapp mount.
85
+ - Updated `signalkutilities` to 3.0.1, fixing continuous 401 log flooding on SK servers with security enabled and anonymous read access disabled.
86
+
87
+ ## [2.1.1] - 2026-07-20
88
+
89
+ ### Fixed
90
+ - Sidebar toggler was unreachable on mobile viewports.
91
+
92
+ ### Changed
93
+ - Updated `signalkutilities` dependency to `^3.0.0` (removes `PolarTable` from the shared library; no behaviour change for this plugin).
94
+
95
+ ## [2.1.0] - 2026-07-10
96
+
97
+ ### Added
98
+ - `null` is written to corrected boat speed (`navigation.speedThroughWater`, `navigation.leewayAngle`) and current (`environment.current.drift`, `environment.current.setTrue`) paths when `estimateBoatSpeed` is toggled off.
99
+ - `null` is written to all active output paths (current, corrected boat speed) when the plugin stops, preventing stale values on the SK bus.
100
+
101
+ ## [2.0.2] - 2026-6-18
102
+ ### Fixed
103
+ - The plugin icon was not displayed in the Signal K App Store because the icon path in the package metadata was incorrect.
104
+ - Test files are no longer included in the published npm package.
105
+
106
+ ## [2.0.1] - 2026-6-17
107
+ ### Fixed
108
+ - Correction table reporting was doing unnecessary work on every update, causing higher CPU use especially on low-power hardware such as a Raspberry Pi.
109
+ - The variance values used for interpolating between correction table cells were calculated with an incorrect formula, causing them to be overestimated. This made the Kalman outlier gate slightly less accurate.
110
+
111
+ ## [2.0.0] - 2026-6-17
112
+ ### Changed
113
+ - Moving average smoother is now O(1) in both CPU and memory: it no longer slows down or uses more memory as the window size grows.
114
+
115
+ - **Breaking:** Requires `signalkutilities` v2.0.0. Source-selection and pass-through machinery has been removed from the library; Signal K Server now handles source priorities natively.
116
+ - Per-path source settings (`headingSource`, `boatSpeedSource`, `SOGSource`, `attitudeSource`) have been removed. Use Signal K Server's built-in source priority configuration instead.
117
+ - The **Prevent Speed Duplication** setting has been removed. The server's `excludeSelf` behaviour and source priorities replace it.
118
+
119
+ ### Removed
120
+ - Source dropdowns in the webapp Inputs section.
121
+ - Source-related fields from persisted plugin configuration. A one-time migration strips these fields automatically on first start with the new version.
122
+
123
+ ### Added
124
+ - Documentation explaining how to configure Signal K source priorities so that the corrected `navigation.speedThroughWater` is delivered to all consumers.
125
+
126
+ ## [1.7.10] - 2026-5-14
127
+ ### Fixed
128
+ - Removed incorrect `self.` prefix from Signal K paths.
129
+
130
+ ## [1.7.9] - 2026-5-14
131
+ ### Added
132
+ - Learning status indicator in the webapp showing whether the correction table is active, stabilising, or off, and whether the last observation was accepted, rejected, invalid, or below threshold.
133
+ - Outlier rejection using Mahalanobis distance: bad observations are discarded before being fed into the Kalman filter, preventing a single bad sample from corrupting a cell.
134
+
135
+ ### Fixed
136
+ - Smoothed learning inputs are now always registered in the reporter regardless of whether `updateCorrectionTable` is enabled, so warnings clear correctly when learning is toggled on at runtime.
137
+
138
+ ## [1.7.8] - 2026-5-14
139
+ ### Added
140
+ - Legend below the correction table explaining factor / leeway values, background colour coding, stripe direction, active cell, and neighbour cell indicators.
141
+
142
+ ### Fixed
143
+ - Correction table learning never started when `assumeCurrent` is off.
144
+ - Active cell highlighting (most recently updated cell) was not visible.
145
+
146
+ ## [1.7.7] - 2026-5-13
147
+ ### Added
148
+ - Optional staleness detection for inputs.
149
+ - Precise warnings on state of inputs.
150
+
151
+ ## [1.7.5] - 2025-11-19
152
+ ### Added
153
+ - New webApp.
154
+ - WebApp allows to change plugin configuration while the plugin is running.
155
+ - Support for multiple correction tables (new, copy, delete).
156
+ - Support for altering the dimensions of a correction table (alter).
157
+ - Better edge case handling.
158
+ - User selectable options for smoothing method and smoothing parameters.
159
+ - Support for unitPreferences.
160
+
161
+ ### Fixed
162
+ - Bug where navigation.speedThroughWater was removed from the signalk bus.
163
+
164
+ ### Removed
165
+ - Possibility to set options via the signalk plugin configuration.
166
+
167
+ ## [1.6.0] - 2025-11-19
168
+ ### Added
169
+ - Option to copy navigation.speedOverGround to navigation.speedThroughWater when speedThroughWater is not properly measured. Thanks to Jean-Laurent Girod for the suggestion.
170
+
171
+ ## [1.5.1] - 2025-10-06
172
+ ### Fixed
173
+ - Bug preventing current to be displayed in KIP.
174
+
175
+ ## [1.5.0] - 2025-10-06
176
+ ### Changed
177
+ - Separate loops for correcting speed and for updating correction factors.
178
+ - Speed correction loop uses raw values.
179
+ - Updating correction factors using moving averages.
180
+ - Unified cell rendering driven by "Show" selections; removed the previous Table style toggle.
181
+
182
+ ### Added
183
+ - Option to set the source for input paths.
184
+ - Full integration of uncertainty estimations (variance) in estimations and corrections.
185
+ - Ability to resize the correction table without losing all correction data.
186
+ - WebApp: "Show" multi-select to choose per-cell metrics (correction X/Y, factor, leeway, trace, N) for the correction table.
187
+ - WebApp: Color mode options for the correction table.
188
+ - WebApp: Persistence for speed unit, angle unit, color mode, and shown metrics via localStorage.
189
+
190
+ ### Fixed
191
+ - Angles are now reported in the appropriate range, 0 to 2 * PI or -PI to PI, depending on path.
192
+
193
+ ## [1.1.0] - 2025-8-20
194
+ ### Changed
195
+ - Path names for current. It now is self.environment.current.setTrue and self.environment.current.drift.
196
+
197
+ ### Fixed
198
+ - Case error in filename that prevented the webapp from displaying the correction table.
199
+ - Corrected speed through water is now calculated when the correction table is fixed.
200
+
201
+ ## [1.0.1] - 2025-1-27
202
+ ### Added
203
+ - Basic functionality to estimate speed, leeway and current using Kalman filters.
204
+ - Basic functionality for a webapp that shows what is going on.
package/README.md CHANGED
@@ -6,7 +6,9 @@ Most paddle wheel logs carry a systematic error that varies with boat speed and
6
6
 
7
7
  This plugin builds and maintains a **2-dimensional correction table** — indexed by speed and heel — that is populated automatically from real sailing observations. No calibration runs, no spreadsheets, no manual entry. Once the table has enough coverage the plugin applies per-point corrections to `navigation.speedThroughWater`, derives leeway from the lateral component of the correction, and estimates water current by subtracting the corrected boat speed vector from the GPS ground speed vector.
8
8
 
9
- In the current implementation the plugin is also explicit about runtime state: if an input goes idle or stale, the webapp shows a warning and the handler resubscribes; when estimation is disabled or the plugin stops, the active output paths are cleared with `null` so downstream consumers do not keep stale values.
9
+ Requires signalk-server >=2.35.0 for reliable metadata-first input subscriptions.
10
+
11
+ If an input goes idle or stale, the webapp shows a warning while the subscription stays attached, ready for the next delta; when estimation is disabled or the plugin stops, the active output paths are cleared with `null` so downstream consumers do not keep stale values.
10
12
 
11
13
  In broad terms the plugin:
12
14
 
@@ -31,6 +33,12 @@ The plugin requires no further configuration in the Signal K admin UI. All setti
31
33
 
32
34
  ---
33
35
 
36
+ ## HTTP endpoint access
37
+
38
+ Signal K read-only users can access the live report (`/api/report`), metadata (`/api/meta`), and status (`/api/status`) endpoints. Settings (`/api/settings`) and correction-table listing or management (`/api/tables...`) require administrator access. Anonymous requests still follow the server's configured security policy; this plugin does not enable anonymous access.
39
+
40
+ ---
41
+
34
42
  ## Configuring Signal K source priorities
35
43
 
36
44
  Signal K Server manages source priorities natively. When multiple devices publish the same path, the server delivers the highest-priority source to all subscribers — including this plugin. No per-path source selection is needed inside the plugin itself.
@@ -289,7 +297,7 @@ Every time conditions are right — plugin running for >60 seconds, smoothers se
289
297
  observation = R(ψ)⁻¹ · V_SOG − R(ψ)⁻¹ · V_current − V_STW
290
298
  ```
291
299
 
292
- where `R(ψ)` rotates from ground frame to boat frame using true heading `ψ`. In plain terms: rotate GPS velocity into the boat frame, subtract the raw paddle wheel velocity. The residual is the implied sensor error for the current speed and heel.
300
+ where `R(ψ)` rotates from ground frame to boat frame using true heading `ψ`. In plain terms: rotate GPS velocity into the boat frame, subtract the current estimate (also rotated), subtract the raw paddle wheel velocity. The residual is the implied sensor error for the current speed and heel.
293
301
 
294
302
  The Kalman update combines this observation with the cell's existing belief:
295
303
 
@@ -298,11 +306,11 @@ K = P · (P + R_obs)⁻¹
298
306
  x_new = x_old + K · (observation − x_old)
299
307
  ```
300
308
 
301
- where P is the cell's current covariance and R_obs is the observation covariance derived from the measurement uncertainty of all contributing signals (SOG variance + STW variance, rotated appropriately). It also includes heading uncertainty in radians squared. For $u = R(-heading)(groundSpeed - current)$, the heading contribution is $J \sigma_h^2 J^T$, where $J = [u_y, -u_x]^T$. This is applied once to the combined ground-speed-minus-current vector because both vectors share the same heading error. **Noisy observations produce a smaller gain and move the cell estimate less.**
309
+ where P is the cell's current covariance and R_obs is the observation covariance derived from the measurement uncertainty of all contributing signals (SOG variance + current variance + STW variance, rotated appropriately). It also includes heading uncertainty in radians squared. For $u = R(-heading)(groundSpeed - current)$, the heading contribution is $J \sigma_h^2 J^T$, where $J = [u_y, -u_x]^T$. This is applied once to the combined ground-speed-minus-current vector because both vectors share the same heading error. **Noisy observations produce a smaller gain and move the cell estimate less.**
302
310
 
303
311
  ### Time-scaled aging and correction drift
304
312
 
305
- Each cell has a small **process-noise rate** that allows it to drift slowly over time, reflecting that a paddle wheel's error can change. For an elapsed interval `dt`, the process covariance added to each diagonal is:
313
+ Each cell has a small **process-noise rate** that allows it to drift slowly over time, reflecting that a paddle wheel's error can change with fouling, recalibration, or crew weight distribution. For an elapsed interval `dt`, the process covariance added to each diagonal is:
306
314
 
307
315
  ```text
308
316
  effectiveDt = min(dt, 90 days)
package/index.js CHANGED
@@ -385,7 +385,7 @@ module.exports = function (app) {
385
385
  function setLifecycleWarning(id, status, path) {
386
386
  const safePath = path || 'unknown path';
387
387
  const message = status === 'idle'
388
- ? `Input ${id} is idle on ${safePath}; resubscribing`
388
+ ? `Input ${id} is idle on ${safePath}`
389
389
  : status === 'incomplete'
390
390
  ? `Input ${id} is missing ${safePath}`
391
391
  : `Input ${id} is stale on ${safePath}`;
@@ -407,7 +407,7 @@ module.exports = function (app) {
407
407
  .sort((a, b) => b.updatedAt - a.updatedAt);
408
408
  }
409
409
 
410
- function buildLifecycleCallbacks(id, getPath, resubscribe) {
410
+ function buildLifecycleCallbacks(id, getPath) {
411
411
  return {
412
412
  onDelta: () => {
413
413
  clearLifecycleWarning(id);
@@ -421,13 +421,8 @@ module.exports = function (app) {
421
421
  onIdle: () => {
422
422
  if (!isRunning) return;
423
423
  const path = getPath();
424
- app.debug(`[${plugin.id}] idle input ${id} on ${path}; resubscribing`);
424
+ app.debug(`[${plugin.id}] idle input ${id} on ${path}`);
425
425
  setLifecycleWarning(id, 'idle', path);
426
- try {
427
- resubscribe();
428
- } catch (e) {
429
- app.debug(`[${plugin.id}] resubscribe failed for ${id}: ${e.message}`);
430
- }
431
426
  }
432
427
  };
433
428
  }
@@ -448,7 +443,7 @@ module.exports = function (app) {
448
443
  app.debug('registerWithRouter');
449
444
  readOptions(); // pre-load so /api/settings works before start()
450
445
 
451
- router.get('/api/report', (req, res) => {
446
+ router.access('readonly').get('/api/report', (req, res) => {
452
447
  if (!isRunning) {
453
448
  res.status(503).json({ error: "Plugin is not running" });
454
449
  } else {
@@ -462,7 +457,7 @@ module.exports = function (app) {
462
457
  }
463
458
  });
464
459
 
465
- router.get('/api/meta', (req, res) => {
460
+ router.access('readonly').get('/api/meta', (req, res) => {
466
461
  if (!isRunning) {
467
462
  res.status(503).json({ error: "Plugin is not running" });
468
463
  } else {
@@ -472,7 +467,7 @@ module.exports = function (app) {
472
467
 
473
468
 
474
469
 
475
- router.get('/api/status', (req, res) => {
470
+ router.access('readonly').get('/api/status', (req, res) => {
476
471
  res.json({ status: pluginStatus, isRunning, lifecycleWarnings, learningState: getLearningStatePayload() });
477
472
  });
478
473
 
@@ -653,8 +648,7 @@ module.exports = function (app) {
653
648
  smootherOptions,
654
649
  ...buildLifecycleCallbacks(
655
650
  'heading.angle',
656
- () => smoothedHeading?.handler?.path || 'navigation.headingTrue',
657
- () => { smoothedHeading?.unsubscribe(); smoothedHeading?.subscribe(false, true); }
651
+ () => smoothedHeading?.handler?.path || 'navigation.headingTrue'
658
652
  )
659
653
  });
660
654
  rawHeading = smoothedHeading.handler;
@@ -669,8 +663,7 @@ module.exports = function (app) {
669
663
  smootherOptions,
670
664
  ...buildLifecycleCallbacks(
671
665
  'attitude.smoothed',
672
- () => smoothedAttitude?.handler?.path || 'navigation.attitude',
673
- () => { smoothedAttitude?.unsubscribe(); smoothedAttitude?.subscribe(); }
666
+ () => smoothedAttitude?.handler?.path || 'navigation.attitude'
674
667
  )
675
668
  });
676
669
  rawAttitude = smoothedAttitude.handler;
@@ -776,10 +769,8 @@ module.exports = function (app) {
776
769
  onIdle: () => {
777
770
  if (!isRunning) return;
778
771
  const path = smoothedBoatSpeed?.handler?.path || 'navigation.speedThroughWater';
779
- app.debug(`[${plugin.id}] idle input boatSpeed.smoothed on ${path}; resubscribing`);
772
+ app.debug(`[${plugin.id}] idle input boatSpeed.smoothed on ${path}`);
780
773
  setLifecycleWarning('boatSpeed.smoothed', 'idle', path);
781
- smoothedBoatSpeed?.unsubscribe();
782
- smoothedBoatSpeed?.subscribe();
783
774
  },
784
775
  onStale: () => {
785
776
  if (!isRunning) return;
@@ -820,8 +811,7 @@ module.exports = function (app) {
820
811
  smootherOptions,
821
812
  ...buildLifecycleCallbacks(
822
813
  'groundSpeed.smoothed',
823
- () => `${smoothedGroundSpeed?.polar?.pathMagnitude || 'navigation.speedOverGround'}, ${smoothedGroundSpeed?.polar?.pathAngle || 'navigation.courseOverGroundTrue'}`,
824
- () => { smoothedGroundSpeed?.unsubscribe(); smoothedGroundSpeed?.subscribe(true, true); }
814
+ () => `${smoothedGroundSpeed?.polar?.pathMagnitude || 'navigation.speedOverGround'}, ${smoothedGroundSpeed?.polar?.pathAngle || 'navigation.courseOverGroundTrue'}`
825
815
  )
826
816
  });
827
817
  rawGroundSpeed = smoothedGroundSpeed.polar;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speedandcurrent",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "A plugin that automatically corrects boat speed and calculates current and leeway using sensor fusion.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -18,7 +18,7 @@
18
18
  "homepage": "https://github.com/Asw1n/speedandcurrent#readme",
19
19
  "engines": {
20
20
  "node": ">=22.0.0",
21
- "signalk-server": ">=2.28.0"
21
+ "signalk-server": ">=2.35.0"
22
22
  },
23
23
  "keywords": [
24
24
  "signalk-webapp",
@@ -41,9 +41,10 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "kalman-filter": "^2.3.0",
44
- "signalkutilities": "^3.1.6"
44
+ "signalkutilities": "^3.2.0"
45
45
  },
46
46
  "files": [
47
+ "CHANGELOG.md",
47
48
  "icon.png",
48
49
  "index.js",
49
50
  "correctionTable.js",