speedandcurrent 1.4.2 → 1.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Aswin
3
+ Copyright (c) 2025 Aswin Bouwmeester
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,85 +1,186 @@
1
- # **Speed Correction & Current Estimation Plugin**
2
-
3
- ## **Overview**
4
- This SignalK plugin estimates **corrected speed through water**, **leeway**, and **water current** in real-time using advanced filter techniques. The plugin continuously refines its estimates based on vessel speed, heading, ground speed, and heel angle.
5
-
6
- ### **Key Features**
7
- - ✅ Real-time estimation of vessel speed (STW).
8
- - ✅ Real-time estimation of leeway.
9
- - ✅ Real-time estimation of water current (both speed and direction).
10
- - ✅ Adaptive learning: The plugin refines corrections over time.
11
- - ✅ A web app to provide real-time insights into the plugin's operation.
12
- - ✅ Use of Kalman filters for optimal estimations of the correction table, boat speed, and current.
13
- - ✅ A user-configurable 2D correction table for speed errors based on heel and speed.
14
-
15
- ## **Introduction**
16
- The plugin is based on the idea that a vessel's speed sensor is imperfect and can be corrected. Speed sensors, most often paddle wheels, are imperfect for two reasons:
17
- 1. **Measurement Precision**: The water flow over the hull influences the paddle wheel's measurement.
18
- 2. **Leeway**: A paddle wheel does not measure lateral speed or leeway, which is crucial for sailing vessels.
19
-
20
- A GPS sensor provides the vessel's speed over ground, which differs from the paddle wheel's speed through water when there is current. The GPS sensor also has its imperfections.
21
-
22
- In theory, one could use the GPS sensor to estimate the paddle wheel's error. However, estimating the paddle wheel's error requires knowing the water current, which cannot be measured directly from a floating vessel. Given enough measurements, one could estimate both current and paddle wheel error.
23
-
24
- The plugin estimates both current and sensor error simultaneously. Using these estimations, the plugin corrects boat speed, estimates leeway, and estimates the current in real-time.
25
-
26
- ### **Assumptions**
27
- - The paddle wheel's error depends on boat speed and heel angle, and it may change slowly over time.
28
- - The speed and direction of the current change slowly and gradually.
29
- - The vessel changes direction regularly.
30
-
31
- ## **Configuration of the Plugin**
32
-
33
- ### **1. Plugin Mode**
34
- The plugin has different modes optimized for various circumstances. If there is no (or very little) current, use one of the no-current modes for the best paddle wheel corrections. If there is current, select one of the modes that account for current.
35
-
36
- You must start with a mode that creates a new correction table. These modes use the specified dimensions to create an empty correction table. Once the table is created, the plugin will automatically switch to one of the fresh correction table modes. Be careful when selecting to start with a new correction table, as the previous table will be permanently deleted.
37
-
38
- The modes that assume a fresh correction table are optimized to estimate valid correction values quickly. Corrections are not applied, and no leeway or current is estimated. Once the correction table has matured, select one of the mature correction table modes. In these modes, corrections are calculated and updated conservatively, and corrections are applied to the boat speed. Leeway and, optionally, current are estimated.
39
-
40
- If circumstances are very dynamic (e.g., unstable wind, currents, or big waves), you can lock the correction table. Corrections will no longer be updated but will be applied.
41
-
42
- | Mode | Updates Correction Table | Assume Current | Correct Boat Speed | Estimate Leeway | Estimate Current |
43
- |------|--------------------------|----------------|---------------------|-----------------|------------------|
44
- | Start with new correction table, no current | Yes, starting with a new one | No | No | No | No |
45
- | Start with new correction table, with current | Yes, starting with a new one | Yes | No | No | No |
46
- | Fresh correction table, no current | Fast | No | No | No | No |
47
- | Fresh correction table, with current | Fast | Yes | No | No | No |
48
- | Mature correction table, no current | Slow | No | Yes | Yes | No |
49
- | Mature correction table, with current | Slow | Yes | Yes | Yes | Yes |
50
- | Locked correction table | No | Yes | Yes | Yes | Yes |
51
-
52
- ### **2. Correction Table**
53
- The other parameters for the plugin deal with the correction table. These include step size for heel and speed, and maximum heel and speed.
54
- - The correction table should cover the vessel's normal dynamic range. Set maximum speed and maximum heel to values the vessel would typically achieve. Avoid setting values for exceptional circumstances, as this may result in unreliable estimations.
55
- - Choose the step size carefully. Too small step sizes may lead to insufficient observations for a good estimation, while too large step sizes may result in imprecise corrections.
56
- - Changing any table dimensions will result in the loss of all existing estimations.
57
-
58
- ## **WebApp**
59
- The plugin comes with a WebApp that allows you to view the correction table and see the estimations in real-time. The WebApp can be launched from the SignalK server. The WebApp:
60
- - Graphically shows the boat speeds (observed and estimated), ground speed, current, and correction.
61
- - Displays incoming deltas.
62
- - Displays the boat speeds (observed and estimated), ground speed, current, and correction.
63
- - Displays the content of the correction table in either Cartesian values or polar coordinates.
64
- - Displays the plugin configuration and internal settings.
65
-
66
- The graphical representation of the speed vectors, current vector, and correction vector can be embedded in KIP displays using the path `/speedandcurrent/vectors.html`.
1
+ # Speed Correction & Current Estimation Plugin
2
+
3
+ ## Overview
4
+ This SignalK plugin estimates **corrected speed through water**, **leeway**, and **water current (drift)** in real-time using adaptive sensor fusion and statistical filtering. It continuously refines its estimates based on vessel speed, heading, ground speed, and heel angle. Its primary goal is to significantly improve paddle wheel speed (navigation.speedThroughWater), where raw errors of 10% or more are common. It does this automatically from real-world observations—no manual calibration required. Leeway (navigation.leewayAngle) is derived from observed dynamics rather than formulas, and current is estimated only after speed is corrected, using a slowly adapting filter.
5
+
6
+ ## Quick Start
7
+ 1. Install / enable the plugin in the Signal K Server admin UI.
8
+ 2. Specify the dimensions of the correction table in the plugin settings.
9
+ 3. Enable the plugin.
10
+ 4. Open the plugin's WebApp from the Signal K App list.
11
+ 5. Watch the corrected speed, leeway, and current fields begin populating as data arrives. Initial stabilization may take some sailing at varying speeds and heel angles.
12
+
13
+ ## Requirements & Data Inputs
14
+ The plugin automatically starts collecting and learning once enabled, but waits the first ~60 seconds before producing estimates to stabilize.
15
+
16
+ Required paths:
17
+ - navigation.speedThroughWater (raw paddle wheel speed)
18
+ - navigation.speedOverGround
19
+ - navigation.courseOverGroundTrue (true)
20
+ - navigation.headingTrue (true)
21
+ - navigation.attitude
22
+
23
+ ## Output & Published Paths
24
+ After the initial ~60 second stabilization period the plugin starts sending the following Signal K paths:
25
+
26
+ | Purpose | Path(s) | Units | Notes |
27
+ |---------|---------|-------|-------|
28
+ | Corrected speed through water (magnitude) | navigation.speedThroughWater | m/s | navigation.speedThroughWater. Source attribute distinguishes plugin value from the original sensor. |
29
+ | Leeway angle | navigation.leewayAngle | rad | Angle (starboard positive) associated with corrected STW vector. |
30
+ | Water current speed | self.environment.current.drift | m/s | Magnitude of estimated water current over ground. |
31
+ | Water current set (direction) | self.environment.current.setTrue | rad | True direction (set) the current flows toward. |
32
+
33
+ Boat speed overwrite / duplication behavior:
34
+ - When the setting "Prevent duplication of boat speed" = true (default): plugin overwrites navigation.speedThroughWater with corrected value.
35
+ - When false: both raw and corrected updates exist under navigation.speedThroughWater with distinct source attributes (viewable in deltas / data browser) allowing comparison.
36
+
37
+ Current vector representation: Published as polar (drift + set). Set angle is the direction the water moves toward (oceanographic convention). Verify tooling expectations if it assumes FROM direction.
38
+
39
+
40
+ ## The correction table
41
+ At the heart of the plugin is a correction table that provides a correction vector based on observed boat speed and heel. The correction table is built and maintained by the plugin itself, no user input is required. The plugin can even build a correction table on tidal waters, although this feature is experimental and may not work properly under all circumstances.
42
+
43
+ Every time the paddle wheel provides a measurement the plugin does two things. First it corrects boat speed and estimates leeway and current using the correction table. Second, using boat speed, ground speed and current estimation the correction table is updated. So the correction table is used and updated simultaneously.
44
+
45
+ ## Configuration
46
+ All settings are managed from the Signal K Server plugin configuration UI.
47
+
48
+ ### Core
49
+ | Setting | Default | Description | When to Change |
50
+ |---------|---------|-------------|----------------|
51
+ | Estimate Boat Speed | false | Enables speed correction, leeway and current estimation. | Normally on. |
52
+ | Update Correction Table | true | Learns / refines correction table. | Temporarily disable in abnormal conditions (heavy seas, abnormal current). |
53
+ | Start with new table | false | Discards existing table and starts fresh. | Only if you want to start all over with correcting. |
54
+
55
+ ### Learning & Stability
56
+ | Setting | Default | Description | Guidance |
57
+ |---------|---------|-------------|----------|
58
+ | Correction Table Stability | 7 | Higher = slower changes (more conservative). | Increase once table broadly populated; lower briefly if you think the sensor error has changed. |
59
+ | Assume Current (experimental) | false | Learns table while assuming a current may be present. | Enable cautiously on tidal waters. |
60
+
61
+ ### Table Definition
62
+ | Setting | Default | Description |
63
+ |---------|---------|-------------|
64
+ | Step size for speed | 1 kn | Speed increment for table rows. |
65
+ | Maximum speed in table | 9 kn | Upper speed coverage. |
66
+ | Step size for heel | 8° | Heel increment for table columns (positive & negative). |
67
+ | Maximum heel in table | 32° | Max absolute heel covered (± value). |
68
+
69
+ ### Data Sources (optional overrides)
70
+ | Setting | Description |
71
+ |---------|-------------|
72
+ | Heading Source | Force a specific source for navigation.headingTrue. |
73
+ | Boat Speed Source | Force a specific source for navigation.speedThroughWater. |
74
+ | Course over ground (COG Source) | Source for navigation.courseOverGroundTrue. |
75
+ | Ground Speed Source (SOG Source) | Source for navigation.speedOverGround. |
76
+ | Attitude Source | Source for navigation.attitude (roll/heel). |
77
+
78
+ If there are multiple sources available for a single path, one should specify the preferred source for the plugin. Alternatively, one could set source priorities in the signalK server settings (under data sources).
79
+
80
+ ### Output Behavior
81
+ | Setting | Default | Description | Notes |
82
+ |---------|---------|-------------|-------|
83
+ | Prevent duplication of boat speed | true | Overwrites navigation.speedThroughWater with corrected value. | Disable to compare raw vs corrected via distinct source attributes under same path. |
84
+
85
+ ### Tips
86
+ 1. Be patient with the correction table. Its corrections will improve over time.
87
+ 2. Once corrections stabilize, raise Stability to lock in values.
88
+ 3. Freeze learning (disable Update Correction Table) before performance-sensitive events (e.g., racing) or in unstable conditions (waves, currents).
89
+ 4. After paddle wheel cleaning or if fouling suspected: temporarily LOWER Stability (e.g., from 7 → 5) to let corrections adapt, then raise again; do not rebuild unless the grid itself must change.
90
+ 5. The plugin will always estimate currents when Estimate boat speed is enabled. Even when Assume currents is disabled, as this setting is only used in updating the correction table.
91
+
92
+ ## Persistence & Data Storage
93
+ The correction table is persisted inside the plugin configuration (saved approximately every 5 seconds while learning). It is reloaded automatically on restart.
94
+
95
+ Safety & backup:
96
+ - If you have a well-populated table, consider copying the plugin configuration JSON (via server backup) before major changes.
97
+
98
+ ## Experimental Note
99
+ The "Assume Current" option is experimental. It works best on a stable correction table as it takes time for the plugin to properly distinguish currents from paddle wheel eerors.
100
+
101
+ ## WebApp Usage Guide
102
+ Open via Signal K Server: Apps → Speed and current.
103
+
104
+ ### Layout & Elements
105
+ | Element | What You See | Notes |
106
+ |---------|--------------|-------|
107
+ | Graphical vector panel | Heading, raw & corrected boat speed, ground speed, current, correction, residual | Drawn with consistent color coding (see below). |
108
+ |Deltas| the different inputs and outputs to the plugin along with their uncertainty (trace)| For some paths also smoothed values are shown. The smoothed values are used to update the correction table|
109
+ | Correction table grid | 2D heel (columns) × speed (rows) cells containing correction vectors | Displays interpolated usage & last-updated cell highlighting. |
110
+
111
+ - Labels and graphical vectors use corresponding colors.
112
+ - Green text (selectedCell): the most recently updated table cell.
113
+ - Blue background shading: cells contributing to the current interpolation. Deeper blue = higher interpolation weight.
114
+ - Empty cells: not yet populated by learning (appear without vector magnitude / direction details initially).
115
+ - Trace is an indication of the uncertainty of the measurement or estimatin. The higher the Trace the less reliable the corresponding value is.
116
+
117
+ ### The menu
118
+ The menu allows you to:
119
+ - select the unit of choice for speed and angle
120
+ - select the values to be displayed in the correction table.
121
+ - Correction. The correction vector (x,y).
122
+ - Factor. The factor for correcting the speed component.
123
+ - Leeway. The leeway angle.
124
+ - Trace. The uncertainty of the correction vector.
125
+ - N. The number of observations the correction vector is based on.
126
+ - select the color mode for the correction table:
127
+ - None. No background colring of the correction table.
128
+ - Weight. Colors cells according to their contribution in speed correction, the darker the cell the more weight is givin to its correction vector.
129
+ - Leeway. Colors cells according to the amount of estimated leeway. The darker, the more leeway.
130
+ - Factor. Colors cell according the the amount of forward speed correction (ignoring leeway).
131
+ - Trace. Colors cells according to their uncertainty, the lighter the cell the more uncertain its correction vector.
132
+
133
+
134
+
135
+ ### Interpreting Vectors
136
+ - Corrected Speed vs Raw: Convergence occurs when their magnitudes diverge by a stable, plausible bias and the correction vector stops oscillating wildly.
137
+ - The corrected speed includes leeway, it therefore is not aligned with the ships axis.
138
+ - Residual: This is the part of the observed speed that is not explained by the sensor error or current estimation. Should trend toward small values as learning progresses.
139
+
140
+ ### Correction Table Health Checklist
141
+ - Leeway ≈ 0 at zero heel; increases logically with heel. Non‑zero leeway at zero heel may indicate heading offset.
142
+ - Correction vectors should be visually consistent with adjacent cells (direction & magnitude trend smoothly).
143
+ - Port / starboard symmetry: similar magnitudes at ± matching heel angles. Large asymmetry can indicate paddle wheel misalignment with the longitudal axis of the boat or off‑center mounting.
144
+
145
+
146
+
147
+ ## How It Works
148
+
149
+
150
+ ### 1. Speed Correction
151
+ - Vessel speed errors depend on **heel** and **speed** and are treated as locally constant within grid cells.
152
+ - The correction table stores a vector (magnitude & direction of sensor error) per (speed, heel) cell.
153
+ - A correction vector is computed by weighting (interpolation) of the nearest populated cells.
154
+ - The corrected speed-through-water vector = raw STW vector + correction vector (sign chosen so magnitude bias is reduced).
155
+
156
+ ### 2. Leeway Estimation
157
+ - Leeway is derived from the angular difference between the corrected STW direction and the hull axis / heading reference.
158
+
159
+ ### 3. Current Estimation
160
+ - The water current is estimated as a **slowly changing** ground-plane vector.
161
+ - A Kalman filter updates the current using residual between ground speed (GPS) and the heading-rotated corrected boat speed, weighted by estimated variances.
162
+ - ![current estimation model](https://github.com/Asw1n/speedandcurrent/raw/main/currentModel.png)
67
163
 
68
- ---
164
+ ### 4. Correction Estimating
165
+ - Each populated cell maintains its own lightweight Kalman filter.
166
+ - For each incoming raw boat speed sample the instantaneous sensor error candidate is derived from boat speed, heel, heading, ground speed and (optionally) current.
167
+ - That error sample updates the cell’s Kalman state (mean correction vector + variance).
168
+ - Sensor and estimation variances are continuously updated; the displayed Trace in the webApp reflects confidence (higher = less reliable).
169
+ - ![correction model](https://github.com/Asw1n/speedandcurrent/raw/main/correctionModel.png)
69
170
 
70
- ## **How It Works**
171
+ ## Glossary
172
+ - Set: Direction the water current flows toward (true angle).
173
+ - Drift: Magnitude (speed) of the water current over ground.
174
+ - Leeway: Lateral angular difference between boat’s heading axis and its actual movement through water (starboard positive).
175
+ - Residual: Remaining ground-speed vector difference after applying corrected STW and estimated current; indicator of modeling error and sensor noise.
176
+ - Stability (setting): Damps rate of change in learned corrections; higher = slower adaptation.
177
+ - Trace: Reported uncertainty indicator derived from internal variance (higher = less confidence in that vector/cell).
178
+ - Kalman filter: An adaptive algorithm that estimates the true value of a variable (such as speed or current) by combining noisy measurements and prior estimates, updating both the value and its uncertainty over time. Used here to refine correction vectors (cell-by-cell) and current estimates .
71
179
 
72
- ### **1. Speed Correction Model**
73
- - Vessel speed errors depend on **heel** and **speed** and are assumed constant for a given speed and heel angle.
74
- - A correction table stores speed errors for steps in speed and heel angle.
75
- - Each cell in the correction table contains a **Kalman filter** that updates speed corrections with new measurements.
76
- - The speed measurement is corrected by subtracting the correction from the correction table. The correction is **interpolated** based on nearby table cells, weighted by the uncertainty provided by the Kalman filter.
77
- - ![correction model](https://github.com/Asw1n/speedandcurrent/raw/main/correctionModel.png)
180
+ ### Coordinate Frames
181
+ - Boat Plane: Vectors relative to hull axis (used for raw & corrected STW, leeway angle computation).
182
+ - Ground Plane: Vectors in earth-referenced frame (used for GPS ground speed, current, residual).
78
183
 
79
- ### **2. Current Estimation Model**
80
- - The water current is estimated as a **slowly changing** vector.
81
- - A **Kalman filter** updates the current based on the difference between GPS speed and corrected speed.
82
- - ![current estimation model](https://github.com/Asw1n/speedandcurrent/raw/main/currentModel.png)
83
184
 
84
185
  ---
85
186
 
@@ -10,13 +10,79 @@ class CorrectionTable extends Table2D{
10
10
 
11
11
  static fromJSON(data, stability) {
12
12
  const table = new CorrectionTable(data.id, data.row, data.col, stability);
13
- table.table = data.table.map(row => row.map(cellData => CorrectionEstimator.fromJSON(cellData)));
13
+ table.table = data.table.map(row => row.map(cellData => CorrectionEstimator.fromJSON(cellData, stability)));
14
14
  return table;
15
15
  }
16
16
 
17
+ /**
18
+ * Resample an existing table onto a new grid conservatively.
19
+ * - Seeds mean from oldTable.getCorrection at each new cell center
20
+ * - Seeds diagonal covariance with a per-axis floor
21
+ * - Sets index (N) = 0 so all cells can re-learn on the new grid
22
+ *
23
+ * @param {CorrectionTable} oldTable - Source table to sample from
24
+ * @param {{min:number,max:number,step:number}} newRow - New speed axis definition (SI units)
25
+ * @param {{min:number,max:number,step:number}} newCol - New heel axis definition (SI units)
26
+ * @param {number} [stability=5] - Stability passed to new table filter model
27
+ * @param {number} [varianceFloor=1e-4] - Floor applied to cov[0][0] and cov[1][1]
28
+ * @returns {CorrectionTable}
29
+ */
30
+ static resample(oldTable, newRow, newCol, stability = 5, varianceFloor = 1e-4) {
31
+ const newTable = new CorrectionTable("correctionTable", newRow, newCol, stability);
32
+
33
+ const nRows = Math.round((newRow.max - newRow.min) / newRow.step) + 1;
34
+ const nCols = Math.round((newCol.max - newCol.min) / newCol.step) + 1;
35
+
36
+ for (let i = 0; i < nRows; i++) {
37
+ const speed = newRow.min + i * newRow.step;
38
+ for (let j = 0; j < nCols; j++) {
39
+ const heel = newCol.min + j * newCol.step;
40
+
41
+ const { correction, variance } = oldTable.getCorrection(speed, heel);
42
+
43
+ const mean = [[(correction?.x ?? 0)], [(correction?.y ?? 0)]];
44
+ const covXX = Math.max(Number.isFinite(variance?.x) ? variance.x : 0, varianceFloor);
45
+ const covYY = Math.max(Number.isFinite(variance?.y) ? variance.y : 0, varianceFloor);
46
+ const covariance = [[covXX, 0], [0, covYY]];
47
+
48
+ // Decide initialization index based on coverage/support from old table
49
+ const inBounds = (
50
+ speed >= oldTable.min[0] && speed <= oldTable.max[0] &&
51
+ heel >= oldTable.min[1] && heel <= oldTable.max[1]
52
+ );
53
+ let supportCount = 0;
54
+ let effectiveN = 0;
55
+ if (Array.isArray(oldTable.neighbours)) {
56
+ for (const n of oldTable.neighbours) {
57
+ const N = n?.cell?.N || 0;
58
+ if (N > 0) supportCount++;
59
+ const w = Number.isFinite(n?.normWeight) ? n.normWeight : 0;
60
+ effectiveN += w * N;
61
+ }
62
+ }
63
+ // Heuristic: require in-bounds AND at least 2 learned neighbours AND some effective support
64
+ const index = (inBounds && supportCount >= 2 && effectiveN >= 1) ? 1 : 0;
65
+
66
+ // Seed prior with mean and conservative covariance; index as decided above
67
+ newTable.table[i][j].filterState = new State({ mean, covariance, index });
68
+ }
69
+ }
70
+ newTable.setDisplayAttributes({ label: "correction table" });
71
+ return newTable;
72
+ }
73
+
74
+ /**
75
+ * Convenience to resample from serialized JSON table data
76
+ */
77
+ static resampleFromJSON(data, newRow, newCol, stability = 5, varianceFloor = 1e-4) {
78
+ const oldTable = CorrectionTable.fromJSON(data, stability);
79
+ return CorrectionTable.resample(oldTable, newRow, newCol, stability, varianceFloor);
80
+ }
81
+
17
82
  constructor(id, row, col, stability=5) {
18
83
  super(id, row, col, CorrectionEstimator, CorrectionEstimator.getFilterModel(stability));
19
84
  this.lastUpdatedCell = null;
85
+ this.neighbours = [];
20
86
  }
21
87
 
22
88
  update(speed, heel, groundSpeed, current, boatSpeed, heading) {
@@ -57,6 +123,44 @@ class CorrectionTable extends Table2D{
57
123
  return { x:x, y:y };
58
124
 
59
125
  }
126
+
127
+ getCorrection(speed, heel) {
128
+ this.neighbours = this.findClosest(speed, heel, 5);
129
+ if (this.neighbours.length == 0) return { correction: {x: 0, y: 0}, variance: null };
130
+ // Compute the correction and variance from the neighbours
131
+ let x = 0;
132
+ let y = 0;
133
+ let varX = 0;
134
+ let varY = 0;
135
+ let totalWeight = 0;
136
+ for (const neighbour of this.neighbours) {
137
+ const { cell:correction, dist } = neighbour;
138
+ if (correction.N > 0) {
139
+ const weight = 1 / (dist + 1e-6);
140
+ neighbour.normWeight = weight;
141
+ x += correction.x * weight;
142
+ y += correction.y * weight;
143
+ varX += correction.covariance[0][0] * weight ** 2;
144
+ varY += correction.covariance[1][1] * weight ** 2;
145
+ totalWeight += weight;
146
+ }
147
+ }
148
+ for (const neighbour of this.neighbours) {
149
+ if (totalWeight > 0) neighbour.normWeight /= totalWeight;
150
+ }
151
+
152
+ const corrAndVar = { correction: { x, y }, variance: { x: varX, y: varY } };
153
+ if (totalWeight === 0) return corrAndVar;
154
+
155
+ // Compute the final correction and variance
156
+ x /= totalWeight;
157
+ y /= totalWeight;
158
+ varX /= totalWeight;
159
+ varY /= totalWeight;
160
+ this.totalWeight = totalWeight;
161
+
162
+ return { correction: { x, y }, variance: { x: varX, y: varY } };
163
+ }
60
164
 
61
165
  report() {
62
166
  return {
@@ -64,12 +168,46 @@ class CorrectionTable extends Table2D{
64
168
  row: { min: this.min[0], max: this.max[0], step: this.step[0] },
65
169
  col: { min: this.min[1], max: this.max[1], step: this.step[1] },
66
170
  table: this.table.map(row =>
67
- row.map(correction => {
171
+ row.map((correction, colIndex, rowArray) => {
68
172
  const cellReport = correction.report();
173
+ // Derive the bin coordinates from indices
174
+ const speedBin = this.min[0] + this.step[0] * this.table.indexOf(rowArray); // row axis represents speed
175
+ const heelBin = this.min[1] + this.step[1] * colIndex; // col axis represents heel
176
+ // Compute forward speed after longitudinal correction
177
+ const forward = speedBin + cellReport.x;
178
+ // Factor (forward relative to original speed); guard division by zero
179
+ const factor = speedBin > 0 ? forward / speedBin : null;
180
+ // Leeway angle based on sideways over forward; only if forward > 0
181
+ const leeway = (forward > 0 && cellReport.N > 0) ? Math.atan2(cellReport.y, forward) : null;
182
+ // Trace ( cov_xx + cov_yy ) when covariance available and N>0
183
+ let trace = null;
184
+ if (cellReport.N > 0) {
185
+ try {
186
+ const cov = correction.covariance;
187
+ if (cov && Array.isArray(cov) && cov[0] && cov[1] && Number.isFinite(cov[0][0]) && Number.isFinite(cov[1][1])) {
188
+ const a = cov[0][0];
189
+ const d = cov[1][1];
190
+ //trace = Math.sqrt(a*a + d*d);
191
+ trace = a + d;
192
+ }
193
+ } catch { /* silent */ }
194
+ }
195
+ cellReport.forward = forward;
196
+ cellReport.factor = factor;
197
+ cellReport.leeway = leeway;
198
+ cellReport.trace = trace;
199
+ cellReport.speedBin = speedBin;
200
+ cellReport.heelBin = heelBin;
69
201
  // Mark selected if this is the last updated cell
70
202
  cellReport.displayAttributes = {
71
203
  selected: correction === this.lastUpdatedCell
72
204
  };
205
+ const found = this.neighbours.find(n => n.cell === correction);
206
+ if (found) {
207
+ cellReport.displayAttributes.normWeight = found.normWeight;
208
+ } else {
209
+ cellReport.displayAttributes.normWeight = 0;
210
+ }
73
211
  return cellReport;
74
212
  })
75
213
  ),
@@ -94,12 +232,12 @@ class CorrectionEstimator {
94
232
  return {
95
233
  observation: {
96
234
  stateProjection: [[1, 0], [0, 1]], // observation matrix H
97
- covariance: [[10 ** stability, 0], [0, 10 ** stability]], //measurement noise R
235
+ covariance: [[1, 0], [0, 1]], //measurement noise R
98
236
  dimension: 2
99
237
  },
100
238
  dynamic: {
101
239
  transition: [[1, 0], [0, 1]], // state transition matrix F
102
- covariance: [1, 1],// process noise covariance matrix Q
240
+ covariance: [1/10**stability, 1/10**stability],// process noise covariance matrix Q
103
241
  }
104
242
  };
105
243
  }
@@ -113,7 +251,11 @@ class CorrectionEstimator {
113
251
  }
114
252
 
115
253
  update(groundSpeed, current, boatSpeed, heading) {
116
- if(groundSpeed.n < 2 || current.n < 2 || boatSpeed.n < 2) return;
254
+ if(groundSpeed.xVariance == null || groundSpeed.yVariance == null ||
255
+ current.xVariance == null || current.yVariance == null ||
256
+ boatSpeed.xVariance == null || boatSpeed.yVariance == null ) {
257
+ return false;
258
+ }
117
259
  // Rotation matrix for -theta
118
260
  const cosTheta = Math.cos(heading);
119
261
  const sinTheta = Math.sin(heading);
@@ -152,8 +294,9 @@ class CorrectionEstimator {
152
294
  groundCov[1][0] + currentCov[1][0] + boatCov[1][0],
153
295
  groundCov[1][1] + currentCov[1][1] + boatCov[1][1]],
154
296
  ];
155
-
156
297
  this.filterState = this.filter.filter({ previousCorrected: this.filterState, observation, observationCovariance });
298
+ //console.log("Filterstate:", this.filterState, "Covariance:", observationCovariance);
299
+ return true;
157
300
  }
158
301
 
159
302