Sailing Performance¶
The XREG-010 uses the boat's own sensors to learn how fast it goes when conditions are steady, then compares present speed against its best-ever for those same conditions. Because conditions are part of the reference, chop is not treated as random noise — it becomes an axis. Over time the boat builds a performance record, and a sustained shortfall (fouling, poor trim, adverse current) shows up as performance below the best-ever surface. Advisory only; interpretation is left to the user.
Speed source¶
Output is boat speed, from a user-selected source — speed through water (STW) or speed over ground (SOG) (perfSpeedSrc in code). The two answer different questions (current shows up only in SOG), so they are not mixed: switching the source invalidates every learned point, so it is equivalent to Clear All and asks for confirmation first.
Axes — apparent wind, not true¶
The masthead measures apparent wind directly; true wind and heading are derived (and true wind depends on boat speed, the quantity being measured). So the learning axes are apparent:
Sailing
| Axis | Notes |
|---|---|
| Apparent wind speed (AWS) | |
| Apparent wind angle (AWA) | a tack swings AWA widely → breaks the run on its own |
| Sea-state | pitch standard deviation over a rolling window |
Motoring
| Axis | Notes |
|---|---|
| RPM | |
| Apparent headwind component | ≈ AWS · cos(AWA) |
| Sea-state | pitch standard deviation over a rolling window |
The sea-state metric is pitch standard deviation (degrees) computed over a rolling window (perfPitchPush() maintains it) — chosen because pitch angle is essentially independent of where the motion sensor is installed. (A future upgrade is bow-corrected vertical-acceleration RMS, a stronger physical proxy that needs lever-arm correction.)
Polars are learned in apparent-wind coordinates but can be displayed in true or apparent wind, user-selectable — a display-layer conversion only.
How a data point is formed¶
Identical mechanism to the alternator (see Alternator Health) — the same shared steady-run engine (Episode), instantiated twice here as sailEpisode and motorEpisode and fed from perfFold_tick():
- Sample on a fast internal tick (about 10 Hz on the device clock, not on a fresh NMEA/GPS sentence) so the cadence is independent of data-source arbitration and a fast excursion on any axis is not missed.
- Steady = every axis within its band, each axis with a user-tunable deviation bound and steady time, tracked independently per axis.
- A trailing-average point emitted at most once a second while the run holds steady, and only once the averaging window is both full and clean: it must span the whole window (2 seconds,
EP_AVG_WIN_MS, the same width the alternator-health instance uses) and each axis's trimmed range across it must be inside that axis's band, so isolated outlier samples are included in the average instead of blocking the emit while a sustained shift still fails. An axis leaving its band does not throw the run away — each axis's sliding window ages out on its own dwell, so the longest still-compatible tail keeps counting. A gap in the feed of more than half a second (stale NMEA, a pause) is a hard barrier and restarts the run.
Engine speed selects which detector is live: above a small RPM floor the boat is motoring (motorsailing counts as motoring), at or below it the boat is sailing. A motorboat (any Vessel Info type other than monohull or catamaran) below the floor is drifting, so neither detector learns. Neither learns while engine speed is ignored under Run Without Failed Sensors either, because motoring cannot then be told from sailing.
The best-ever front¶
The reference is a best-ever speed surface over the axes above, stored as sparse support points (the front, FrontStore in code — one each for sailing and motoring, sailFront and motorFront). For display, the firmware fits the local slope of the surface through nearby records (the same evaluator as alternator health) rather than averaging neighbors, which reads falsely low at the edge of the conditions sailed so far. A finished episode is kept only if its speed exceeds the surface at those conditions (the bar is the lower of the neighbor average and the local fit), with the same unvisited-region exception as the alternator: the first point in a new region of conditions is admitted unconditionally so it can open that region. With cloud features enabled, the cloud keeps every episode the regulator uploads (each admitted one, plus non-admitted ones at most once per conditions cell every 10 minutes) and rebuilds the pruned front from all of them, so the reference only moves up and never decays. The device evaluates the held front locally, so rating works offline.
What you see¶
- Live performance % with a confidence state — present speed ÷ best-ever for the current wind/angle/sea-state, labeled MEASURED (a recorded best exists for these conditions), ESTIMATED (interpolated between nearby records), or learning / no reference (no number shown). Sent to the dashboard by
perfSendLive()andperfSendMotorLive(). - A sailing polar plot — the learned best-ever surface sliced at the current wind speed, with the live boat speed displayed in the center hub; switchable between apparent- and true-wind display frames.
- A motoring map: best speed versus RPM at the current headwind and sea state, with the live point overlaid.
- Polar import/export — a learned polar can be downloaded as a CSV backup or shared, and an imported polar can be used either frozen (held exactly as imported, learning paused) or as a starting point that continued sailing keeps refining.
- Across the fleet, the same data eventually answers which boats and hull shapes lose the least speed in chop.