signalk-racer 0.9.9 → 0.9.11

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
@@ -1,6 +1,10 @@
1
1
  # Signal K Racing Plugin
2
2
 
3
- This PROTOTYPE plugin adds sail racing-relevant data calculations to a Signal K server. It focuses on real-time information useful during the start of a race, such as distance to the line, start line bias, and geographic position of the line ends.
3
+ This plugin adds sail racing-relevant data to a Signal K server. It focuses on real-time information useful during the start of a race, such as:
4
+ + A server side race timer that can be displayed on multiple screens and be finely adjusted
5
+ + A mechanism to set and adjust the start line using waypoints and REST commands
6
+ + Calculation of the distance to the line.
7
+ + Calculation of the TWA for the next leg of the course.
4
8
 
5
9
  GitHub repository: [https://github.com/gregw/signalk-racer](https://github.com/gregw/signalk-racer)
6
10
 
@@ -10,24 +14,26 @@ GitHub repository: [https://github.com/gregw/signalk-racer](https://github.com/g
10
14
 
11
15
  This plugin calculates and publishes the following Signal K paths:
12
16
 
13
- | Path | Description | Units | Std |
14
- |----------------------------------------------------|---------------------------------------------------------|-------|-----|
15
- | `navigation.racing.`<br/>`distanceStartline` | Signed minimum distance from the bow to the start line | `m` | Y |
16
- | `navigation.racing.`<br/>`startLineLength` | Total length of the start line | `m` | |
17
- | `navigation.racing.`<br/>`stbLineBias` | Bias of the start line toward the starboard end | `m` | |
18
- | `navigation.racing.`<br/>`startLinePort.latitude` | Latitude of the port (pin) end of the start line | `deg` | Y |
19
- | `navigation.racing.`<br/>`startLinePort.longitude` | Longitude of the port (pin) end of the start line | `deg` | Y |
20
- | `navigation.racing.`<br/>`startLineStb.latitude` | Latitude of the starboard (boat) end of the start line | `deg` | Y |
21
- | `navigation.racing.`<br/>`startLineStb.longitude` | Longitude of the starboard (boat) end of the start line | `deg` | Y |
22
- | `navigation.racing.`<br/>`nextLegHeading` | True heading for the next leg of the course | `deg` | |
23
- | `navigation.racing.`<br/>`nextLegTWA` | True Wind Angle for the next leg of the course | `deg` | |
24
- | `navigation.racing.`<br/>`timeToStart` | Period of time until the race start | `s` | Y |
25
- | `navigation.racing.`<br/>`startTime` | The start time as an ISO timestamp | `iso` | |
26
-
27
- These values can be displayed in KIP, Freeboard-SK, or other Signal K clients.
17
+ | Path | Description | Units | Std |
18
+ |-------------------------------------------------|--------------------------------------------------------|------------------------|-----|
19
+ | `navigation.racing.`<br/>`distanceStartline` | Signed minimum distance from the bow to the start line | `m` | Y |
20
+ | `navigation.racing.`<br/>`startLineLength` | Total length of the start line | `m` | |
21
+ | `navigation.racing.`<br/>`stbLineBias` | Bias of the start line toward the starboard end | `m` | |
22
+ | `navigation.racing.`<br/>`startLinePort` | Location of the port (pin) end of the start line | `{latitude,longitude}` | Y |
23
+ | `navigation.racing.`<br/>`startLineStb` | Location of the starboard (boat) end of the start line | `{latitude,longitude}` | Y |
24
+ | `navigation.racing.`<br/>`nextLegHeading` | True heading for the next leg of the course | `rad` | |
25
+ | `navigation.racing.`<br/>`nextLegTrueWindAngle` | True Wind Angle for the next leg of the course | `rad` | |
26
+ | `navigation.racing.`<br/>`timeToStart` | Period of time until the race start | `s` | Y |
27
+ | `navigation.racing.`<br/>`startTime` | The start time as an ISO timestamp | `rfc3339` | |
28
+
29
+ These values can be displayed in KIP widgets, Freeboard-SK, or other Signal K clients.
28
30
 
29
31
  ![KIP and Freeboard Screenshot](kip-freeboard-screenshot.png)
30
32
 
33
+ There is also a webapp to adjust the line and timer.
34
+
35
+ ![Signalk Racer Webapp](racer-webapp.png)
36
+
31
37
  ---
32
38
 
33
39
  ## ⚙️ Configuration
@@ -36,11 +42,14 @@ You can configure the plugin via the Signal K web interface or by editing `setti
36
42
 
37
43
  ### Parameters:
38
44
 
39
- | Parameter | Description | Default |
40
- |----------------|--------------------------------------------------------------------------|---------------|
41
- | `startLineStb` | Name of the waypoint for the starboard (committee boat) end of the line | `"startBoat"` |
42
- | `startLinePort` | Name of the waypoint for the port (pin) end of the line | `"startPin"` |
43
- | `period` | How often to update values (in milliseconds) | `1000` |
45
+ | Parameter | Description | Default |
46
+ |---------------------------|-------------------------------------------------------------------------|---------------|
47
+ | `startLineStb` | Name of the waypoint for the starboard (committee boat) end of the line | `"startBoat"` |
48
+ | `startLinePort` | Name of the waypoint for the port (pin) end of the line | `"startPin"` |
49
+ | `timer` | Initial time for the race timer (in seconds) | `300` |
50
+ | `period` | How often to update values (in milliseconds) | `1000` |
51
+ | `updateStartLineWaypoint` | Should the waypoints be updated if the line is set/adjusted | `true` |
52
+ | `createStartLineWaypoint` | Should the waypoints be created if the line is set | `true` |
44
53
 
45
54
  ---
46
55
 
@@ -79,7 +88,7 @@ If `navigation.headingTrue` is not available, then `navigation.courseOverGroundT
79
88
 
80
89
  ### Next Leg
81
90
  - If a course is active then the true bearing for the next leg is calculated as `navigation.racing.nextLegHeading`
82
- - If the `environment.wind.directionTrue` is available, then the True Wind Angle for the next leg is also calculated as `navigation.racing.nextLegHeading`
91
+ - If the `environment.wind.directionTrue` is available, then the True Wind Angle for the next leg is also calculated as `navigation.racing.nextLegTrueWindAngle`
83
92
  - If the next mark of the active route is beyond the first mark, then the start line calculations are suspended.
84
93
 
85
94
  ---
@@ -123,8 +132,16 @@ Used to **start**, **sync**, **reset**, or **set a fixed start time**.
123
132
  ```
124
133
  ---
125
134
 
135
+ ## 🧩 KIP Widgets
136
+ KIP components for `racer-timer` and `racer-line` have been developed and will be available in a future release of KIP.
137
+
126
138
  ## 🔄 Dependencies
127
139
 
140
+ ### Plugins
141
+ - [resources-provider](https://www.npmjs.com/package/@signalk/resources-provider) plugin with waypoints enabled
142
+ - [course-provider](https://www.npmjs.com/package/@signalk/course-provider) plugin
143
+
144
+ ### Libraries
128
145
  - `geolib`: for distance and bearing calculations.
129
146
 
130
147
  ---
@@ -134,7 +151,6 @@ Used to **start**, **sync**, **reset**, or **set a fixed start time**.
134
151
  - Add calculations for:
135
152
  - **Time to line** (using STW and heading or polar speeds)
136
153
  - **Laylines** and distance/time to them
137
- - A KIP widget as well as the webapp.
138
154
  - Better integration with **Freeboard-SK** and **KIP** dashboards
139
155
 
140
156
  ---
package/ReleaseNotes.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 0.9.11 Release Candidate
2
+ + Improved documentation
3
+
4
+ ## 0.9.10 Updates for KIP widgets
5
+ + Do not set a start time unless timer is running
6
+ + Schema RFC 3339 units for start time.
7
+
1
8
  ## 0.9.9 Working with KIP prototypes
2
9
  + Fixed API handlers to use return status rather than callback
3
10
 
package/index.js CHANGED
@@ -50,6 +50,7 @@ module.exports = (app) => {
50
50
  {
51
51
  "path": "navigation.racing.startLineLength",
52
52
  "value": {
53
+ "type": "number",
53
54
  "units": "m",
54
55
  "description": "Length of the start line",
55
56
  "displayName": "Length of the start line",
@@ -59,6 +60,7 @@ module.exports = (app) => {
59
60
  {
60
61
  "path": "navigation.racing.stbLineBias",
61
62
  "value": {
63
+ "type": "number",
62
64
  "units": "m",
63
65
  "description": "Bias of the start line for the starboard end",
64
66
  "displayName": "Bias of the start line for the starboard end",
@@ -68,6 +70,7 @@ module.exports = (app) => {
68
70
  {
69
71
  "path": "navigation.racing.nextLegHeading",
70
72
  "value": {
73
+ "type": "number",
71
74
  "units": "rad",
72
75
  "description": "Heading of the next leg of the course",
73
76
  "displayName": "Next Heading",
@@ -77,12 +80,26 @@ module.exports = (app) => {
77
80
  {
78
81
  "path": "navigation.racing.nextLegTrueWindAngle",
79
82
  "value": {
83
+ "type": "number",
80
84
  "units": "rad",
81
85
  "description": "TWA on the next leg of the course",
82
86
  "displayName": "Next TWA",
83
87
  "shortName": "NextTWA",
84
88
  }
85
89
  },
90
+ {
91
+ "path": "navigation.racing.startTime",
92
+ "value": {
93
+ "type": "string",
94
+ "units": "RFC 3339 (UTC)",
95
+ "example": "2014-04-10T08:33:53.123Z",
96
+ "format": "date-time",
97
+ "pattern" : ".*Z$",
98
+ "description": "Time of the race start in RFC 3339 UTC only format ",
99
+ "displayName": "Start Time",
100
+ "shortName": "StartTime",
101
+ }
102
+ },
86
103
  ]
87
104
  }
88
105
  ]
@@ -235,58 +252,62 @@ module.exports = (app) => {
235
252
 
236
253
  // Set/create the waypoint if we are configured to do so
237
254
  if (state.options.updateStartLineWaypoint || updateWaypoint) {
238
- const waypointConfig = `startLine${camelCase(end)}`;
239
- app.debug(`waypointConfig: ${waypointConfig}`);
240
- const waypointName = state.options[waypointConfig];
241
- app.debug(`waypointName: ${waypointName}`);
242
- let waypointId = startLine ? startLine[end + 'Id'] : null;
243
- app.debug(`waypointId: ${waypointId}`);
244
-
245
- // If we have not cached the Id of the line end, then
246
- if (!waypointId) {
247
- // Get the ID of the last existing waypoint with the given name.
248
- const waypoints = await app.resourcesApi.listResources('waypoints', {});
249
- for (const [id, resource] of Object.entries(waypoints)) {
250
- if (resource.name === waypointName) {
251
- waypointId = id;
252
- // don't break here as we want the last waypoint with the given name
253
- }
254
- }
255
+ try {
256
+ const waypointConfig = `startLine${camelCase(end)}`;
257
+ app.debug(`waypointConfig: ${waypointConfig}`);
258
+ const waypointName = state.options[waypointConfig];
259
+ app.debug(`waypointName: ${waypointName}`);
260
+ let waypointId = startLine ? startLine[end + 'Id'] : null;
255
261
  app.debug(`waypointId: ${waypointId}`);
256
- }
257
262
 
258
- app.debug(`Startline waypoint to set ${end}(${waypointName}/${waypointId}): ${JSON.stringify(position)}`);
263
+ // If we have not cached the Id of the line end, then
264
+ if (!waypointId) {
265
+ // Get the ID of the last existing waypoint with the given name.
266
+ const waypoints = await app.resourcesApi.listResources('waypoints', {});
267
+ for (const [id, resource] of Object.entries(waypoints)) {
268
+ if (resource.name === waypointName) {
269
+ waypointId = id;
270
+ // don't break here as we want the last waypoint with the given name
271
+ }
272
+ }
273
+ app.debug(`waypointId: ${waypointId}`);
274
+ }
259
275
 
260
- const waypoint = {
261
- name: waypointName,
262
- feature: {
263
- type: 'Feature',
264
- geometry: {
265
- type: 'Point',
266
- coordinates: [position.longitude, position.latitude]
276
+ app.debug(`Startline waypoint to set ${end}(${waypointName}/${waypointId}): ${JSON.stringify(position)}`);
277
+
278
+ const waypoint = {
279
+ name: waypointName,
280
+ feature: {
281
+ type: 'Feature',
282
+ geometry: {
283
+ type: 'Point',
284
+ coordinates: [position.longitude, position.latitude]
285
+ },
286
+ properties: {}
267
287
  },
268
- properties: {}
269
- },
270
- type: end === 'port' ? 'start-pin' : 'start-boat',
271
- position: {
272
- latitude: position.latitude,
273
- longitude: position.longitude
288
+ type: end === 'port' ? 'start-pin' : 'start-boat',
289
+ position: {
290
+ latitude: position.latitude,
291
+ longitude: position.longitude
292
+ }
274
293
  }
275
- }
276
294
 
277
- app.debug(`waypoint: ${waypointId} -> ${end}/${waypointName} : ${JSON.stringify(waypoint)}`);
295
+ app.debug(`waypoint: ${waypointId} -> ${end}/${waypointName} : ${JSON.stringify(waypoint)}`);
278
296
 
279
- if (waypointId)
280
- await app.resourcesApi.setResource('waypoints', waypointId, waypoint);
281
- else if (state.options.createStartLineWaypoint)
282
- await app.resourcesApi.createResource('waypoints', waypoint);
283
- else
284
- app.debug('startline waypoint not created');
297
+ if (waypointId)
298
+ await app.resourcesApi.setResource('waypoints', waypointId, waypoint);
299
+ else if (state.options.createStartLineWaypoint)
300
+ await app.resourcesApi.createResource('waypoints', waypoint);
301
+ else
302
+ app.debug('startline waypoint not created');
303
+ }
304
+ catch (err) {
305
+ app.error(`Failed to set startline waypoint: ${err}`);
306
+ }
285
307
  } else {
286
308
  app.debug('startline waypoint not updated');
287
309
  }
288
310
 
289
-
290
311
  // If we have a startLine, then process position against the new line
291
312
  if (startLine)
292
313
  processPosition(null);
@@ -461,17 +482,21 @@ module.exports = (app) => {
461
482
  return;
462
483
  }
463
484
 
464
- state.timeToStart += delta;
465
- if (state.timeToStart < 0) state.timeToStart = 0;
485
+ state.timeToStart = Math.max(state.timeToStart + delta, 0);
466
486
 
467
- let now = Date.now();
468
- const adjustedStart = new Date(now - (now % 1000) + state.timeToStart * 1000).toISOString();
469
- state.startTime = adjustedStart;
487
+ if (state.timerRunning) {
488
+ let now = Date.now();
489
+ state.startTime = new Date(now - (now % 1000) + state.timeToStart * 1000).toISOString();
490
+ }
491
+
492
+ const deltas = [
493
+ { path: 'navigation.racing.timeToStart', value: state.timeToStart }
494
+ ];
495
+ if (state.timerRunning && state.startTime) {
496
+ deltas.push({path: 'navigation.racing.startTime', value: state.startTime });
497
+ }
498
+ sendDeltas(deltas);
470
499
 
471
- sendDeltas([
472
- {path: 'navigation.racing.timeToStart', value: state.timeToStart},
473
- {path: 'navigation.racing.startTime', value: adjustedStart}
474
- ]);
475
500
  return complete(callback, 200, 'adjust timer: OK');
476
501
  }
477
502
 
@@ -480,6 +505,7 @@ module.exports = (app) => {
480
505
  }
481
506
  }
482
507
  } catch (err) {
508
+ app.debug('startTimerCommand:', JSON.stringify(err));
483
509
  return complete(callback, 500, 'Failed to command timer: ' + JSON.stringify(err));
484
510
  }
485
511
  }
Binary file
package/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "signalk-racer",
3
- "version": "0.9.9",
4
- "description": "Signalk plugin to calculate values of interest to sail racers: such as Distance to Line; Next leg TWA.",
3
+ "version": "0.9.11",
4
+ "description": "Signalk plugin to calculate values of interest to sail racers: such as Time to Start, Time of Start, Distance to Line; Next leg TWA.",
5
5
  "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
6
  "repository": {
10
7
  "type": "git",
11
8
  "url": "git+https://github.com/gregw/signalk-racer.git"
Binary file