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 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.