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 +39 -23
- package/ReleaseNotes.md +7 -0
- package/index.js +76 -50
- package/kip-freeboard-screenshot.png +0 -0
- package/package.json +2 -5
- package/racer-webapp.png +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Signal K Racing Plugin
|
|
2
2
|
|
|
3
|
-
This
|
|
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
|
|
14
|
-
|
|
15
|
-
| `navigation.racing.`<br/>`distanceStartline`
|
|
16
|
-
| `navigation.racing.`<br/>`startLineLength`
|
|
17
|
-
| `navigation.racing.`<br/>`stbLineBias`
|
|
18
|
-
| `navigation.racing.`<br/>`startLinePort
|
|
19
|
-
| `navigation.racing.`<br/>`
|
|
20
|
-
| `navigation.racing.`<br/>`
|
|
21
|
-
| `navigation.racing.`<br/>`
|
|
22
|
-
| `navigation.racing.`<br/>`
|
|
23
|
-
| `navigation.racing.`<br/>`
|
|
24
|
-
|
|
25
|
-
|
|
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
|

|
|
30
32
|
|
|
33
|
+
There is also a webapp to adjust the line and timer.
|
|
34
|
+
|
|
35
|
+

|
|
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
|
|
40
|
-
|
|
41
|
-
| `startLineStb`
|
|
42
|
-
| `startLinePort`
|
|
43
|
-
| `
|
|
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.
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
type: '
|
|
266
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
295
|
+
app.debug(`waypoint: ${waypointId} -> ${end}/${waypointName} : ${JSON.stringify(waypoint)}`);
|
|
278
296
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
|
465
|
-
if (state.timeToStart < 0) state.timeToStart = 0;
|
|
485
|
+
state.timeToStart = Math.max(state.timeToStart + delta, 0);
|
|
466
486
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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.
|
|
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"
|
package/racer-webapp.png
ADDED
|
Binary file
|