speedandcurrent 1.1.0 → 1.4.2
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 +10 -0
- package/correctionTable.js +26 -14
- package/index.js +202 -174
- package/package.json +2 -2
- package/public/TableRenderer.js +8 -6
- package/public/app.js +29 -19
- package/public/icon.png +0 -0
- package/public/index.html +22 -14
- package/public/main.css +177 -0
- package/public/vectors.html +1 -1
- package/public/vectors.js +30 -50
- package/public/style.css +0 -184
- package/public/vectors.css +0 -59
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { MessageHandler, Polar, PolarDamped, SI, Reporter } = require('signalkutilities');
|
|
2
2
|
const { CorrectionTable } = require('./correctionTable.js');
|
|
3
3
|
const { KalmanFilter } = require('kalman-filter');
|
|
4
4
|
|
|
@@ -7,13 +7,25 @@ const { KalmanFilter } = require('kalman-filter');
|
|
|
7
7
|
|
|
8
8
|
module.exports = function (app) {
|
|
9
9
|
|
|
10
|
-
let unsubscribes = [];
|
|
11
10
|
let options = {};
|
|
12
11
|
let isRunning = false;
|
|
13
12
|
let corrTable = null;
|
|
14
13
|
let lastSave = null;
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
let heading = null;
|
|
15
|
+
let attitude = null;
|
|
16
|
+
let current = null;
|
|
17
|
+
let currentStat = null;
|
|
18
|
+
let boatSpeed = null;
|
|
19
|
+
let boatSpeedStat = null;
|
|
20
|
+
let correctedBoatSpeed = null;
|
|
21
|
+
let boatSpeedRefGround = null;
|
|
22
|
+
let groundSpeed = null;
|
|
23
|
+
let groundSpeedStat = null;
|
|
24
|
+
let speedCorrection = null;
|
|
25
|
+
let residual = null;
|
|
26
|
+
let reportFull = null;
|
|
27
|
+
let reportVector = null;
|
|
28
|
+
let table = null;
|
|
17
29
|
|
|
18
30
|
const plugin = {};
|
|
19
31
|
plugin.id = "SpeedAndCurrent";
|
|
@@ -63,6 +75,42 @@ module.exports = function (app) {
|
|
|
63
75
|
description: "Correction table maximum speed.",
|
|
64
76
|
default: 9
|
|
65
77
|
},
|
|
78
|
+
|
|
79
|
+
headingSource: {
|
|
80
|
+
type: "string",
|
|
81
|
+
title: "Heading Source",
|
|
82
|
+
description: "Source to use for navigation.heading (optional).",
|
|
83
|
+
default: " "
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
boatSpeedSource: {
|
|
87
|
+
type: "string",
|
|
88
|
+
title: "Boat Speed Source",
|
|
89
|
+
description: "Source to use for navigation.speedThroughWater (optional).",
|
|
90
|
+
default: " "
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
COGSource: {
|
|
94
|
+
type: "string",
|
|
95
|
+
title: "Course over ground",
|
|
96
|
+
description: "Source to use for navigation.courseOverGroundTrue (optional)",
|
|
97
|
+
default: " "
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
SOGSource: {
|
|
101
|
+
type: "string",
|
|
102
|
+
title: "Ground Speed Source",
|
|
103
|
+
description: "Source to use for navigation.speedOverGround (optional)",
|
|
104
|
+
default: " "
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
attitudeSource: {
|
|
108
|
+
type: "string",
|
|
109
|
+
title: "Attitude Source",
|
|
110
|
+
description: "Source to use for navigation.attitude (optional)",
|
|
111
|
+
default: " "
|
|
112
|
+
},
|
|
113
|
+
|
|
66
114
|
preventDuplication: {
|
|
67
115
|
type: "boolean",
|
|
68
116
|
title: "Prevent duplication of boat speed",
|
|
@@ -119,10 +167,10 @@ module.exports = function (app) {
|
|
|
119
167
|
maximum: 8
|
|
120
168
|
}
|
|
121
169
|
}
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
plugin.uiSchema = {
|
|
173
|
+
'ui:order': ["mode", "maxSpeed", "speedStep", "maxHeel", "heelStep", "headingSource", "boatSpeedSource", "COGSource", "SOGSource", "attitudeSource", "preventDuplication", "updateCorrectionTable", "estimateBoatSpeed", "correctionStability", "doStartFresh", "assumeCurrent", "estimateCurrent", "currentStability"],
|
|
126
174
|
doStartFresh: {
|
|
127
175
|
"ui:widget": "hidden",
|
|
128
176
|
},
|
|
@@ -141,7 +189,7 @@ module.exports = function (app) {
|
|
|
141
189
|
mode: {
|
|
142
190
|
"ui:widget": "select",
|
|
143
191
|
},
|
|
144
|
-
|
|
192
|
+
updateCorrectionTable: {
|
|
145
193
|
"ui:widget": "hidden",
|
|
146
194
|
},
|
|
147
195
|
estimateBoatSpeed: {
|
|
@@ -163,7 +211,8 @@ module.exports = function (app) {
|
|
|
163
211
|
"ui:widget": "hidden",
|
|
164
212
|
}
|
|
165
213
|
};
|
|
166
|
-
|
|
214
|
+
|
|
215
|
+
|
|
167
216
|
plugin.registerWithRouter = function (router) {
|
|
168
217
|
app.debug('registerWithRouter');
|
|
169
218
|
|
|
@@ -172,9 +221,8 @@ module.exports = function (app) {
|
|
|
172
221
|
res.status(503).json({ error: "Plugin is not running" });
|
|
173
222
|
}
|
|
174
223
|
else {
|
|
175
|
-
res.json(
|
|
224
|
+
res.json(reportFull.report());
|
|
176
225
|
}
|
|
177
|
-
|
|
178
226
|
});
|
|
179
227
|
|
|
180
228
|
router.get('/getVectors', (req, res) => {
|
|
@@ -182,30 +230,14 @@ module.exports = function (app) {
|
|
|
182
230
|
res.status(503).json({ error: "Plugin is not running" });
|
|
183
231
|
}
|
|
184
232
|
else {
|
|
185
|
-
|
|
186
|
-
const d = [heading, attitude];
|
|
187
|
-
const p = [groundSpeed, boatSpeedPolar, correctedBoatSpeed, current, speedCorrection, residual];
|
|
188
|
-
p.forEach(polar => {
|
|
189
|
-
v.polars.push({
|
|
190
|
-
id: polar.id,
|
|
191
|
-
plane: polar.plane,
|
|
192
|
-
label: polar.label,
|
|
193
|
-
speed: polar.pValue.speed,
|
|
194
|
-
angle: polar.pValue.angle,
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
d.forEach(delta => {
|
|
198
|
-
v.deltas.push({
|
|
199
|
-
id: delta.id,
|
|
200
|
-
value: delta.value,
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
res.json(v);
|
|
233
|
+
res.json(reportVector.report());
|
|
204
234
|
}
|
|
205
235
|
});
|
|
206
236
|
|
|
207
237
|
}
|
|
208
238
|
|
|
239
|
+
// ...existing code...
|
|
240
|
+
|
|
209
241
|
function saveTable(options, correctionTable) {
|
|
210
242
|
options.correctionTable = correctionTable.toJSON();
|
|
211
243
|
app.savePluginOptions(options, () => { app.debug('Correction table saved') });
|
|
@@ -214,124 +246,135 @@ module.exports = function (app) {
|
|
|
214
246
|
|
|
215
247
|
|
|
216
248
|
plugin.start = (opts) => {
|
|
217
|
-
app.
|
|
218
|
-
|
|
249
|
+
app.setPluginStatus("starting");
|
|
250
|
+
app.debug("plugin starting");
|
|
251
|
+
let outputs = [];
|
|
252
|
+
|
|
219
253
|
// make options survive to the stop function
|
|
220
254
|
options = opts;
|
|
221
255
|
loadPresets();
|
|
222
256
|
|
|
223
257
|
// correction table
|
|
224
|
-
|
|
225
|
-
|
|
258
|
+
table = loadTable(options);
|
|
226
259
|
|
|
227
|
-
// heading
|
|
228
|
-
// TBD: headingTrue or headingMagnetic
|
|
229
|
-
heading = new DeltaSubscribe("navigation.headingTrue");
|
|
230
|
-
heading.subscribe(app, plugin.id, unsubscribes);
|
|
231
|
-
heading.setDisplayAttributes("heading","heading");
|
|
232
260
|
|
|
233
|
-
//
|
|
234
|
-
|
|
261
|
+
// heading
|
|
262
|
+
heading = new MessageHandler("heading", "navigation.headingTrue", options.headingSource);
|
|
263
|
+
heading.subscribe(app, plugin.id, true);
|
|
264
|
+
heading.setDisplayAttributes({ label: "heading" });
|
|
265
|
+
headingStat = new PolarDamped("headingDamped", heading, 1);
|
|
266
|
+
heading.onChange = () => {
|
|
267
|
+
headingStat.sample();
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// attitude
|
|
271
|
+
attitude = new MessageHandler("attitude", "navigation.attitude", options.attitudeSource);
|
|
235
272
|
attitude.value = { pitch: 0, roll: 0, yaw: 0 }; //prevents errors when there is no attitude sensor
|
|
236
|
-
attitude.setDisplayAttributes("attitude"
|
|
237
|
-
attitude.subscribe(app, plugin.id,
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const currentStat = new PolarStat(current);
|
|
273
|
+
attitude.setDisplayAttributes({ label: "attitude" });
|
|
274
|
+
attitude.subscribe(app, plugin.id, true);
|
|
275
|
+
attitudeStat = new PolarDamped("attitudeDamped", attitude, 1);
|
|
276
|
+
attitude.onChange = () => {
|
|
277
|
+
attitudeStat.sample();
|
|
278
|
+
};
|
|
244
279
|
|
|
245
|
-
//
|
|
246
|
-
|
|
280
|
+
// current
|
|
281
|
+
current = new Polar("current", "self.environment.current.drift", "self.environment.current.setTrue");
|
|
282
|
+
current.setDisplayAttributes({ label: "current", plane: "Ground" });
|
|
283
|
+
current.setAngleRange('0to2pi');
|
|
284
|
+
currentStat = new PolarDamped("currentDamped", current, 1);
|
|
285
|
+
currentStat.setAngleRange('0to2pi');
|
|
286
|
+
outputs.push(current);
|
|
287
|
+
// Current should be initialised as no current
|
|
288
|
+
current.setVectorValue({ x: 0, y: 0 });
|
|
289
|
+
// There should be at least two samples, otherwise we can't calculate a valid speed
|
|
290
|
+
currentStat.sample();
|
|
291
|
+
currentStat.sample();
|
|
247
292
|
|
|
248
293
|
// boat speed
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
boatSpeedPolar = new PolarDeltaBase("navigation.speedThroughWater", "environment.wind.directionTruenavigation.leewayAngle");
|
|
260
|
-
boatSpeedPolar.setDisplayAttributes("boatSpeed", "ref_boat", "observed boat speed");
|
|
261
|
-
const boatSpeedStat = new PolarStat(boatSpeedPolar);
|
|
294
|
+
boatSpeed = new Polar("boatSpeed", "navigation.speedThroughWater", options.boatSpeedSource);
|
|
295
|
+
boatSpeed.subscribe(app, plugin.id, true, false, !options.preventDuplication || !options.estimateBoatSpeed);
|
|
296
|
+
boatSpeed.setDisplayAttributes({ label: "boat speed", plane: "Boat" });
|
|
297
|
+
boatSpeedStat = new PolarDamped("boatSpeedDamped", boatSpeed, 1);
|
|
298
|
+
boatSpeedStat.setDisplayAttributes({ label: "boat speed (damped)", plane: "Boat" });
|
|
299
|
+
boatSpeed.onChange = () => {
|
|
300
|
+
boatSpeedStat.sample();
|
|
301
|
+
};
|
|
262
302
|
|
|
263
|
-
correctedBoatSpeed = new
|
|
264
|
-
correctedBoatSpeed.setDisplayAttributes("
|
|
303
|
+
correctedBoatSpeed = new Polar("correctedBoatSpeed", "navigation.speedThroughWater", "navigation.leewayAngle");
|
|
304
|
+
correctedBoatSpeed.setDisplayAttributes({ label: "corrected boat speed", plane: "Boat" });
|
|
305
|
+
outputs.push(correctedBoatSpeed);
|
|
306
|
+
//const correctedBoatSpeedStat = new PolarDamped("correctedBoatSpeedDamped", correctedBoatSpeed);
|
|
265
307
|
|
|
266
|
-
|
|
267
|
-
boatSpeedRefGround.setDisplayAttributes("boatSpeedRefGround", "ref_ground","boat speed over ground");
|
|
308
|
+
// TODO: Add navigation.speedThroughWaterTransverse and navigation.speedThroughWaterLongitudinal
|
|
268
309
|
|
|
310
|
+
boatSpeedRefGround = new Polar("boatSpeedRefGround");
|
|
311
|
+
boatSpeedRefGround.setDisplayAttributes("boat speed over ground", "Ground");
|
|
269
312
|
|
|
270
313
|
// ground speed
|
|
271
|
-
groundSpeed = new
|
|
272
|
-
groundSpeed.subscribe(app, plugin.id,
|
|
273
|
-
groundSpeed.setDisplayAttributes(
|
|
274
|
-
|
|
314
|
+
groundSpeed = new Polar("groundSpeed", "navigation.speedOverGround", "navigation.courseOverGroundTrue", options.SOGSource, options.COGSource);
|
|
315
|
+
groundSpeed.subscribe(app, plugin.id, true, true, true);
|
|
316
|
+
groundSpeed.setDisplayAttributes({ label: "ground speed", plane: "Ground" });
|
|
317
|
+
groundSpeed.setAngleRange('0to2pi');
|
|
318
|
+
groundSpeedStat = new PolarDamped("groundSpeedDamped", groundSpeed, 1);
|
|
319
|
+
groundSpeedStat.setDisplayAttributes({ label: "ground speed (damped)", plane: "Ground" });
|
|
320
|
+
groundSpeedStat.setAngleRange('0to2pi');
|
|
321
|
+
groundSpeed.onChange = () => {
|
|
322
|
+
groundSpeedStat.sample();
|
|
323
|
+
};
|
|
275
324
|
|
|
276
325
|
// correction vector
|
|
277
|
-
speedCorrection = new
|
|
278
|
-
speedCorrection.setDisplayAttributes("correction", "
|
|
326
|
+
speedCorrection = new Polar("speedCorrection");
|
|
327
|
+
speedCorrection.setDisplayAttributes("speed correction", "Boat");
|
|
328
|
+
speedCorrection.setDisplayAttributes({ label: "speed correction", plane: "Boat" });
|
|
279
329
|
|
|
280
330
|
// residual
|
|
281
|
-
residual = new
|
|
282
|
-
residual.setDisplayAttributes("residual", "
|
|
331
|
+
residual = new Polar("residual");
|
|
332
|
+
residual.setDisplayAttributes({ label: "residual", plane: "Ground" });
|
|
283
333
|
|
|
284
334
|
// Make reporting object for webApp
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
dimension: 2
|
|
313
|
-
},
|
|
314
|
-
dynamic: {
|
|
315
|
-
transition: [[1, 0], [0, 1]], // state transition matrix F
|
|
316
|
-
covariance: [1, 1],// process noise covariance matrix Q
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
let boatSpeedEstimation = null;
|
|
335
|
+
reportFull = new Reporter();
|
|
336
|
+
reportFull.addDelta(heading);
|
|
337
|
+
reportFull.addAttitude(attitude);
|
|
338
|
+
reportFull.addPolar(groundSpeed);
|
|
339
|
+
reportFull.addPolar(boatSpeed);
|
|
340
|
+
//reportFull.addPolar(boatSpeedStat);
|
|
341
|
+
//reportFull.addPolar(groundSpeedStat);
|
|
342
|
+
reportFull.addPolar(speedCorrection);
|
|
343
|
+
reportFull.addPolar(correctedBoatSpeed);
|
|
344
|
+
reportFull.addPolar(current);
|
|
345
|
+
reportFull.addPolar(residual);
|
|
346
|
+
reportFull.addTable(table);
|
|
347
|
+
|
|
348
|
+
reportVector = new Reporter();
|
|
349
|
+
reportVector.addDelta(heading);
|
|
350
|
+
reportVector.addPolar(residual);
|
|
351
|
+
reportVector.addPolar(speedCorrection);
|
|
352
|
+
reportVector.addPolar(current);
|
|
353
|
+
reportVector.addPolar(correctedBoatSpeed);
|
|
354
|
+
reportVector.addPolar(boatSpeed);
|
|
355
|
+
reportVector.addPolar(groundSpeed);
|
|
356
|
+
|
|
357
|
+
boatSpeed.onChange = () => {
|
|
358
|
+
boatSpeedStat.sample();
|
|
359
|
+
calculate();
|
|
360
|
+
};
|
|
361
|
+
|
|
320
362
|
|
|
321
363
|
|
|
322
364
|
isRunning = true;
|
|
365
|
+
app.setPluginStatus("Running");
|
|
366
|
+
app.debug("Running");
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|
|
323
370
|
|
|
324
371
|
|
|
325
372
|
|
|
326
373
|
// Main function, estimates boatSpeed, leeway and current
|
|
327
|
-
function calculate(
|
|
374
|
+
function calculate() {
|
|
328
375
|
// prepare iteration
|
|
329
|
-
boatSpeedPolar.setVValue({ x: boatSpeed.value, y: 0 });
|
|
330
|
-
groundSpeedStat.sample();
|
|
331
|
-
currentStat.sample();
|
|
332
|
-
boatSpeedStat.sample();
|
|
333
376
|
heel = attitude.value.roll;
|
|
334
|
-
speed = boatSpeed.
|
|
377
|
+
speed = boatSpeed.magnitude;
|
|
335
378
|
theta = heading.value;
|
|
336
379
|
if (!Number.isFinite(heel)) {
|
|
337
380
|
app.debug("Heel is not a valid number, skipping calculation");
|
|
@@ -345,18 +388,11 @@ module.exports = function (app) {
|
|
|
345
388
|
app.debug("Heading is not a valid number, skipping calculation");
|
|
346
389
|
return;
|
|
347
390
|
}
|
|
348
|
-
reporter.newReport();
|
|
349
|
-
reporter.addOptions(options);
|
|
350
|
-
reporter.addPolar( groundSpeed);
|
|
351
|
-
reporter.addPolar( boatSpeedPolar);
|
|
352
|
-
reporter.addAttitude( attitude);
|
|
353
|
-
reporter.addDelta( heading);
|
|
354
391
|
|
|
355
392
|
// update correction table
|
|
356
393
|
if (options.updateCorrectionTable) {
|
|
357
394
|
table.update(speed, heel, groundSpeedStat, currentStat, boatSpeedStat, theta);
|
|
358
395
|
}
|
|
359
|
-
reporter.addTable(table, speed, heel);
|
|
360
396
|
|
|
361
397
|
// correct and estimate boat speed
|
|
362
398
|
estimateBoatSpeed();
|
|
@@ -369,8 +405,10 @@ module.exports = function (app) {
|
|
|
369
405
|
|
|
370
406
|
|
|
371
407
|
// Send calculated delta's
|
|
372
|
-
|
|
373
|
-
|
|
408
|
+
updates = [];
|
|
409
|
+
if (options.estimateCurrent) updates.push(current);
|
|
410
|
+
if (options.estimateBoatSpeed) updates.push(correctedBoatSpeed);
|
|
411
|
+
if (updates.length) Polar.send(app, plugin.id, updates);
|
|
374
412
|
|
|
375
413
|
// Save correction table
|
|
376
414
|
// periodically
|
|
@@ -382,31 +420,19 @@ module.exports = function (app) {
|
|
|
382
420
|
// estimates boat speed from observed boatSpeed and correction
|
|
383
421
|
function estimateBoatSpeed() {
|
|
384
422
|
cor = table.getKalmanCorrection(speed, heel);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
speedCorrection.setVValue(cor);
|
|
388
|
-
|
|
389
|
-
reporter.addPolar(speedCorrection);
|
|
423
|
+
correctedBoatSpeed.copyFrom(boatSpeed);
|
|
424
|
+
speedCorrection.setVectorValue(cor);
|
|
390
425
|
correctedBoatSpeed.add(speedCorrection);
|
|
391
|
-
const v = correctedBoatSpeed.getVValue();
|
|
392
|
-
const observation = [v.x, v.y];
|
|
393
|
-
boatSpeedEstimation = boatSpeedEstimator.filter({ previousCorrected: boatSpeedEstimation, observation });
|
|
394
|
-
correctedBoatSpeed.setKValue(boatSpeedEstimation);
|
|
395
|
-
reporter.addPolar(correctedBoatSpeed);
|
|
396
426
|
}
|
|
397
427
|
|
|
398
428
|
// estimates current from observed groundspeed and estimated boatspeed
|
|
399
429
|
function estimateCurrent() {
|
|
400
430
|
boatSpeedRefGround.copyFrom(correctedBoatSpeed);
|
|
401
431
|
boatSpeedRefGround.rotate(theta);
|
|
402
|
-
reporter.addPolar( boatSpeedRefGround);
|
|
403
432
|
current.copyFrom(groundSpeed);
|
|
404
433
|
current.substract(boatSpeedRefGround);
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
currentEstimation = currentEstimator.filter({ previousCorrected: currentEstimation, observation });
|
|
408
|
-
current.setKValue(currentEstimation);
|
|
409
|
-
reporter.addPolar( current);
|
|
434
|
+
currentStat.sample();
|
|
435
|
+
current.copyFrom(currentStat);
|
|
410
436
|
}
|
|
411
437
|
|
|
412
438
|
// Calculate what ground speed is observed that is not attributed to estimated boat speed or estimated current
|
|
@@ -416,14 +442,13 @@ module.exports = function (app) {
|
|
|
416
442
|
boatSpeedRefGround.rotate(theta);
|
|
417
443
|
residual.substract(boatSpeedRefGround);
|
|
418
444
|
residual.substract(current);
|
|
419
|
-
reporter.addPolar(residual);
|
|
420
445
|
}
|
|
421
446
|
|
|
422
447
|
// Make sure the table matches the settings
|
|
423
|
-
function enforceConsistancy(row, col
|
|
448
|
+
function enforceConsistancy(row, col) {
|
|
424
449
|
// function is bugged due to IS units vs knots and degrees
|
|
425
450
|
if (!options?.correctionTable) return false;
|
|
426
|
-
|
|
451
|
+
table = options.correctionTable;
|
|
427
452
|
if (!table) return false;
|
|
428
453
|
if (!table.row) return false;
|
|
429
454
|
if (table.row.min != row.min) return false;
|
|
@@ -439,28 +464,28 @@ module.exports = function (app) {
|
|
|
439
464
|
const row = { min: 0, max: SI.fromKnots(options.maxSpeed), step: SI.fromKnots(options.speedStep) };
|
|
440
465
|
const col = { min: -SI.fromDegrees(options.maxHeel), max: SI.fromDegrees(options.maxHeel), step: SI.fromDegrees(options.heelStep) };
|
|
441
466
|
let table;
|
|
442
|
-
if (
|
|
467
|
+
if (enforceConsistancy(row, col) && !options.doStartFresh) {
|
|
443
468
|
table = CorrectionTable.fromJSON(options.correctionTable, options.correctionStability);
|
|
444
469
|
app.debug("Correction table loaded");
|
|
445
470
|
lastSave = new Date();
|
|
446
471
|
}
|
|
447
472
|
else {
|
|
448
|
-
table = new CorrectionTable(row, col, options.correctionStability);
|
|
473
|
+
table = new CorrectionTable("correctionTable", row, col, options.correctionStability);
|
|
449
474
|
app.debug("Correction table created");
|
|
450
475
|
options.doStartFresh = false;
|
|
451
476
|
lastSave = saveTable(options, table);
|
|
452
477
|
}
|
|
453
|
-
table.setDisplayAttributes("
|
|
478
|
+
table.setDisplayAttributes({ label: "correction table" });
|
|
454
479
|
return table;
|
|
455
480
|
}
|
|
456
481
|
|
|
457
482
|
|
|
458
|
-
function loadPresets() {
|
|
483
|
+
function loadPresets() {
|
|
459
484
|
const modeEnum = plugin.schema.properties.mode.enum;
|
|
460
485
|
app.debug("Loading presests for mode: " + options.mode);
|
|
461
486
|
|
|
462
487
|
switch (modeEnum.indexOf(options.mode)) {
|
|
463
|
-
case
|
|
488
|
+
case -1:
|
|
464
489
|
app.debug("Invalid mode");
|
|
465
490
|
app.debug(options);
|
|
466
491
|
//options.mode = modeEnum[2];
|
|
@@ -473,7 +498,7 @@ module.exports = function (app) {
|
|
|
473
498
|
options.assumeCurrent = false;
|
|
474
499
|
options.estimateCurrent = false;
|
|
475
500
|
options.correctionStability = 5;
|
|
476
|
-
options.mode = modeEnum[2];
|
|
501
|
+
options.mode = modeEnum[2];
|
|
477
502
|
break;
|
|
478
503
|
case 1:
|
|
479
504
|
// new table, with current
|
|
@@ -484,7 +509,7 @@ module.exports = function (app) {
|
|
|
484
509
|
options.estimateCurrent = false;
|
|
485
510
|
options.correctionStability = 5;
|
|
486
511
|
options.currentStability = 7;
|
|
487
|
-
options.mode = modeEnum[3];
|
|
512
|
+
options.mode = modeEnum[3];
|
|
488
513
|
break;
|
|
489
514
|
case 2:
|
|
490
515
|
// fresh table, no current
|
|
@@ -494,7 +519,7 @@ module.exports = function (app) {
|
|
|
494
519
|
options.assumeCurrent = false;
|
|
495
520
|
options.estimateCurrent = false;
|
|
496
521
|
options.correctionStability = 6;
|
|
497
|
-
|
|
522
|
+
break;
|
|
498
523
|
case 3:
|
|
499
524
|
// fresh table, with current
|
|
500
525
|
options.doStartFresh = false;
|
|
@@ -512,7 +537,7 @@ module.exports = function (app) {
|
|
|
512
537
|
options.estimateBoatSpeed = true;
|
|
513
538
|
options.assumeCurrent = false;
|
|
514
539
|
options.estimateCurrent = false;
|
|
515
|
-
options.correctionStability =
|
|
540
|
+
options.correctionStability = 10;
|
|
516
541
|
break;
|
|
517
542
|
case 5:
|
|
518
543
|
// mature table, with current
|
|
@@ -521,7 +546,7 @@ module.exports = function (app) {
|
|
|
521
546
|
options.estimateBoatSpeed = true;
|
|
522
547
|
options.assumeCurrent = true;
|
|
523
548
|
options.estimateCurrent = true;
|
|
524
|
-
options.correctionStability =
|
|
549
|
+
options.correctionStability = 10;
|
|
525
550
|
options.currentStability = 3;
|
|
526
551
|
break;
|
|
527
552
|
case 6:
|
|
@@ -532,7 +557,7 @@ module.exports = function (app) {
|
|
|
532
557
|
options.assumeCurrent = true;
|
|
533
558
|
options.estimateCurrent = true;
|
|
534
559
|
options.currentStability = 2;
|
|
535
|
-
break;
|
|
560
|
+
break;
|
|
536
561
|
case 7:
|
|
537
562
|
// all manual configuration;
|
|
538
563
|
break;
|
|
@@ -550,30 +575,33 @@ module.exports = function (app) {
|
|
|
550
575
|
}
|
|
551
576
|
|
|
552
577
|
|
|
553
|
-
|
|
578
|
+
plugin.stop = () => {
|
|
554
579
|
return new Promise((resolve, reject) => {
|
|
555
580
|
try {
|
|
556
|
-
unsubscribes.forEach(f => f());
|
|
557
|
-
unsubscribes = [];
|
|
558
581
|
//corrTable.save(app);
|
|
559
582
|
if (corrTable != null) {
|
|
560
583
|
lastSave = saveTable(options, corrTable);
|
|
561
584
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
boatSpeed =
|
|
568
|
-
|
|
569
|
-
correctedBoatSpeed =
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
585
|
+
|
|
586
|
+
heading = heading?.terminate(app);
|
|
587
|
+
attitude = attitude?.terminate(app);
|
|
588
|
+
current = current?.terminate(app);
|
|
589
|
+
currentStat = null;
|
|
590
|
+
boatSpeed = boatSpeed?.terminate(app);
|
|
591
|
+
boatSpeedStat = null;
|
|
592
|
+
correctedBoatSpeed = correctedBoatSpeed?.terminate(app);
|
|
593
|
+
boatSpeedRefGround = boatSpeedRefGround?.terminate(app);
|
|
594
|
+
groundSpeed = groundSpeed?.terminate(app);
|
|
595
|
+
groundSpeedStat = null;
|
|
596
|
+
speedCorrection = speedCorrection?.terminate(app);
|
|
597
|
+
residual = residual?.terminate(app);
|
|
598
|
+
reportFull = null;
|
|
599
|
+
reportVector = null;
|
|
600
|
+
table = null;
|
|
601
|
+
app.setPluginStatus("Stopped");
|
|
602
|
+
app.debug("Stopped");
|
|
603
|
+
|
|
574
604
|
isRunning = false;
|
|
575
|
-
corrTable = null;
|
|
576
|
-
|
|
577
605
|
resolve();
|
|
578
606
|
} catch (error) {
|
|
579
607
|
reject(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "speedandcurrent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.2",
|
|
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": {
|
|
@@ -29,6 +29,6 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"kalman-filter": "^2.3.0",
|
|
32
|
-
"signalkutilities": "^1.
|
|
32
|
+
"signalkutilities": "^1.4.0"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/public/TableRenderer.js
CHANGED
|
@@ -28,11 +28,13 @@ class TableRenderer {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
render(data) {
|
|
31
|
-
const { row, col, table,
|
|
31
|
+
const {id, row, col, table, displayAttributes } = data;
|
|
32
32
|
this.data = data;
|
|
33
|
-
|
|
33
|
+
const label = displayAttributes && displayAttributes.label ? displayAttributes.label : "";
|
|
34
|
+
|
|
34
35
|
// Create a table element
|
|
35
36
|
const tableElement = document.createElement('table');
|
|
37
|
+
|
|
36
38
|
tableElement.id = id;
|
|
37
39
|
tableElement.classList.add('Table2D');
|
|
38
40
|
// create header row
|
|
@@ -41,7 +43,7 @@ class TableRenderer {
|
|
|
41
43
|
// Create other rows
|
|
42
44
|
var rIndex = 0;
|
|
43
45
|
for (let r = row.min; r <= row.max + 0.01; r += row.step) {
|
|
44
|
-
tableElement.appendChild(this.tableRow(r, rIndex, col, table
|
|
46
|
+
tableElement.appendChild(this.tableRow(r, rIndex, col, table));
|
|
45
47
|
rIndex++;
|
|
46
48
|
}
|
|
47
49
|
return tableElement;
|
|
@@ -54,7 +56,7 @@ class TableRenderer {
|
|
|
54
56
|
firstHeaderCell.innerText = label ? label : '';
|
|
55
57
|
firstHeaderCell.classList.add('TableRowHeader');
|
|
56
58
|
headerRow.appendChild(firstHeaderCell);
|
|
57
|
-
|
|
59
|
+
|
|
58
60
|
// Create header row
|
|
59
61
|
for (let c = col.min; c <= col.max + 0.01; c += col.step) {
|
|
60
62
|
const headerCell = document.createElement('th');
|
|
@@ -73,7 +75,7 @@ class TableRenderer {
|
|
|
73
75
|
return headerCell;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
tableRow(r, rIndex, col, table
|
|
78
|
+
tableRow(r, rIndex, col, table) {
|
|
77
79
|
var cIndex = 0;
|
|
78
80
|
const rowElement = document.createElement('tr');
|
|
79
81
|
rowElement.appendChild(this.rowLabel(r));
|
|
@@ -88,7 +90,7 @@ class TableRenderer {
|
|
|
88
90
|
else {
|
|
89
91
|
cell.classList.add('emptyCell');
|
|
90
92
|
}
|
|
91
|
-
if (
|
|
93
|
+
if (value.displayAttributes && value.displayAttributes.selected) cell.classList.add('selectedCell');
|
|
92
94
|
|
|
93
95
|
rowElement.appendChild(cell);
|
|
94
96
|
cIndex++;
|