Lap KPIs
The Lap KPIs layer turns telemetry channels into named scalars ("rolling 5-second average RPM", "% of lap above gate threshold", "throttle pump count") that can be filtered and compared across laps without re-running raw telemetry every time.
Two ways to author a KPI
There are two paths. They share the same underlying engine
(packages/kpi-engine) and produce the same output shape.
1. Data Analysis Profiles (admin)
A Data Analysis Profile is a named collection of KPIs you
author directly in the UI — no plugin needed. Each KPI is a
{ name, expression, dimension? } row, evaluated against an
array of telemetry samples.
Author at Admin → Data Analysis Profiles
(/admin/data-analysis-profiles). See
Data Analysis Profiles for the
admin reference.
Evaluate via:
POST /data-analysis-profiles/:id/evaluate
{ "samples": [ { "t": 0.0, "RPM": 8200, "Throttle": 0.96, … }, … ] }
The endpoint parses each KPI independently, evaluates it against the samples, and returns:
{
"items": [
{ "name": "RPMRollingAvg5s", "value": 8127.4 },
{ "name": "GateAboveThrottle95", "value": 0.62 },
{ "name": "EdgeCountBrake", "error": "unknown channel: Brakes" }
]
}
Errors on one KPI never poison the batch — each row is wrapped
in its own try/catch, so you'll see { error: "..." } on the
failing row and real values on the rest.
2. KPI plugins
For KPIs that need imperative logic the expression language can't
express (rolling state machines, multi-channel correlations,
ML-style classifiers), author a plugin implementing the
kpiProcessor interface. See
Writing a Plugin.
The seeded rolling-avg-rpm plugin is the worked example.
The KPI expression language
KPI expressions reuse the math grammar but add channel intrinsics that read from a telemetry sample buffer:
| Intrinsic | Purpose |
|---|---|
Channel("name") | Read a named channel from the active sample window |
Window(seconds, expr) | Run expr with the active window restricted to the trailing N seconds |
RollingAvg(channel, seconds) | Trailing rolling average |
GateAbove(channel, threshold) | Fraction of samples ≥ threshold |
GateBelow(channel, threshold) | Fraction of samples ≤ threshold |
EdgeCount(channel, threshold) | Number of upward zero-crossings of (channel − threshold) |
Delta(channel) | Last − first sample |
Where(channel, predicate-expr) | Filter samples by predicate |
Min / Max / Mean / Average / Avg / Sum / StdDev / First / Last | Scalar reductions over a channel |
Abs / Sqrt / Round / If | Standard scalar helpers |
See KPI Expression Language for the full catalogue with signatures.
What you can do today
- Author a Data Analysis Profile with multiple KPIs in Admin → Data Analysis Profiles
- Evaluate it against a sample buffer via
POST /data-analysis-profiles/:id/evaluate - Author a KPI as a plugin (the path the demo plugin takes)
- Invoke a plugin's KPI processor via
POST /plugins/:id/invoke/kpi(the Plugin Runner UI is the client for this endpoint)
What's coming
- Auto-trigger on new laps — there is no scheduler that runs
KPIs when a new lap is POSTed. Today you call
/data-analysis-profiles/:id/evaluate(or the plugin invoke endpoint) explicitly. - KPI results stored back into the lap row — today they're returned in the response only.
- KPI columns on the Run Sheet Main tab — pending a stored KPI snapshot per lap.
In-app surface (/analysis/lap-kpis)
The Lap KPIs screen is now wired in. It lives at
/analysis/lap-kpis and reads the active session from the
context strip at the top of the window.
For every run sheet in the session it shows:
- A summary bar — laps, best, avg, pit stops, and yellow-flag laps.
- A scrollable table — lap number, lap time, Δ to best, three sectors, fuel-used per lap (kg), pit in/out icons, and the track status (green / yellow / red / SC / VSC / FCY) chip.
The fastest lap is highlighted in purple to match the convention used elsewhere (Run Sheet, Strategy Overview).
To see the screen, drop into a session via the breadcrumb and open Analysis → Lap KPIs from the sidebar.