signalk-racer 1.1.2 → 1.3.1
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/.claude/RESUME.md +23 -0
- package/.claude/settings.local.json +15 -1
- package/README.md +154 -14
- package/ReleaseNotes.md +48 -0
- package/index.js +380 -84
- package/openapi.json +62 -0
- package/package.json +1 -1
- package/public/index.html +419 -13
- package/public/line-view.js +430 -0
- package/racer-webapp.png +0 -0
- package/racer.js +254 -65
- package/tests/racer-maths.test.js +1 -1
- package/tests/racer-ttl.test.js +16 -11
- package/tests/racer-vmg-override.test.js +169 -0
- package/tests/racer-vmg.test.js +175 -118
- package/tests/racer-zone.test.js +184 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Claude Code — resume checkpoint
|
|
2
|
+
|
|
3
|
+
Session: fe0c247c-615f-438d-9888-bb2d81cfa8b7
|
|
4
|
+
Written: 2026-08-19T08:14:05.997Z
|
|
5
|
+
Trigger: near-limit
|
|
6
|
+
Ref with your in-progress files: refs/claude/checkpoint-fe0c247c
|
|
7
|
+
|
|
8
|
+
## To resume
|
|
9
|
+
|
|
10
|
+
claude --resume fe0c247c-615f-438d-9888-bb2d81cfa8b7
|
|
11
|
+
|
|
12
|
+
(or open Claude Code in this directory and run /resume)
|
|
13
|
+
|
|
14
|
+
## Plan (from TodoWrite state)
|
|
15
|
+
|
|
16
|
+
No task list was active; see transcript via the resume command above.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
Don't want these changes? Resume this session (above), then run
|
|
21
|
+
`/rewind` to roll back the turn's tool edits (bash-made changes
|
|
22
|
+
excluded). refs/claude/checkpoint-fe0c247c holds a full snapshot until this session's
|
|
23
|
+
next checkpoint, or for up to ~2 weeks.
|
|
@@ -5,7 +5,21 @@
|
|
|
5
5
|
"Bash(git -C /home/gregw/src/signalk-racer tag -l)",
|
|
6
6
|
"Bash(git -C /home/gregw/src/signalk-racer log --oneline -5)",
|
|
7
7
|
"Bash(git -C /home/gregw/src/signalk-racer status -sb)",
|
|
8
|
-
"Bash(git -C /home/gregw/src/signalk-racer remote -v)"
|
|
8
|
+
"Bash(git -C /home/gregw/src/signalk-racer remote -v)",
|
|
9
|
+
"Bash(npx jest *)",
|
|
10
|
+
"Bash(node --check index.js)",
|
|
11
|
+
"Bash(node --check racer.js)",
|
|
12
|
+
"Bash(node *)",
|
|
13
|
+
"Bash(npm root *)",
|
|
14
|
+
"Read(//home/gregw/.signalk/**)",
|
|
15
|
+
"Read(//home/gregw/.signalk/plugin-config-data/**)",
|
|
16
|
+
"Bash(npm view *)",
|
|
17
|
+
"Bash(systemctl list-units *)",
|
|
18
|
+
"Bash(systemctl --user list-units --type=service --all)",
|
|
19
|
+
"Bash(sed *)",
|
|
20
|
+
"Bash(python3 -)",
|
|
21
|
+
"Bash(awk '/^## 1.2.0/,/^## 1.1.2/' ReleaseNotes.md)",
|
|
22
|
+
"Bash(tee /dev/stderr)"
|
|
9
23
|
]
|
|
10
24
|
}
|
|
11
25
|
}
|
package/README.md
CHANGED
|
@@ -14,26 +14,34 @@ GitHub repository: [https://github.com/gregw/signalk-racer](https://github.com/g
|
|
|
14
14
|
|
|
15
15
|
This plugin calculates and publishes the following Signal K paths:
|
|
16
16
|
|
|
17
|
-
| Path
|
|
18
|
-
|
|
19
|
-
| `navigation.racing
|
|
20
|
-
| `navigation.racing
|
|
21
|
-
| `navigation.racing
|
|
22
|
-
| `navigation.racing
|
|
23
|
-
| `navigation.racing
|
|
24
|
-
| `navigation.racing
|
|
25
|
-
| `navigation.racing
|
|
26
|
-
| `navigation.racing
|
|
27
|
-
| `navigation.racing
|
|
28
|
-
| `navigation.racing
|
|
29
|
-
| `navigation.racing
|
|
17
|
+
| Path | Description | Units | Std |
|
|
18
|
+
|--------------------------------------------|------------------------------------------------------------------------|----------------------------------------------------|-----|
|
|
19
|
+
| `navigation.racing.distanceStartline` | Signed minimum distance from the bow to the start line | `m` | Y |
|
|
20
|
+
| `navigation.racing.startLineLength` | Total length of the start line | `m` | |
|
|
21
|
+
| `navigation.racing.startLineBearing` | Bearing of the start line, from the stb end to the port end | `rad` | |
|
|
22
|
+
| `navigation.racing.stbLineBias` | Bias of the start line toward the starboard end | `m` | |
|
|
23
|
+
| `navigation.racing.bestVmg.toCourseSide` | Best VMG across the line towards the course side | `m/s` | |
|
|
24
|
+
| `navigation.racing.bestVmg.fromCourseSide` | Best VMG back across the line from the course side (used when OCS) | `m/s` | |
|
|
25
|
+
| `navigation.racing.bestVmg.toPortEnd` | Best VMG along the line towards the port end (pin) | `m/s` | |
|
|
26
|
+
| `navigation.racing.bestVmg.toStbEnd` | Best VMG along the line towards the stb end (boat) | `m/s` | |
|
|
27
|
+
| `navigation.racing.bestVmg.*.override` | The manual adjustment behind each best VMG, `null` when not adjusted | `m/s` | |
|
|
28
|
+
| `navigation.racing.effectiveVmg.toLine` | VMG the perpendicular leg of the time to line is divided by | `m/s` | |
|
|
29
|
+
| `navigation.racing.effectiveVmg.alongLine` | VMG the along-line leg is divided by, 0 when inside the start zone | `m/s` | |
|
|
30
|
+
| `navigation.racing.startLinePort` | Location of the port (pin) end of the start line | `{latitude,longitude}` | Y |
|
|
31
|
+
| `navigation.racing.startLineStb` | Location of the starboard (boat) end of the start line | `{latitude,longitude}` | Y |
|
|
32
|
+
| `navigation.racing.nextLegHeading` | True heading for the next leg of the course | `rad` | |
|
|
33
|
+
| `navigation.racing.nextLegTrueWindAngle` | True Wind Angle for the next leg of the course | `rad` | |
|
|
34
|
+
| `navigation.racing.timeToStart` | Period of time until the race start | `s` | Y |
|
|
35
|
+
| `navigation.racing.timeToLine` | Period of time to sail to the line at best VMG | `s` | |
|
|
36
|
+
| `navigation.racing.timeToBurn` | Period of time delay before sailing to the at best VMG | `s` | |
|
|
37
|
+
| `navigation.racing.startTime` | The start time as an ISO timestamp | `rfc3339` | |
|
|
30
38
|
|
|
31
39
|
These values can be displayed in KIP widgets, Freeboard-SK, or other Signal K clients.
|
|
32
40
|
There are dedicated widgets for the start timer and line adjustment since 3.5.0 of KIP
|
|
33
41
|
|
|
34
42
|

|
|
35
43
|
|
|
36
|
-
There is also a webapp to adjust the line and timer.
|
|
44
|
+
There is also a webapp to: adjust the line, visualize the approach to the line, adjust VMGs and configure the timer.
|
|
37
45
|
|
|
38
46
|

|
|
39
47
|
|
|
@@ -53,6 +61,7 @@ You can configure the plugin via the Signal K web interface or by editing `setti
|
|
|
53
61
|
| `period` | How often to update values (in milliseconds) | `1000` |
|
|
54
62
|
| `updateStartLineWaypoint` | Should the waypoints be updated if the line is set/adjusted | `true` |
|
|
55
63
|
| `createStartLineWaypoint` | Should the waypoints be created if the line is set | `true` |
|
|
64
|
+
| `minEffectiveVmg` | Floor under each effective VMG used for time to line (in knots) | `0.5` |
|
|
56
65
|
| `lines` | Array of named lines | null |
|
|
57
66
|
|
|
58
67
|
---
|
|
@@ -90,9 +99,18 @@ If `navigation.headingTrue` is not available, then `navigation.courseOverGroundT
|
|
|
90
99
|
|
|
91
100
|
- Uses the effective VMG as the maximum VMG of either the current COG/SOG or the 90th percentile of the VMG recently achieved (approx in the last 10 minutes).
|
|
92
101
|
This allows a boat to luff / delay without changing the time to line.
|
|
102
|
+
- Each effective VMG is floored at `minEffectiveVmg` (default 0.5 knots), so a direction
|
|
103
|
+
with nothing collected yet - or a drifting boat - still yields a time rather than none.
|
|
104
|
+
The floor is not applied to the along-line leg when the boat is inside the start zone,
|
|
105
|
+
there being no such leg to sail.
|
|
93
106
|
- A boat within the start zone has the time calculated by the perpendicular distance to the line divided by their effective VMG to the line.
|
|
94
107
|
- A boat outside the start zone also has the perpendicular time plus the time calculated by the parallel distance to the zone divided by their effective VMG in that direction.
|
|
95
108
|
- If the line is changed, then the samples used to calculate the effective VMGs are cleared.
|
|
109
|
+
- The four collected VMGs are published under `navigation.racing.bestVmg.*` and may be manually
|
|
110
|
+
adjusted. An adjustment stands in for the 90th percentile of the samples, but the VMG the boat
|
|
111
|
+
is actually sailing still wins if it is better, so an adjustment can never make the estimate
|
|
112
|
+
ignore real progress. Adjustments are cleared when the start timer reaches zero, when the line
|
|
113
|
+
changes, and by the `reset` command.
|
|
96
114
|
- If the boat is navigating a route and the next waypoint is beyond the second in the route, then the time to line is not calculated.
|
|
97
115
|
|
|
98
116
|
|
|
@@ -114,6 +132,35 @@ If `navigation.headingTrue` is not available, then `navigation.courseOverGroundT
|
|
|
114
132
|
|
|
115
133
|
---
|
|
116
134
|
|
|
135
|
+
## 📉 Webapp Start Line Visualization
|
|
136
|
+
|
|
137
|
+
The webapp draws the line "line up": always horizontal, with the **port (pin) end to the left**
|
|
138
|
+
and the **starboard (committee boat) end to the right**. The course side of the line is therefore
|
|
139
|
+
always the **upper** half of the drawing and the pre-start side the lower half, so the arrow beside
|
|
140
|
+
the line length and start heading always points straight up. The drawing is auto-scaled to fit the
|
|
141
|
+
line and the boat, so it zooms as the boat closes. The line length and the heading to sail to cross
|
|
142
|
+
the line are marked on the line itself, and the boat is drawn as a triangle pointing along its
|
|
143
|
+
heading, amber when OCS.
|
|
144
|
+
|
|
145
|
+
A projection runs from the boat, drawn to the same scale as the rest of the drawing, so its **tip
|
|
146
|
+
can be read directly against the line**. Hovering the boat reports its SOG and COG and names the
|
|
147
|
+
line running from it.
|
|
148
|
+
|
|
149
|
+
#### Course projection — "Current cog/sog to start"
|
|
150
|
+
|
|
151
|
+
Runs along the boat's present `navigation.courseOverGroundTrue`. While the start timer is counting
|
|
152
|
+
down, its length is `SOG × timeToStart`: the distance the boat will cover before the gun if nothing
|
|
153
|
+
changes. Its tip is therefore **where the boat will be when the start fires**:
|
|
154
|
+
|
|
155
|
+
- tip short of the line — the boat is late and will cross after the gun
|
|
156
|
+
- tip on the line — a perfect start
|
|
157
|
+
- tip beyond the line — the boat is early and will be OCS
|
|
158
|
+
|
|
159
|
+
With no timer running there is nothing to project against, so it degrades to a short fixed-length
|
|
160
|
+
stub showing course only.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
117
164
|
## 🌐 API Access
|
|
118
165
|
|
|
119
166
|
This plugin uses the following WebSocket-based PUT requests to Signal K model paths.
|
|
@@ -139,6 +186,39 @@ Used to **set** or **adjust** either end of the start line.
|
|
|
139
186
|
|
|
140
187
|
---
|
|
141
188
|
|
|
189
|
+
### `navigation.racing.swapStartLine`
|
|
190
|
+
|
|
191
|
+
Used to **swap** the port (pin) and starboard (boat) ends of the line, reversing its bearing.
|
|
192
|
+
Takes no parameters. Collected VMG samples and any manual adjustments are exchanged along
|
|
193
|
+
with the ends rather than discarded, since reversing the bearing flips the sign of both VMG
|
|
194
|
+
components and so leaves every sample valid, just relabelled.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### `navigation.racing.setBestVmg`
|
|
199
|
+
|
|
200
|
+
Used to **adjust** or **reset** the best VMGs used to estimate the time to line.
|
|
201
|
+
|
|
202
|
+
#### Payload:
|
|
203
|
+
```
|
|
204
|
+
{
|
|
205
|
+
"vmg": "toCourseSide" | "fromCourseSide" | "toPortEnd" | "toStbEnd",
|
|
206
|
+
"value": 5.0,
|
|
207
|
+
"delta": 0.0514,
|
|
208
|
+
"command": "reset" | "clear"
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
- `vmg`: which best VMG to adjust. Optional for `reset`, which otherwise clears all four.
|
|
213
|
+
- `value`: absolute best VMG in `m/s`.
|
|
214
|
+
- `delta`: adjustment in `m/s` applied to the current best VMG.
|
|
215
|
+
- `command`: `"reset"` to clear the override and revert to the collected samples, or
|
|
216
|
+
`"clear"` to throw away the collected samples themselves and start again.
|
|
217
|
+
|
|
218
|
+
An absolute value may also be put directly to `navigation.racing.bestVmg.<name>`.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
142
222
|
### `navigation.racing.setStartTime`
|
|
143
223
|
|
|
144
224
|
Used to **start**, **sync**, **reset**, or **set a fixed start time**.
|
|
@@ -184,6 +264,9 @@ Bug reports and suggestions are welcome at
|
|
|
184
264
|
|
|
185
265
|
---
|
|
186
266
|
## Development
|
|
267
|
+
|
|
268
|
+
### Linking the plugin into a local server
|
|
269
|
+
|
|
187
270
|
```text
|
|
188
271
|
npm install
|
|
189
272
|
npm link
|
|
@@ -191,6 +274,63 @@ cd ~/.signalk
|
|
|
191
274
|
npm link signalk-racer
|
|
192
275
|
```
|
|
193
276
|
|
|
277
|
+
This replaces `~/.signalk/node_modules/signalk-racer` with a symlink to your checkout, so
|
|
278
|
+
edits take effect on the next server restart with no reinstall. Verify the link with:
|
|
279
|
+
|
|
280
|
+
```text
|
|
281
|
+
readlink -f ~/.signalk/node_modules/signalk-racer
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
> ⚠️ **The link is fragile.** `~/.signalk/package.json` still lists `signalk-racer` as a
|
|
285
|
+
> normal dependency, so running `npm install` (or installing/updating *any* plugin from the
|
|
286
|
+
> server's Appstore) will overwrite the symlink with the published release from npm, silently
|
|
287
|
+
> reverting you to the registry version. Re-run `npm link signalk-racer` in `~/.signalk`
|
|
288
|
+
> whenever that happens.
|
|
289
|
+
|
|
290
|
+
### Checking which build is actually running
|
|
291
|
+
|
|
292
|
+
The plugin stamps its version in three places, so a stale link is easy to spot:
|
|
293
|
+
|
|
294
|
+
- the **Plugin Config** page in the admin UI, as the plugin status `signalk-racer <version> started`
|
|
295
|
+
- the bottom of the **racer webapp**
|
|
296
|
+
- the server log, via `app.debug`, including the directory it was loaded from
|
|
297
|
+
|
|
298
|
+
During development the version carries a `-dev.N` suffix (e.g. `1.2.0-dev.0`), so it is
|
|
299
|
+
immediately distinguishable from a released version installed from npm.
|
|
300
|
+
|
|
301
|
+
A restart is required for any change to plugin code — the server does not hot-reload plugins.
|
|
302
|
+
Changes to files under `public/` are served statically, so those only need a browser reload
|
|
303
|
+
(with cache bypass: `Ctrl+Shift+R`).
|
|
304
|
+
|
|
305
|
+
### Running the server on this machine
|
|
306
|
+
|
|
307
|
+
The server is installed **globally** (`npm root -g` → `/usr/lib/node_modules`) and runs as a
|
|
308
|
+
system service, using `~/.signalk` as its config directory:
|
|
309
|
+
|
|
310
|
+
```text
|
|
311
|
+
sudo systemctl status signalk # is it running?
|
|
312
|
+
sudo systemctl restart signalk # pick up plugin changes
|
|
313
|
+
journalctl -u signalk -f # follow the log
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Updating signalk-server
|
|
317
|
+
|
|
318
|
+
Because it is installed into a system directory, updating needs `sudo`, and the service must be
|
|
319
|
+
restarted afterwards:
|
|
320
|
+
|
|
321
|
+
```text
|
|
322
|
+
npm view signalk-server version # latest published
|
|
323
|
+
npm ls -g --depth=0 signalk-server # currently installed
|
|
324
|
+
sudo npm install -g signalk-server # update to latest
|
|
325
|
+
sudo systemctl restart signalk
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
To pin a specific version instead, use `sudo npm install -g signalk-server@2.31.1`.
|
|
329
|
+
|
|
330
|
+
Note that `~/.signalk/package.json` also lists `signalk-server` as a dependency; that entry is
|
|
331
|
+
only bookkeeping for the Appstore and is **not** what the service runs — the globally installed
|
|
332
|
+
copy in `/usr/lib/node_modules/signalk-server` is. Updating the global copy is what counts.
|
|
333
|
+
|
|
194
334
|
---
|
|
195
335
|
## 🚀 Releasing
|
|
196
336
|
|
package/ReleaseNotes.md
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
## 1.3.1 Start line approach
|
|
2
|
+
+ Fixed the distance to the start zone being measured against a 54.7 degree wedge while
|
|
3
|
+
the zone itself was tested at 45 degrees, so `distanceStartline` and `timeToLine`
|
|
4
|
+
jumped by 0.29 times the offset as a boat crossed the boundary. They are now continuous
|
|
5
|
+
and the along-line leg reaches zero exactly at the wedge
|
|
6
|
+
+ The start zone geometry moved from `index.js` into `racer.js` as `startZoneDistances`,
|
|
7
|
+
where it is unit tested - it had no coverage at all, which is how the above survived
|
|
8
|
+
+ `navigation.racing.timeToBurn` is now published when OCS as well. The time to line is
|
|
9
|
+
then the time to get back over the line from the course side, so the spare time before
|
|
10
|
+
you have to turn and do it is just as real, and goes negative once it is too late
|
|
11
|
+
+ Removed `navigation.racing.bestApproach` and the `cog`/`sog` each VMG sample carried
|
|
12
|
+
to produce it. It described one course the boat had genuinely sailed, which was useful
|
|
13
|
+
but not the path the time to line is computed over, so it read as a second answer to a
|
|
14
|
+
question that already had one. Best VMG angles will be revisited once the polar plugin
|
|
15
|
+
offers an API for them, by which point the shape is likely to be quite different
|
|
16
|
+
+ Each effective VMG is now floored at a configurable minimum, `minEffectiveVmg`,
|
|
17
|
+
defaulting to 0.5 knots. A direction with nothing collected reported no VMG at all, which
|
|
18
|
+
left the time to line falling back to the time to start rather than estimating anything
|
|
19
|
+
+ `navigation.racing.setBestVmg` accepts `command: "clear"`, which discards the collected
|
|
20
|
+
samples themselves rather than just the manual adjustments
|
|
21
|
+
+ Published the two VMGs the time to line is actually divided by as
|
|
22
|
+
`navigation.racing.effectiveVmg.{toLine,alongLine}`: the collected best for each
|
|
23
|
+
direction, or the VMG being sailed right now when that is better. `bestVmg.*` reports
|
|
24
|
+
only the collected half, so a client drawing the approach from it disagreed with the
|
|
25
|
+
published time whenever the boat was going well
|
|
26
|
+
|
|
27
|
+
## 1.3.0 Visualize line and best VMGs
|
|
28
|
+
+ Added `navigation.racing.swapStartLine` to swap the port and starboard ends of the line,
|
|
29
|
+
carrying the collected VMG samples across with the ends
|
|
30
|
+
+ Published the four best VMGs behind the time to line as `navigation.racing.bestVmg.`
|
|
31
|
+
`{toCourseSide,fromCourseSide,toPortEnd,toStbEnd}`, each with an `.override` beside it
|
|
32
|
+
reporting the manual adjustment, if any
|
|
33
|
+
+ Added `navigation.racing.setBestVmg` to adjust or reset them, plus direct puts to each path.
|
|
34
|
+
An adjustment holds until the gun, but the VMG actually being sailed wins if it is better
|
|
35
|
+
+ Samples now keep the `cog` and `sog` that produced them, published as
|
|
36
|
+
`navigation.racing.bestApproach`: the course actually sailed that best closes the line
|
|
37
|
+
+ Published the start line bearing as `navigation.racing.startLineBearing`
|
|
38
|
+
+ Webapp: swap-ends button, race countdown, and a start line visualisation with the best VMGs
|
|
39
|
+
as a cross below it and projections showing where the boat reaches at the gun
|
|
40
|
+
+ Webapp and admin UI now show the running plugin version
|
|
41
|
+
+ Fixed VMG samples being rejected below an absolute 1 m/s in each direction, so a boat closing
|
|
42
|
+
the line at an angle only fed the direction it was mostly travelling (now `minVmg`, 0.01 m/s)
|
|
43
|
+
+ Fixed the collected VMG samples being discarded whenever any waypoint changed
|
|
44
|
+
+ Fixed the start line bias not being cleared when the line is lost
|
|
45
|
+
+ Fixed the webapp race countdown never being displayed
|
|
46
|
+
|
|
47
|
+
## 1.2.0 Never released
|
|
48
|
+
|
|
1
49
|
## 1.1.2 TTL fixes
|
|
2
50
|
+ use a FIFO queue for VMGs towards the line.
|
|
3
51
|
+ avoid undefined values in deltas
|