DEV Community

Cover image for How I’m Building a Racing-Analysis Web App from Raw Telemetry
Ibrahim Pima
Ibrahim Pima

Posted on

How I’m Building a Racing-Analysis Web App from Raw Telemetry

How I’m Building a Racing-Analysis Web App from Raw Telemetry

(And How You Can Copy-Paste the Whole Stack)


0. The Spark

I spent last winter watching GT4 cars throw 32768-lap grenades into their data streams while the real lap count quietly hid in the time stamps. The ECU clock drifted like a cheap Rolex, but the GPS trace never lied.

That mess is now becoming a Next.js app that turns any $200 OBD+GPS logger into a pro-level race-eng tool. Below is the exact blueprint I’m coding to, parameter by parameter.


1. Data Model – One JSON per Lap

Every time the car crosses the start/finish line I collapse the last chunk of rows into a single document that lands in MongoDB (Atlas free tier).

Shape:

{
_id: ObjectId,
car: { chassis: "004", sticker: "78", ecuTimeOffsetMs: -2473 },
session: { track: "Sebring-12", date: "2025-11-22", type: "Race" },
lap: { nr: 14, time: 123.456, dist: 3861.2, valid: true },
telemetry: {
speed: [96,97,98,…], // 20 Hz
gear: [4,4,4,…],
rpm: [5200,5300,…],
throttle: [78,79,…],
brakeF: [0,0,12.3,…],
brakeR: [0,0,8.7,…],
accX: [-0.12,-0.11,…],
accY: [0.44,0.46,…],
steer: [2.1,2.3,…],
lat: [40.1234567,…],
lon: [-74.654321,…],
sDist: [0,1.6,3.2,…] // distance from SF line (m)
},
meta: {
sampleRate: 20,
ecuClockDriftMs: -2473,
sourceFiles: ["GR86-004-78_20251122_Race.log"]
}
}

Notice I store arrays, not rows – one lap = one document = lightning-fast reads.


2. Lap Count Fix – Kill 32768

Pseudo-code (runs in Node API route):

const FIX_LAP = (rows) => {
let lap = 0, lastDist = 0, lastTime = 0;
return rows.map(r => {
if (r.lap === 32768 || r.lap < lap) {
// derive lap from distance
if (r.Laptrigger_lapdist_dls < lastDist) lap++;
r.lap = lap;
} else {
lap = r.lap;
}
lastDist = r.Laptrigger_lapdist_dls;
lastTime = r.timestamp;
return r;
});
};


3. ECU Clock Drift Compensation

Grab the first GPS UTC timestamp and the ECU timestamp for the same sample:

driftMs = GPS_UTC – ECU_time;

Store driftMs in the car document; subtract it on every future render so engineers see real wall-clock time.


4. Frontend Stack – Next.js 14 + Tailwind + Recharts

Pages
/ – upload .log or .csv

/[chassis] – list all sessions

/[chassis]/[id] – single lap analyser

Components
– two laps, same trace, delta colour-map

– brake pressure overlay, automatic threshold detection

– Mapbox GL, paint racing line by speed (turbo colour scale)

– scatter accX vs accY, 50 ms dots, convex-hull “g-g” envelope

All graphs are SVG, <20 kB each – works on a pit-lane iPad over 4G.


5. The Magic – Automatic Brake Point Detection

  1. Compute speed derivative (m/s²).
  2. Find first sample where decel > 6 m/s² and brakeF > 5 bar.
  3. Walk backward until speed derivative < 1 m/s² → that’s lift-off.
  4. Store distance from SF line.

Now you can sort every lap by “brake point T1” and instantly see who’s late.


6. Delta-T Computer – No VBOX Pro Needed

Because we have Laptrigger_lapdist_dls at 20 Hz, we build a reference lap (best valid lap) then for any other lap:

delta[i] = (t_ref[sDist[i]] – t_lap[i]) * 1000 // ms

Paint it as a colour band under the map trace – green = faster, red = slower.


7. Corner Names Without a Track Map

K-means cluster (lat, lon) samples where |accY| > 1.2 g.

Each cluster centre = apex.

Let the user label once (“T1”, “T2”) → store in a tiny JSON config per track.

Next session the app auto-tags corners.


8. Deployment – Free for Club Racers

Frontend: Vercel hobby tier (git push → live in 30 s).

Backend: Next.js API routes + MongoDB Atlas (512 MB free).

File storage: Vercel blob (upload logs up to 100 MB, auto-deleted after 30 days).

Auth: GitHub OAuth – no passwords, just your team.


9. Copy-Paste Roadmap

Week 1

– Scaffold Next.js 14, /api/upload, parse CSV → raw JSON.

– Build , show raw parameters in a table.

Week 2

– Implement lap fix & drift compensation.

– Store one-lap documents in MongoDB.

– Build table (lap time, tyre, fuel).

Week 3

– Mapbox overlay, colour by speed.

– Brake-point detection API, render as flags on map.

Week 4

– Delta-T trace, two-lap overlay.

– Export best lap as CSV (for engineers who still love Excel).

Week 5

– Corner naming UI, accY cluster.

– Dark mode, print CSS so drivers can tape the page to the dash.


10. What I Learned So Far

  1. 32768 is the new 404.
  2. GPS distance beats ECU lap count every single time.
  3. Storing arrays instead of rows shrinks bundle size by 70 %.
  4. Brake pressure > 5 bar is the simplest “am I braking?” gate across every GT car I’ve tested.
  5. Club racers will give you beer if you auto-detect their brake points – pro teams will give you money if you can do it for 30 cars in real time. Guess which feature I’m building next…

Top comments (0)