signalk-polar-performance-plugin 0.0.14 → 0.0.16

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
@@ -37,12 +37,12 @@ In the plugin configuration you can toggle the following options:
37
37
  - Plugin option to use SOG as boat speed
38
38
  - Wind angle for maximum speed at this wind speed
39
39
  - Fill up the ends of the polar diagram
40
+ - Visualisation of the polar diagram
40
41
 
41
42
  ### To-do list
42
43
  - Improved interpolation
43
44
  - Make moment to do calculation smarter/configurable
44
45
  - API to see JSON of polar
45
- - Visualisation of the polar diagram
46
46
  - Extrapolation of polar data
47
47
  - Create polar from live data
48
48
  -- Save polar info to file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-polar-performance-plugin",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "A plugin that calculates performance information based on a (CSV) polar diagram.",
5
5
  "main": "plugin/index.js",
6
6
  "scripts": {
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "homepage": "https://github.com/htool/signalk-polar-performance-plugin#readme",
19
19
  "keywords": [
20
+ "signalk-webapp",
20
21
  "signalk-node-server-plugin",
21
22
  "signalk-category-instruments",
22
23
  "signalk-category-chart-plotters"
package/plugin/index.js CHANGED
@@ -28,6 +28,10 @@ module.exports = function (app) {
28
28
  type: 'boolean',
29
29
  title: 'Enable sending Target TWA (performance.targetAngle)'
30
30
  },
31
+ tackTrue: {
32
+ type: 'boolean',
33
+ title: 'Enable calculating Opposite Tack True (performance.tackTrue)'
34
+ },
31
35
  optimumWindAngle: {
32
36
  type: 'boolean',
33
37
  title: 'Enable calculation of Optimum Wind Angle (difference between TWA and beat/run angle (depends on beat/run angle)'
@@ -62,9 +66,23 @@ module.exports = function (app) {
62
66
  // Load polar table
63
67
  var polar = csvToPolarObject(options.csvTable)
64
68
  app.debug('polar: %s', JSON.stringify(polar))
69
+
70
+ plugin.registerWithRouter = function(router) {
71
+ // Will appear here; plugins/signalk-polar-performance-plugin/
72
+ app.debug("registerWithRouter")
73
+ router.get("/polar", (req, res) => {
74
+ res.contentType("application/json")
75
+ res.send(JSON.stringify(polar))
76
+ })
77
+ router.get("/chartData", (req, res) => {
78
+ res.contentType("application/json")
79
+ res.send(JSON.stringify(getChartData()))
80
+ })
81
+ }
82
+
65
83
 
66
84
  // Global variables
67
- var BSP, STW, TWA, TWS, port // Angles in rad, speed in m/s
85
+ var BSP, STW, TWA, targetTWA, TWS, HDG, port // Angles in rad, speed in m/s
68
86
  var halfPi = Math.PI / 2
69
87
 
70
88
  // Subscribe to paths
@@ -93,6 +111,12 @@ module.exports = function (app) {
93
111
  policy: 'instant'
94
112
  })
95
113
  }
114
+ if (options.tackTrue == true) {
115
+ localSubscription.subscribe.push({
116
+ path: 'navigation.headingTrue',
117
+ policy: 'instant'
118
+ })
119
+ }
96
120
 
97
121
  app.subscriptionmanager.subscribe(
98
122
  localSubscription,
@@ -120,6 +144,9 @@ module.exports = function (app) {
120
144
  SOG = delta.value
121
145
  BSP = SOG
122
146
  // app.debug('speedThroughWater (STW): %d', STW)
147
+ } else if (delta.path == 'navigation.headingTrue') {
148
+ HDG = delta.value
149
+ // app.debug('heading (HDG): %d', HDG)
123
150
  } else if (delta.path == 'environment.wind.speedTrue') {
124
151
  TWS = delta.value
125
152
  // app.debug('environment.wind.speedTrue (TWS): %d', TWS)
@@ -186,6 +213,11 @@ module.exports = function (app) {
186
213
  values.push({path: 'performance.maxSpeedAngle', value: roundDec(perfObj.maxSpeedAngle)})
187
214
  metas.push({path: 'performance.maxSpeedAngle', value: {"units": "rad"}})
188
215
  }
216
+ if (options.tackTrue == true) {
217
+ if (typeof perfObj.tackTrue != 'undefined') {
218
+ values.push({path: 'performance.tackTrue', value: roundDec(perfObj.tackTrue)})
219
+ }
220
+ }
189
221
 
190
222
  app.debug('sendUpdates: %s', JSON.stringify(values))
191
223
  app.handleMessage(plugin.id, {
@@ -218,6 +250,7 @@ module.exports = function (app) {
218
250
  let beatLower = polar[indexTWS]['Beat angle']
219
251
  let beatUpper = polar[indexTWS+1]['Beat angle']
220
252
  performance.beatAngle = beatLower + ((beatUpper - beatLower) * twsGapRatio)
253
+ targetTWA = performance.beatAngle
221
254
  }
222
255
  // Calculate optimum wind angle (B&G thing)
223
256
  if (options.optimumWindAngle == true) {
@@ -239,6 +272,7 @@ module.exports = function (app) {
239
272
  let runUpper = polar[indexTWS+1]['Run angle']
240
273
  //app.debug('runLower: %s runUpper: %s', runLower, runUpper)
241
274
  performance.runAngle = runLower + ((runUpper - runLower) * twsGapRatio)
275
+ targetTWA = performance.runAngle
242
276
  }
243
277
  if (options.optimumWindAngle == true) {
244
278
  if (typeof performance.runAngle != 'undefined') {
@@ -254,6 +288,21 @@ module.exports = function (app) {
254
288
  performance.runVMG = VMGLower + ((VMGUpper - VMGLower) * twsGapRatio)
255
289
  }
256
290
  }
291
+ // Calculate opposite Tack True
292
+ // app.debug('tackTrue: port: %d HDG: %d targetTWA: %d', port, radToDeg(HDG), radToDeg(targetTWA))
293
+ if (port) {
294
+ let tackTrue = HDG - targetTWA
295
+ if (tackTrue < 0) {
296
+ tackTrue = tackTrue + (2*Math.PI)
297
+ }
298
+ performance.tackTrue = tackTrue
299
+ } else {
300
+ let tackTrue = HDG + targetTWA
301
+ if (tackTrue > (2*Math.PI)) {
302
+ tackTrue = tackTrue - (2*Math.PI)
303
+ }
304
+ performance.tackTrue = tackTrue
305
+ }
257
306
  // Calculate polar target boat speed
258
307
 
259
308
  // Interpolate Define the 4 near data points
@@ -420,9 +469,9 @@ module.exports = function (app) {
420
469
  for (let index = 0; index < polar.length-1; index++) {
421
470
  let tws = polar[index].tws
422
471
  let beatVMG = 0
423
- let beatVMGelement = 0
472
+ let beatElement = 0
424
473
  let runVMG = 0
425
- let runVMGelement = 0
474
+ let runElement = 0
426
475
 
427
476
  let twaArray = polar[index].twa
428
477
  for (let element = 0; element < twaArray.length-1; element++) {
@@ -439,12 +488,12 @@ module.exports = function (app) {
439
488
  }
440
489
  }
441
490
  }
442
- app.debug('beatVMG for %d is %d (angel %d)', tws, twaArray[beatVMGelement].vmg, twaArray[beatVMGelement].twa)
443
- app.debug(' runVMG for %d is %d (angel %d)', tws, twaArray[runVMGelement].vmg, twaArray[runVMGelement].twa)
444
- polar[index]['Beat angle'] = twaArray[beatVMGelement].twa
445
- polar[index]['Beat VMG'] = twaArray[beatVMGelement].vmg
446
- polar[index]['Run angle'] = twaArray[runVMGelement].twa
447
- polar[index]['Run VMG'] = twaArray[runVMGelement].vmg
491
+ app.debug('beatVMG for %d is %d (angel %d)', tws, twaArray[beatElement].vmg, twaArray[beatElement].twa)
492
+ app.debug(' runVMG for %d is %d (angel %d)', tws, twaArray[runElement].vmg, twaArray[runElement].twa)
493
+ polar[index]['Beat angle'] = twaArray[beatElement].twa
494
+ polar[index]['Beat VMG'] = twaArray[beatElement].vmg
495
+ polar[index]['Run angle'] = twaArray[runElement].twa
496
+ polar[index]['Run VMG'] = twaArray[runElement].vmg
448
497
  }
449
498
 
450
499
  }
@@ -462,7 +511,8 @@ module.exports = function (app) {
462
511
  // Now put some extra values at the beginning
463
512
  var topArray = []
464
513
  for (let angle = 0; angle < lowTWA; angle = angle + degToRad(5)) {
465
- let tbs = (angle / lowTWA) * Math.pow(Math.cos((-1*lowTWA + angle)*3),3) * lowTBS
514
+ let tbs = (angle / lowTWA) * Math.pow(Math.cos((-1*lowTWA + angle)*2),2) * lowTBS
515
+ if (tbs < 0 ) { tbs = 0 }
466
516
  let Obj = {'twa': angle, 'tbs': tbs}
467
517
  topArray.push(Obj)
468
518
  }
@@ -492,6 +542,41 @@ module.exports = function (app) {
492
542
  // app.debug(JSON.stringify(polar))
493
543
  return (polar)
494
544
  }
545
+
546
+ function getChartData () {
547
+ const color = ["#0000FF","#3300FF","#6600FF","#9900FF","#CC00FF","#FF00FF","#0033FF","#3333FF","#6633FF","#9933FF","#CC33FF","#FF33FF","#0066FF","#3366FF","#6666FF","#9966FF","#CC66FF","#FF66FF"]
548
+
549
+ var data = {
550
+ labels: [],
551
+ datasets: []
552
+ }
553
+ for (let angle = 0; angle <= 180; angle += 5) {
554
+ data.labels.push(angle)
555
+ }
556
+ for (let index = 0; index < polar.length -1; index++) {
557
+ // Add x axis label TWS
558
+ let tws = msToKts(polar[index].tws).toFixed(0)
559
+ let twaArray = polar[index].twa
560
+ data.datasets[index] = {
561
+ data: [],
562
+ pointRadius: [],
563
+ borderColor: color[index],
564
+ fill: false,
565
+ parsing: false,
566
+ label: tws + ' kts'
567
+ }
568
+ for (let twaIndex = 0; twaIndex <= twaArray.length-1; twaIndex++) {
569
+ let twaObj = twaArray[twaIndex]
570
+ let radius = 2
571
+ if (twaObj.twa == polar[index]['Beat angle'] || twaObj.twa == polar[index]['Run angle']) {
572
+ radius = 5
573
+ }
574
+ data.datasets[index].data.push({x: Math.round(radToDeg(twaObj.twa)), y: Number(msToKts(twaObj.tbs).toFixed(2))})
575
+ data.datasets[index].pointRadius.push(radius)
576
+ }
577
+ }
578
+ return data
579
+ }
495
580
  }
496
581
 
497
582
  plugin.stop = function () {
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
4
+ <script type='text/javascript' src='/jquery/dist/jquery.min.js'></script>
5
+ <title>Polar performance</title>
6
+ <body>
7
+
8
+ <canvas id="myChart" style="width:100%;max-width:1200px"></canvas>
9
+
10
+ <script>
11
+ $.getJSON("/plugins/signalk-polar-performance-plugin/chartData", function(json) {
12
+ showChart(json)
13
+ })
14
+
15
+ function showChart (data) {
16
+ console.log(data)
17
+
18
+ new Chart("myChart", {
19
+ type: "scatter",
20
+ data: data,
21
+ options: {
22
+ legend: {display: true},
23
+ spanGaps: true,
24
+ scales: {
25
+ x: {
26
+ type: 'linear'
27
+ },
28
+ xAxes: [{
29
+ scaleLabel: {
30
+ display: true,
31
+ labelString: 'True wind angle (TWA)'
32
+ }
33
+ }],
34
+ yAxes: [{
35
+ scaleLabel: {
36
+ display: true,
37
+ labelString: 'Target boat speed (POL SPD)'
38
+ }
39
+ }],
40
+ }
41
+ }
42
+ })
43
+ }
44
+ </script>
45
+