signalk-polar-performance-plugin 0.0.23 → 0.0.26

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/README.md CHANGED
@@ -42,6 +42,7 @@ In the plugin configuration you can toggle the following options:
42
42
  - Wind angle for maximum speed at this wind speed
43
43
  - Fill up the ends of the polar diagram
44
44
  - Visualisation of the polar diagram
45
+ - Configurable damping alorithm on inputs
45
46
 
46
47
  ### To-do list
47
48
  - Improved interpolation
@@ -52,7 +53,6 @@ In the plugin configuration you can toggle the following options:
52
53
  -- Save polar info to file
53
54
  -- Save new record speed for angle in polar
54
55
  -- Determine if we're on a steady course to avoid fake records
55
- -- Dampening algoritms
56
56
  -- Configure the resolution of the polar diagram
57
57
  - Support multiple polar diagrams
58
58
  - Capture heel in polar diagram
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-polar-performance-plugin",
3
- "version": "0.0.23",
3
+ "version": "0.0.26",
4
4
  "description": "A plugin that calculates performance information based on a (CSV) polar diagram.",
5
5
  "main": "plugin/index.js",
6
6
  "scripts": {
package/plugin/index.js CHANGED
@@ -52,18 +52,19 @@ module.exports = function (app) {
52
52
  },
53
53
  dampingTWA: {
54
54
  type: 'number',
55
- title: 'True Wind Angle damping factor (0 = no damping, 1 = full damping)',
56
- default: 0
55
+ description: 'If data appears erratic or too sensitive, damping may be applied to amke information appear more stable. With damping set to 0, the data is presented in raw form wih no damping applied.',
56
+ title: 'True Wind Angle damping seconds',
57
+ default: 1
57
58
  },
58
59
  dampingTWS: {
59
60
  type: 'number',
60
- title: 'True Wind Speed damping factor (0 = no damping, 1 = full damping)',
61
- default: 0
61
+ title: 'True Wind Speed damping seconds',
62
+ default: 1
62
63
  },
63
64
  dampingBSP: {
64
65
  type: 'number',
65
- title: 'Boat speed damping factor (0 = no damping, 1 = full damping)',
66
- default: 0
66
+ title: 'Boat speed damping damping',
67
+ default: 1
67
68
  },
68
69
  csvTable: {
69
70
  type: "string",
@@ -157,11 +158,11 @@ module.exports = function (app) {
157
158
  deltas.forEach(delta => {
158
159
  // app.debug('handleData: %s', JSON.stringify(delta))
159
160
  if (delta.path == 'navigation.speedThroughWater') {
160
- STW = applyDamping (delta.value, 'BSP', options.dampingBSP || 0, 3)
161
+ STW = applyDamping (delta.value, 'BSP', options.dampingBSP || 0)
161
162
  BSP = STW
162
163
  // app.debug('speedThroughWater (STW): %d', STW)
163
164
  } else if (delta.path == 'navigation.speedOverGround') {
164
- SOG = applyDamping (delta.value, 'BSP', options.dampingBSP || 0, 3)
165
+ SOG = applyDamping (delta.value, 'BSP', options.dampingBSP || 0)
165
166
  BSP = SOG
166
167
  // app.debug('speedThroughWater (STW): %d', STW)
167
168
  } else if (delta.path == 'navigation.headingTrue') {
@@ -169,7 +170,7 @@ module.exports = function (app) {
169
170
  // app.debug('heading (HDG): %d', HDG)
170
171
  } else if (delta.path == 'environment.wind.speedTrue') {
171
172
  // applyDamping (Xn, unit, a, n)
172
- TWS = applyDamping (delta.value, 'TWS', options.dampingTWS || 0, 3)
173
+ TWS = applyDamping (delta.value, 'TWS', options.dampingTWS || 0)
173
174
  // app.debug('environment.wind.speedTrue (TWS): %d applyDamping: %d', delta.value, TWS)
174
175
  // app.debug('TWS: %d TWA: %d BSP: %d', msToKts(TWS), radToDeg(TWA)*port, msToKts(BSP))
175
176
  sendUpdates(getPerformanceData(TWS, TWA, BSP))
@@ -179,8 +180,8 @@ module.exports = function (app) {
179
180
  } else {
180
181
  port = 1
181
182
  }
182
- TWA = Math.abs(applyDamping (delta.value, 'TWA', options.dampingTWA || 0, 1))
183
- // app.debug('environment.wind.angleTrueWater (TWA): %s applyDamping: %s', radToDeg(delta.value).toFixed(0), radToDeg(TWA).toFixed(0))
183
+ TWA = Math.abs(applyDamping (delta.value, 'TWA', options.dampingTWA || 0))
184
+ app.debug('environment.wind.angleTrueWater (TWA): %s applyDamping: %s', radToDeg(delta.value).toFixed(0), radToDeg(TWA).toFixed(0))
184
185
  }
185
186
  })
186
187
  }
@@ -401,7 +402,7 @@ module.exports = function (app) {
401
402
  performance.polarSpeedRatio = BSP / performance.polarSpeed
402
403
  if (options.VMG == true) {
403
404
  performance.velocityMadeGood = Math.abs(BSP * Math.cos(TWA))
404
- performance.polarVelocityMadeGood = Math.abs(performance.polarSpeed * Math.cos(TWA))
405
+ performance.polarVelocityMadeGood = Math.abs(performance.polarSpeed * Math.cos(targetTWA))
405
406
  performance.polarVelocityMadeGoodRatio = performance.velocityMadeGood / performance.polarVelocityMadeGood
406
407
  }
407
408
  } else {
@@ -545,6 +546,23 @@ module.exports = function (app) {
545
546
  }
546
547
 
547
548
  // And now fill in some missing ends to avoid doing expensive calculations in the main loop
549
+ if (polar[0].tws > 0) {
550
+ // Add a 0 line to allow interpolation at very low wind speeds
551
+ app.debug('Add a 0 line to allow interpolation at very low wind speeds')
552
+ let Obj = {
553
+ tws: 0.0001,
554
+ 'Beat angle': polar[0]['Beat angle'],
555
+ 'Beat VMG': polar[0]['Beat VMG'],
556
+ 'Run angle': polar[0]['Run angle'],
557
+ 'Run VMG': polar[0]['Run VMG'],
558
+ twa: []
559
+ }
560
+ Object.keys(polar[0].twa).forEach(twaObj => {
561
+ Obj.twa.push({twa: twaObj.twa, tbs: 0})
562
+ })
563
+ polar.unshift(Obj)
564
+ }
565
+
548
566
  for (let index = 0; index < polar.length; index++) {
549
567
  // Sorted on angle, so first is lowest
550
568
  let tws = polar[index].tws
@@ -567,7 +585,7 @@ module.exports = function (app) {
567
585
  polar[index].twa = topArray.concat(twaArray)
568
586
  }
569
587
 
570
- for (let index = 0; index < polar.length-1; index++) {
588
+ for (let index = 0; index < polar.length; index++) {
571
589
  let tws = polar[index].tws
572
590
  let twaArray = polar[index].twa
573
591
  let highTBS = twaArray[twaArray.length-1].tbs
@@ -634,25 +652,31 @@ module.exports = function (app) {
634
652
  data.datasets[index].pointRadius.push(radius)
635
653
  }
636
654
  }
655
+ // Remove 0 kts line
656
+ data.datasets.shift()
637
657
  return data
638
658
  }
639
659
 
640
- function applyDamping (Xn, unit, a, n) {
641
- if (typeof damping[unit] == 'undefined') {
642
- // Doesn't exist yet, make array for unit
643
- damping[unit] = {Yn: []}
644
- }
645
- // Yn = a * Yn-1 + (1-a) * Xn
646
- let Yn = a * (damping[unit]['Yn'][n-1] || Xn) + (1-a) * Xn
647
- // Add Yn to begin of Yn array
648
- damping[unit]['Yn'].push(Yn)
649
- // Remove last value from Yn array if needed
650
- if (damping[unit]['Yn'].length > n+1) {
651
- damping[unit]['Yn'].shift()
652
- }
653
- // app.debug('Unit: %s array: %s', unit, damping[unit]['Yn'].join(','))
654
- return Yn
655
- }
660
+ function applyDamping (Xn, unit, RC) {
661
+ if (RC == 0) {
662
+ return Xn
663
+ } else {
664
+ if (typeof damping[unit] == 'undefined') {
665
+ // Doesn't exist yet, make obj for unit
666
+ damping[unit] = {Yn: Xn, dt: Date.now()}
667
+ }
668
+ // Calculate a
669
+ let dt = (Date.now() - damping[unit].dt)/1000
670
+ damping[unit].dt = Date.now()
671
+ let a = dt / (RC + dt)
672
+ // Yn = (1-a) * Yn-1 + a * Xn
673
+ let Yn = (1-a) * (damping[unit].Yn || Xn) + a * Xn
674
+ // Remember Yn
675
+ damping[unit].Yn = Yn
676
+ // app.debug('Unit: %s dt: %d a: %d obj: %s', unit, dt, a, JSON.stringify(damping[unit]))
677
+ return Yn
678
+ }
679
+ }
656
680
  }
657
681
 
658
682
  plugin.stop = function () {
@@ -689,7 +713,11 @@ function msToKts(ms) {
689
713
  }
690
714
 
691
715
  function roundDec (value) {
692
- value = Number(value.toFixed(3))
693
- return value
716
+ if (typeof value == 'undefined') {
717
+ return undefined
718
+ } else {
719
+ value = Number(value.toFixed(3))
720
+ return value
721
+ }
694
722
  }
695
723