Skip to main content

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:

IntrinsicPurpose
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 / LastScalar reductions over a channel
Abs / Sqrt / Round / IfStandard 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.