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
- Compute speed derivative (m/s²).
- Find first sample where decel > 6 m/s² and brakeF > 5 bar.
- Walk backward until speed derivative < 1 m/s² → that’s lift-off.
- 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
- 32768 is the new 404.
- GPS distance beats ECU lap count every single time.
- Storing arrays instead of rows shrinks bundle size by 70 %.
- Brake pressure > 5 bar is the simplest “am I braking?” gate across every GT car I’ve tested.
- 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)