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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- const { PolarStat, DeltaStat, DeltaBase, DeltaSubscribe, PolarDeltaBase, PolarDeltaCatch, PolarDeltaSubscribe, SI, Reporter } = require('signalkutilities');
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
- var residual, heading, attitude, boatSpeed, boatSpeedPolar, correctedBoatSpeed, groundSpeed, current, speedCorrection, reporter;
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
- plugin.uiSchema = {
125
- 'ui:order': ["mode", "maxSpeed", "speedStep", "maxHeel", "heelStep", "preventDuplication", "updateCorrectionTable", "estimateBoatSpeed", "correctionStability", "doStartFresh", "assumeCurrent", "estimateCurrent", "currentStability"],
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
- updateCorrectionTable: {
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(reporter?.getReport());
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
- const v = { deltas: [], polars: [] };
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.debug("plugin started");
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
- const table = loadTable(options);
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
- //attitude
234
- attitude = new DeltaSubscribe("navigation.attitude");
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","attitude");
237
- attitude.subscribe(app, plugin.id, unsubscribes);
238
-
239
- // current
240
- current = new PolarDeltaBase("self.environment.current.drift","self.environment.current.setTrue");
241
- current.setDisplayAttributes("current", "ref_ground", "current");
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
- // magnetic variance
246
- variance = new DeltaSubscribe("navigation.magneticVariation");
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
- if (options.preventDuplication) {
250
- // DeltaCatch needed here
251
- boatSpeed = new DeltaSubscribe("navigation.speedThroughWater");
252
- boatSpeed.subscribe(app, plugin.id, unsubscribes, calculate);
253
- }
254
- else {
255
- boatSpeed = new DeltaSubscribe("navigation.speedThroughWater");
256
- boatSpeed.subscribe(app, plugin.id, unsubscribes, calculate);
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 PolarDeltaBase("navigation.speedThroughWater", "environment.wind.directionTruenavigation.leewayAngle");
264
- correctedBoatSpeed.setDisplayAttributes("correctedSpeed", "ref_boat", "estimated boat Speed");
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
- boatSpeedRefGround = new PolarDeltaBase();
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 PolarDeltaSubscribe("navigation.speedOverGround", "navigation.courseOverGroundTrue");
272
- groundSpeed.subscribe(app, plugin.id, unsubscribes);
273
- groundSpeed.setDisplayAttributes("groundSpeed", "ref_ground", "observed speed over ground");
274
- const groundSpeedStat = new PolarStat(groundSpeed);
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 PolarDeltaBase();
278
- speedCorrection.setDisplayAttributes("correction", "ref_boat", "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 PolarDeltaBase();
282
- residual.setDisplayAttributes("residual", "ref_ground", "residual");
331
+ residual = new Polar("residual");
332
+ residual.setDisplayAttributes({ label: "residual", plane: "Ground" });
283
333
 
284
334
  // Make reporting object for webApp
285
- reporter = new Reporter();
286
-
287
- // filter for current
288
- stability = 10 ** (options.currentStability);
289
- const currentEstimator = new KalmanFilter({
290
- observation: {
291
- stateProjection: [[1, 0], [0, 1]], // observation matrix H
292
- covariance: [[1 , 0], [0, 1 ]], //measurement noise R
293
- dimension: 2
294
- },
295
- dynamic: {
296
- transition: [[1, 0], [0, 1]], // state transition matrix F
297
- covariance: [1 / stability, 1 / stability],// process noise covariance matrix Q
298
- init: {
299
- mean: [[0], [0]],
300
- covariance: [[1 / stability, 0], [0, 1 / stability]]
301
- }
302
- }
303
- });
304
- let currentEstimation = null;
305
-
306
- // filter for boat speed
307
- stability = 10 ** (1);
308
- const boatSpeedEstimator = new KalmanFilter({
309
- observation: {
310
- stateProjection: [[1, 0], [0, 1]], // observation matrix H
311
- covariance: [[1 * stability, 0], [0, 1 * stability]], //measurement noise R
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(timestamp) {
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.value;
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
- if (options.estimateCurrent) PolarDeltaBase.sendDeltas(app, plugin.id, [current]);
373
- if (options.estimateBoatSpeed) PolarDeltaBase.sendDeltas(app, plugin.id, [correctedBoatSpeed]);
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
- // app.debug(cor);
386
- correctedBoatSpeed.copyFrom(boatSpeedPolar);
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
- const v = current.getVValue();
406
- const observation = [v.x, v.y];
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
- const table = options.correctionTable;
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 ( enforceConsistancy(row, col) && !options.doStartFresh) {
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("correctionTable", "Speed / Heel");
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 -1:
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
- break;
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 = 8;
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 = 8;
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
- plugin.stop = () => {
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
- options = {};
563
-
564
- residual = null;
565
- heading = null;
566
- attitude = null;
567
- boatSpeed = null;
568
- boatSpeedPolar = null;
569
- correctedBoatSpeed = null;
570
- groundSpeed = null;
571
- current = null;
572
- speedCorrection = null;
573
- reporter = null;
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.1.0",
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.0.0"
32
+ "signalkutilities": "^1.4.0"
33
33
  }
34
34
  }
@@ -28,11 +28,13 @@ class TableRenderer {
28
28
  }
29
29
 
30
30
  render(data) {
31
- const { row, col, table, selected, id, label } = data;
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, selected));
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, selected) {
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 (selected[0] == rIndex && selected[1] == cIndex) cell.classList.add('selectedCell');
93
+ if (value.displayAttributes && value.displayAttributes.selected) cell.classList.add('selectedCell');
92
94
 
93
95
  rowElement.appendChild(cell);
94
96
  cIndex++;