speedandcurrent 1.1.0 → 1.3.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/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
+ reportFullheading = null;
15
+ reportFullattitude = null;
16
+ reportFullcurrent = null;
17
+ reportFullcurrentStat = null;
18
+ reportFullboatSpeed = null;
19
+ reportFullboatSpeedStat = null;
20
+ reportFullcorrectedBoatSpeed = null;
21
+ reportFullboatSpeedRefGround = null;
22
+ reportFullgroundSpeed = null;
23
+ reportFullgroundSpeedStat = null;
24
+ reportFullspeedCorrection = null;
25
+ reportFullresidual = null;
26
+ reportFullreportFull = null;
27
+ var reportVector = null;
28
+ var 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,161 @@ 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
+
219
252
  // make options survive to the stop function
220
253
  options = opts;
221
254
  loadPresets();
222
255
 
223
256
  // correction table
224
- const table = loadTable(options);
225
-
257
+ table = loadTable(options);
258
+
226
259
 
227
260
  // heading
228
- // TBD: headingTrue or headingMagnetic
229
- heading = new DeltaSubscribe("navigation.headingTrue");
230
- heading.subscribe(app, plugin.id, unsubscribes);
231
- heading.setDisplayAttributes("heading","heading");
261
+ heading = new MessageHandler("heading", "navigation.headingTrue", options.headingSource);
262
+ heading.subscribe(app, plugin.id, true);
263
+ heading.setDisplayAttributes({ label: "heading" });
232
264
 
233
- //attitude
234
- attitude = new DeltaSubscribe("navigation.attitude");
265
+ // attitude
266
+ attitude = new MessageHandler("attitude", "navigation.attitude", options.attitudeSource);
235
267
  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");
268
+ attitude.setDisplayAttributes({ label: "attitude" });
269
+ attitude.subscribe(app, plugin.id, true);
242
270
 
243
- const currentStat = new PolarStat(current);
244
-
245
- // magnetic variance
246
- variance = new DeltaSubscribe("navigation.magneticVariation");
271
+ // current
272
+ current = new Polar("current", "self.environment.current.drift", "self.environment.current.setTrue");
273
+ current.setDisplayAttributes({ label: "current", plane: "Ground" });
274
+ current.setAngleRange('0to2pi');
275
+ currentStat = new PolarDamped("currentDamped", current);
276
+ currentStat.setAngleRange('0to2pi');
277
+ currentStat.timeConstant = 60; // Set time constant for damping
278
+ // Current should be initialised as no current
279
+ current.setVectorValue({ x: 0, y: 0 });
280
+ // There should be at least two samples, otherwise we can't calculate a valid speed
281
+ currentStat.sample();
282
+ currentStat.sample();
247
283
 
248
284
  // 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
- }
285
+ boatSpeed = new Polar("boatSpeed", "navigation.speedThroughWater", options.boatSpeedSource);
286
+ boatSpeed.subscribe(app, plugin.id, true, false, !options.preventDuplication || !options.estimateBoatSpeed);
287
+ boatSpeed.setDisplayAttributes({ label: "boat speed", plane: "Boat" });
288
+ boatSpeedStat = new PolarDamped("boatSpeedDamped", boatSpeed);
289
+ boatSpeedStat.setDisplayAttributes({ label: "boat speed (damped)", plane: "Boat" });
258
290
 
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);
262
-
263
- correctedBoatSpeed = new PolarDeltaBase("navigation.speedThroughWater", "environment.wind.directionTruenavigation.leewayAngle");
264
- correctedBoatSpeed.setDisplayAttributes("correctedSpeed", "ref_boat", "estimated boat Speed");
265
-
266
- boatSpeedRefGround = new PolarDeltaBase();
267
- boatSpeedRefGround.setDisplayAttributes("boatSpeedRefGround", "ref_ground","boat speed over ground");
291
+ correctedBoatSpeed = new Polar("correctedBoatSpeed", "navigation.speedThroughWater", "environment.wind.directionTruenavigation.leewayAngle");
292
+ correctedBoatSpeed.setDisplayAttributes({ label: "corrected boat speed", plane: "Boat" });
293
+ //const correctedBoatSpeedStat = new PolarDamped("correctedBoatSpeedDamped", correctedBoatSpeed);
268
294
 
295
+ boatSpeedRefGround = new Polar("boatSpeedRefGround");
296
+ boatSpeedRefGround.setDisplayAttributes("boat speed over ground", "Ground");
269
297
 
270
298
  // 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);
