signalk-race-control 0.1.0
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/LICENSE +21 -0
- package/README.md +138 -0
- package/index.js +1133 -0
- package/package.json +18 -0
- package/public/app.js +1540 -0
- package/public/index.html +114 -0
- package/public/style.css +535 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 joabakk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# signalk-race-control
|
|
2
|
+
|
|
3
|
+
A [SignalK](https://signalk.org) server plugin + webapp for planning, running, and
|
|
4
|
+
reviewing races: elapsed and handicap-corrected time, a course chart with AIS replay,
|
|
5
|
+
and a live projected finishing order — during and after the race.
|
|
6
|
+
|
|
7
|
+
- **Named, plannable races** — create races ahead of time, optionally with a
|
|
8
|
+
scheduled start; switch between past and upcoming races from a dropdown to
|
|
9
|
+
review results later. Nothing gets overwritten by starting the next race.
|
|
10
|
+
- **Add/remove boats explicitly** — type a name (autocompletes against the VET
|
|
11
|
+
register, the cross-race boat registry, and any live AIS/self vessel — matching
|
|
12
|
+
anywhere in the name, not just the start) and click **Add Boat**. Boats aren't
|
|
13
|
+
auto-populated from AIS; you control exactly who's racing.
|
|
14
|
+
- **Elapsed / corrected time** — Time-on-Time correction: `corrected = elapsed × TCF`,
|
|
15
|
+
where TCF is edited per boat directly in the webapp and persisted server-side,
|
|
16
|
+
shared by everyone viewing the page.
|
|
17
|
+
- **VET-tall import** — pick a boat's handicap straight from SSCA's VET-tall register.
|
|
18
|
+
VET-tall ("veteranbåt-tall") is [Seilskøyteklubben Colin
|
|
19
|
+
Archer](https://ssca.no)'s (SSCA) own handicap register specifically for
|
|
20
|
+
traditional, gaff-rigged/classic wooden sailing boats, published as a downloadable
|
|
21
|
+
spreadsheet on [ssca.no/aktiviteter/vet-tall](https://ssca.no/aktiviteter/vet-tall)
|
|
22
|
+
— this plugin resolves and downloads whichever sheet is linked there live, so it
|
|
23
|
+
tracks the current year's numbers automatically without needing an update. Each
|
|
24
|
+
sail-configuration variant (e.g. with/without topsail) is a separate alternative in
|
|
25
|
+
a per-boat dropdown; picking one applies that exact TCF, and the dropdown keeps
|
|
26
|
+
showing whichever alternative is currently in effect. Since it's specific to
|
|
27
|
+
traditional boats, it won't have every boat in a mixed fleet, so it's **off by
|
|
28
|
+
default** — turn it on in the plugin's settings (`vetEnabled`) for clubs that
|
|
29
|
+
actually race under VET-tall; see **Cross-race TCF memory** below for what happens
|
|
30
|
+
to handicaps while it's off.
|
|
31
|
+
- **Cross-race TCF memory for boats outside VET** — whenever a boat isn't matched in
|
|
32
|
+
the VET register (or the register is disabled altogether), the TCF you set for it by
|
|
33
|
+
hand is remembered by boat name and applied automatically the next time a boat with
|
|
34
|
+
that name is added to any race. A boat that *is* VET-matched never has its TCF
|
|
35
|
+
carried over this way — it always starts from the default until you pick a VET
|
|
36
|
+
alternative or edit it again.
|
|
37
|
+
- **MMSI, remembered across races** — set once per boat (or picked up automatically
|
|
38
|
+
from a live AIS/self vessel with a matching name), it's remembered in a small
|
|
39
|
+
cross-race registry: add a boat with the same name in a later race and its MMSI
|
|
40
|
+
fills in on its own.
|
|
41
|
+
- **Editable finish times** — click **Now** to record a finish as it happens, type a
|
|
42
|
+
specific `HH:MM:SS` into the finish-time field to correct a mistimed click, or
|
|
43
|
+
**Clear** to undo. Handles races that cross midnight.
|
|
44
|
+
- **Course & chart** — enter lat/lon for the start line, an ordered list of rounding
|
|
45
|
+
marks, and the finish line (or click **Use my position** if you're sitting at that
|
|
46
|
+
spot). The webapp draws them on a built-in chart, overlaid with each AIS-tracked
|
|
47
|
+
boat's recorded track for the current race — drag the **replay** slider to step
|
|
48
|
+
back through it, or leave it on **Live**. The course is also published as SignalK
|
|
49
|
+
waypoint/route resources for any chart plotter (e.g. freeboard-sk) that reads the
|
|
50
|
+
standard resources API — that part only does anything if your server has a
|
|
51
|
+
resources provider installed; it's a no-op otherwise, never a failure.
|
|
52
|
+
- **Estimated finish time & live rank** — while a boat is still racing, if it has a
|
|
53
|
+
live AIS position and speed and the race has a finish line, the plugin projects a
|
|
54
|
+
finish time and corrected time from its remaining distance and speed, and ranks it
|
|
55
|
+
accordingly. The distance routes around whichever marks the boat hasn't rounded yet
|
|
56
|
+
(detected automatically from its recorded track passing within ~0.1nm of each mark
|
|
57
|
+
in order) rather than cutting straight to the finish — accuracy still depends on
|
|
58
|
+
that detection actually catching each rounding.
|
|
59
|
+
- **Stop / call off the race** — freezes elapsed/corrected time for everyone without
|
|
60
|
+
touching boats, finish times, or the course (unlike Reset, which clears the race
|
|
61
|
+
back to not-started), and marks every boat that hadn't finished as **DNF**,
|
|
62
|
+
capturing its last known AIS position if one's available. You can also schedule a
|
|
63
|
+
call-off for a future time (mirrors Schedule Start), and mark or un-mark an
|
|
64
|
+
individual boat DNF by hand at any point, independent of the whole race. There's no
|
|
65
|
+
true pause: **Resume** discards the stop, un-DNFs everyone it DNF'd, and the clock
|
|
66
|
+
jumps straight back to real elapsed time — the time spent stopped isn't excluded
|
|
67
|
+
from anyone's result. Recording a real finish time on a DNF'd boat clears its DNF.
|
|
68
|
+
- **Compare to a "self" boat** — click the star next to a boat's name to mark it as
|
|
69
|
+
self. Every other boat then shows, in the **vs Self** column: a live-ticking
|
|
70
|
+
countdown to the moment self would tie them on corrected time if that boat has
|
|
71
|
+
already finished (going negative, in red, once self can no longer catch up even by
|
|
72
|
+
finishing instantly), or the current corrected-time gap if both are still racing or
|
|
73
|
+
both have finished.
|
|
74
|
+
- **Export to Excel** — a genuine `.xlsx` snapshot of the current standings (same
|
|
75
|
+
ranking, same rows as the on-screen table: rank, boat, MMSI, TCF, elapsed,
|
|
76
|
+
corrected, finish time, status), with the finish-time column rendered in your
|
|
77
|
+
browser's own timezone rather than the server's.
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
Install this directory's dependencies once (needed for the Excel export), then copy
|
|
82
|
+
(or symlink) it into your SignalK server's `node_modules`, e.g.:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cd /path/to/race-control && npm install
|
|
86
|
+
cd ~/.signalk/node_modules
|
|
87
|
+
ln -s /path/to/race-control signalk-race-control
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then restart the SignalK server, enable "Race Control" under
|
|
91
|
+
**Server → Plugin Config**, and open the webapp from the SignalK **Webapps** list
|
|
92
|
+
(or navigate to `/signalk-race-control/`).
|
|
93
|
+
|
|
94
|
+
## Using it
|
|
95
|
+
|
|
96
|
+
1. Click **+ New Race**, give it a name (e.g. "Onsdagsseilas 3"). It becomes the
|
|
97
|
+
active race, shared across every open browser tab/device.
|
|
98
|
+
2. Add boats by name. Each gets a default TCF of 1.0 — edit it directly, or use the
|
|
99
|
+
VET-alternatives dropdown once a matching register entry is found. Set an MMSI per
|
|
100
|
+
boat if you want its AIS position tracked for the chart/estimate.
|
|
101
|
+
3. Optionally expand **Course & chart** and enter the start line, marks (in rounding
|
|
102
|
+
order — reorder with ↑/↓), and finish line, then **Save Course**.
|
|
103
|
+
4. Either click **Start Race** now, or set a date/time and click **Schedule Start** —
|
|
104
|
+
the race starts itself automatically at that moment (even across a server restart).
|
|
105
|
+
5. As boats finish, click **Now** to stamp the current time, or type the exact
|
|
106
|
+
`HH:MM:SS` into the finish-time field. **Clear** undoes a finish.
|
|
107
|
+
6. **Stop** calls the race off now (freezes the clock, DNFs whoever hasn't finished);
|
|
108
|
+
**Schedule Call-off** does the same at a future time instead. **Resume** discards
|
|
109
|
+
a stop and un-DNFs whoever it DNF'd. **Reset** (click once to arm, again to
|
|
110
|
+
confirm) clears this race's start/finish/DNF state and recorded tracks entirely so
|
|
111
|
+
it can be re-run — boats and TCF values are kept.
|
|
112
|
+
7. Click a boat's ☆ to mark it **self** and see the **vs Self** column fill in for
|
|
113
|
+
every other boat.
|
|
114
|
+
8. **Export to Excel** downloads the current standings as a `.xlsx` file at any time
|
|
115
|
+
— before, during, or after the race.
|
|
116
|
+
9. Switch races anytime via the dropdown at the top to review an earlier race's
|
|
117
|
+
results, or plan the next one. **Delete Race** (arm-then-confirm) removes one.
|
|
118
|
+
|
|
119
|
+
Corrected time is shown live throughout the race (using elapsed-so-far), and freezes
|
|
120
|
+
once a boat's finish time is recorded. The **Est. finish** column shows a projected
|
|
121
|
+
finish time/rank for boats still racing, when the plugin has enough to estimate one.
|
|
122
|
+
|
|
123
|
+
## Notes
|
|
124
|
+
|
|
125
|
+
- All race state (names, schedules, boats, TCF, finish times, course, recorded
|
|
126
|
+
tracks) is persisted to disk under the plugin's data directory, so it survives a
|
|
127
|
+
server restart.
|
|
128
|
+
- While a race is running, every unfinished boat with an MMSI gets a position sample
|
|
129
|
+
recorded roughly every 15 seconds — that's the track the chart replays. Recording
|
|
130
|
+
stops for a boat once it finishes; **Start**/**Reset** clear previously recorded
|
|
131
|
+
tracks for a fresh run.
|
|
132
|
+
- The VET-tall source page can be overridden in the plugin's settings
|
|
133
|
+
(`handicapSourceUrl`) — point it at a specific year's Google Sheet link directly to
|
|
134
|
+
skip the SSCA page lookup, e.g. if a club uses its own register.
|
|
135
|
+
- Whether the VET-tall register is used at all (`vetEnabled`, off by default) is also a plugin
|
|
136
|
+
setting, not a per-race or webapp-side option — change it under
|
|
137
|
+
**Server → Plugin Config → Race Control** and restart the plugin (the server does
|
|
138
|
+
this automatically on save) for the webapp to pick it up.
|