299
+ groundSpeed = new Polar("groundSpeed", "navigation.speedOverGround", "navigation.courseOverGroundTrue", options.SOGSource, options.COGSource);
300
+ groundSpeed.subscribe(app, plugin.id, true, true, true);
301
+ groundSpeed.setDisplayAttributes({ label: "ground speed", plane: "Ground" });
302
+ groundSpeed.setAngleRange('0to2pi');
303
+ groundSpeedStat = new PolarDamped("groundSpeedDamped", groundSpeed);
304
+ groundSpeedStat.setDisplayAttributes({ label: "ground speed (damped)", plane: "Ground" });
305
+ groundSpeedStat.setAngleRange('0to2pi');
275
306
 
276
307
  // correction vector
277
- speedCorrection = new PolarDeltaBase();
278
- speedCorrection.setDisplayAttributes("correction", "ref_boat", "correction");
308
+ speedCorrection = new Polar("speedCorrection");
309
+ speedCorrection.setDisplayAttributes("speed correction", "Boat");
310
+ speedCorrection.setDisplayAttributes({ label: "speed correction", plane: "Boat" });
279
311
 
280
312
  // residual
281
- residual = new PolarDeltaBase();
282
- residual.setDisplayAttributes("residual", "ref_ground", "residual");
313
+ residual = new Polar("residual");
314
+ residual.setDisplayAttributes({ label: "residual", plane: "Ground" });
283
315
 
284
316
  // 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]]
317
+ reportFull = new Reporter();
318
+ reportFull.addDelta(heading);
319
+ reportFull.addAttitude(attitude);
320
+ reportFull.addPolar(groundSpeed);
321
+ reportFull.addPolar(boatSpeed);
322
+ //reportFull.addPolar(boatSpeedStat);
323
+ //reportFull.addPolar(groundSpeedStat);
324
+ reportFull.addPolar(speedCorrection);
325
+ reportFull.addPolar(correctedBoatSpeed);
326
+ reportFull.addPolar(current);
327
+ reportFull.addPolar(residual);
328
+ reportFull.addTable(table);
329
+
330
+ reportVector = new Reporter();
331
+ reportVector.addDelta(heading);
332
+ reportVector.addPolar(residual);
333
+ reportVector.addPolar(speedCorrection);
334
+ reportVector.addPolar(current);
335
+ reportVector.addPolar(correctedBoatSpeed);
336
+ reportVector.addPolar(boatSpeed);
337
+ reportVector.addPolar(groundSpeed);
338
+
339
+
340
+
341
+ app.debug("Analyzing input data");
342
+ let candidates = [heading, attitude, boatSpeed, groundSpeed];
343
+ // wait for all to settle
344
+ setTimeout(() => {
345
+ //app.debug(groundSpeed.magnitudeHandler);
346
+ //app.debug(groundSpeed.angleHandler);
347
+ // Check if all candidates get input data
348
+ candidates.forEach(candidate => {
349
+ if (candidate.lackingInputData()) {
350
+ app.setPluginStatus("Halted: insufficient input data");
351
+ app.debug("Insufficient input data for " + candidate.displayAttributes.label );
352
+ return;
301
353
  }
354
+ });
355
+
356
+ // determing slowest input path and use this as a heartbeat for calculations
357
+ candidates.sort((a, b) => (a.frequency || 0) - (b.frequency || 0));
358
+
359
+
360
+
361
+ if (!candidates[0].lackingInputData()) {
362
+ slowestInput = candidates[0];
363
+ app.debug("Using " + slowestInput.displayAttributes.label + " as heartbeat for calculations ( " + Math.round(candidates[0].frequency) + " updates per second)");
364
+ slowestInput.onChange = calculate;
365
+ if (slowestInput == groundSpeed) {
366
+ groundSpeed.onChange = () => {
367
+ groundSpeedStat.sample();
368
+ calculate();
369
+ };
370
+ }
371
+ else {
372
+ groundSpeed.onChange = () => {
373
+ groundSpeedStat.sample();
374
+ };
375
+ }
376
+ if (slowestInput == boatSpeed) {
377
+ boatSpeed.onChange = () => {
378
+ boatSpeedStat.sample();
379
+ calculate();
380
+ };
381
+ }
382
+ else {
383
+ boatSpeed.onChange = () => {
384
+ boatSpeedStat.sample();
385
+ };
386
+ }
387
+
388
+ isRunning = true;
389
+ app.setPluginStatus("Running");
390
+ app.debug("Running");
302
391
  }
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;
320
392
 
393
+ }, 5000);
321
394
 
322
- isRunning = true;
395
+
323
396
 
324
397
 
325
398
 
326
399
  // Main function, estimates boatSpeed, leeway and current
327
- function calculate(timestamp) {
400
+ function calculate() {
328
401
  // prepare iteration
329
- boatSpeedPolar.setVValue({ x: boatSpeed.value, y: 0 });
330
- groundSpeedStat.sample();
331
- currentStat.sample();
332
- boatSpeedStat.sample();
333
402
  heel = attitude.value.roll;
334
- speed = boatSpeed.value;
403
+ speed = boatSpeed.magnitude;
335
404
  theta = heading.value;
336
405
  if (!Number.isFinite(heel)) {
337
406
  app.debug("Heel is not a valid number, skipping calculation");
@@ -345,18 +414,11 @@ module.exports = function (app) {
345
414
  app.debug("Heading is not a valid number, skipping calculation");
346
415
  return;
347
416
  }
348
- reporter.newReport();
349
- reporter.addOptions(options);
350
- reporter.addPolar( groundSpeed);
351
- reporter.addPolar( boatSpeedPolar);
352
- reporter.addAttitude( attitude);
353
- reporter.addDelta( heading);
354
417
 
355
418
  // update correction table
356
419
  if (options.updateCorrectionTable) {
357
420
  table.update(speed, heel, groundSpeedStat, currentStat, boatSpeedStat, theta);
358
421
  }
359
- reporter.addTable(table, speed, heel);
360
422
 
361
423
  // correct and estimate boat speed
362
424
  estimateBoatSpeed();
@@ -369,8 +431,10 @@ module.exports = function (app) {
369
431
 
370
432
 
371
433
  // Send calculated delta's
372
- if (options.estimateCurrent) PolarDeltaBase.sendDeltas(app, plugin.id, [current]);
373
- if (options.estimateBoatSpeed) PolarDeltaBase.sendDeltas(app, plugin.id, [correctedBoatSpeed]);
434
+ updates = [];
435
+ if (options.estimateCurrent) updates.push(current);
436
+ if (options.estimateBoatSpeed) updates.push(correctedBoatSpeed);
437
+ if (updates.length) Polar.send(app, plugin.id, updates);
374
438
 
375
439
  // Save correction table
376
440
  // periodically
@@ -382,31 +446,19 @@ module.exports = function (app) {
382
446
  // estimates boat speed from observed boatSpeed and correction
383
447
  function estimateBoatSpeed() {
384
448
  cor = table.getKalmanCorrection(speed, heel);
385
- // app.debug(cor);
386
- correctedBoatSpeed.copyFrom(boatSpeedPolar);
387
- speedCorrection.setVValue(cor);
388
-
389
- reporter.addPolar(speedCorrection);
449
+ correctedBoatSpeed.copyFrom(boatSpeed);
450
+ speedCorrection.setVectorValue(cor);
390
451
  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
452
  }
397
453
 
398
454
  // estimates current from observed groundspeed and estimated boatspeed
399
455
  function estimateCurrent() {
400
456
  boatSpeedRefGround.copyFrom(correctedBoatSpeed);
401
457
  boatSpeedRefGround.rotate(theta);
402
- reporter.addPolar( boatSpeedRefGround);
403
458
  current.copyFrom(groundSpeed);
404
459
  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);
460
+ currentStat.sample();
461
+ current.copyFrom(currentStat);
410
462
  }
411
463
 
412
464
  // Calculate what ground speed is observed that is not attributed to estimated boat speed or estimated current
@@ -416,14 +468,13 @@ module.exports = function (app) {
416
468
  boatSpeedRefGround.rotate(theta);
417
469
  residual.substract(boatSpeedRefGround);
418
470
  residual.substract(current);
419
- reporter.addPolar(residual);
420
471
  }
421
472
 
422
473
  // Make sure the table matches the settings
423
- function enforceConsistancy(row, col ) {
474
+ function enforceConsistancy(row, col) {
424
475
  // function is bugged due to IS units vs knots and degrees
425
476
  if (!options?.correctionTable) return false;
426
- const table = options.correctionTable;
477
+ table = options.correctionTable;
427
478
  if (!table) return false;
428
479
  if (!table.row) return false;
429
480
  if (table.row.min != row.min) return false;
@@ -439,28 +490,28 @@ module.exports = function (app) {
439
490
  const row = { min: 0, max: SI.fromKnots(options.maxSpeed), step: SI.fromKnots(options.speedStep) };
440
491
  const col = { min: -SI.fromDegrees(options.maxHeel), max: SI.fromDegrees(options.maxHeel), step: SI.fromDegrees(options.heelStep) };
441
492
  let table;
442
- if ( enforceConsistancy(row, col) && !options.doStartFresh) {
493
+ if (enforceConsistancy(row, col) && !options.doStartFresh) {
443
494
  table = CorrectionTable.fromJSON(options.correctionTable, options.correctionStability);
444
495
  app.debug("Correction table loaded");
445
496
  lastSave = new Date();
446
497
  }
447
498
  else {
448
- table = new CorrectionTable(row, col, options.correctionStability);
499
+ table = new CorrectionTable("correctionTable", row, col, options.correctionStability);
449
500
  app.debug("Correction table created");
450
501
  options.doStartFresh = false;
451
502
  lastSave = saveTable(options, table);
452
503
  }
453
- table.setDisplayAttributes("correctionTable", "Speed / Heel");
504
+ table.setDisplayAttributes({ label: "correction table" });
454
505
  return table;
455
506
  }
456
507
 
457
508
 
458
- function loadPresets() {
509
+ function loadPresets() {
459
510
  const modeEnum = plugin.schema.properties.mode.enum;
460
511
  app.debug("Loading presests for mode: " + options.mode);
461
512
 
462
513
  switch (modeEnum.indexOf(options.mode)) {
463
- case -1:
514
+ case -1:
464
515
  app.debug("Invalid mode");
465
516
  app.debug(options);
466
517
  //options.mode = modeEnum[2];
@@ -473,7 +524,7 @@ module.exports = function (app) {
473
524
  options.assumeCurrent = false;
474
525
  options.estimateCurrent = false;
475
526
  options.correctionStability = 5;
476
- options.mode = modeEnum[2];
527
+ options.mode = modeEnum[2];
477
528
  break;
478
529
  case 1:
479
530
  // new table, with current
@@ -484,7 +535,7 @@ module.exports = function (app) {
484
535
  options.estimateCurrent = false;
485
536
  options.correctionStability = 5;
486
537
  options.currentStability = 7;
487
- options.mode = modeEnum[3];
538
+ options.mode = modeEnum[3];
488
539
  break;
489
540
  case 2:
490
541
  // fresh table, no current
@@ -494,7 +545,7 @@ module.exports = function (app) {
494
545
  options.assumeCurrent = false;
495
546
  options.estimateCurrent = false;
496
547
  options.correctionStability = 6;
497
- break;
548
+ break;
498
549
  case 3:
499
550
  // fresh table, with current
500
551
  options.doStartFresh = false;
@@ -532,7 +583,7 @@ module.exports = function (app) {
532
583
  options.assumeCurrent = true;
533
584
  options.estimateCurrent = true;
534
585
  options.currentStability = 2;
535
- break;
586
+ break;
536
587
  case 7:
537
588
  // all manual configuration;
538
589
  break;
@@ -550,30 +601,33 @@ module.exports = function (app) {
550
601
  }
551
602
 
552
603
 
553
- plugin.stop = () => {
604
+ plugin.stop = () => {
554
605
  return new Promise((resolve, reject) => {
555
606
  try {
556
- unsubscribes.forEach(f => f());
557
- unsubscribes = [];
558
607
  //corrTable.save(app);
559
608
  if (corrTable != null) {
560
609
  lastSave = saveTable(options, corrTable);
561
610
  }
562
- options = {};
563
-
564
- residual = null;
611
+
565
612
  heading = null;
566
613
  attitude = null;
614
+ current = null;
615
+ currentStat = null;
567
616
  boatSpeed = null;
568
- boatSpeedPolar = null;
617
+ boatSpeedStat = null;
569
618
  correctedBoatSpeed = null;
619
+ boatSpeedRefGround = null;
570
620
  groundSpeed = null;
571
- current = null;
621
+ groundSpeedStat = null;
572
622
  speedCorrection = null;
573
- reporter = null;
623
+ residual = null;
624
+ reportFull = null;
625
+ reportVector = null;
626
+ table =
627
+ app.setPluginStatus("Stopped");
628
+ app.debug("Stopped");
629
+
574
630
  isRunning = false;
575
- corrTable = null;
576
-
577
631
  resolve();
578
632
  } catch (error) {
579
633
  reject(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speedandcurrent",
3
- "version": "1.1.0",
3
+ "version": "1.3.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": {
@@ -29,6 +29,6 @@
29
29
  },
30
30
  "dependencies": {
31
31
  "kalman-filter": "^2.3.0",
32
- "signalkutilities": "^1.0.0"
32
+ "signalkutilities": "^1.3.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++;