Skip to content

Lap file format

Laps export and import as JSON (Sessions → json / Import lap…) so they can be shared or backed up, and export as CSV for MoTeC i2 or Excel.

JSON export (v2)

{
  "format": "gt7-datalogger-lap",
  "version": 2,
  "lap": {
    "number": 4,
    "time_ms": 92450,
    "car_id": 3298,
    "fuel_start": 42.1,
    "fuel_end": 40.3,
    "...": "per-lap metrics, engine health, tod_ms",
    "counts_for_best": false,
    "full_lap": true,
    "best_override": false,
    "exclude_reason": "contact",
    "salvaged": false,
    "events": [ { "type": "lockup", "start_dist": 812.4, "end_dist": 818.0,
                  "wheels": ["fl"], "severity": 0.71 } ],
    "gearing": { "ratios": [3.21, 2.44, 1.88, 1.51, 1.24, 1.03],
                 "top_speed": 289.0, "rpm_alert": 7500 },
    "wheelbase_m": 2.6,
    "samples": { "t": [...], "dist": [...], "speed": [...], "...": "..." }
  }
}

counts_for_best says whether the lap counts toward bests; full_lap is the span guard's verdict and best_override the user's ruling on top of it (null = none), with exclude_reason saying why when the ruling is an exclusion — one of off-track, contact, restart, dirty, pit-out. salvaged marks a lap recovered from a replay ending. All five are additive; the format version is unchanged. So is a sample column added since, such as body_slip or suggested_gear: a file without it imports as a lap without the channel, and an older version importing a file with it drops the column it does not know.

wheelbase_m is the car's wheelbase in metres as packet C broadcasts it (constant per car), rounded to a millimetre, and null for a lap recorded without packet C. With the wheel_angle sample column (the front road wheels' mean steering angle, packet C only) it gives the yaw rate the car would have with no slip, speed × wheel_angle ÷ wheelbase_m. Both are additive like the fields above: the version stays 2, a file without them imports as a lap without them, and an older version drops them.

The samples object holds the full 60 Hz series as parallel arrays, one per channel — see Derived channels & metrics for the complete column list with formulas and units (41 columns: time, distance, speed, inputs, gear and the gear GT7 suggested, RPM, boost, per-wheel slip, per-corner tire temps and suspension travel, world position and elevation, fuel, driver-aids bitmask, surface, and the optional steering, g-force, filtered-input, race-position, body-slip, front-wheel-angle and engine-torque columns). suggested_gear holds 15 where GT7 suggested nothing, and is left out of a lap on which it suggested nothing at all. t and dist both start half a frame gap past the line (why); files written by earlier versions start t at exactly 0 and are converted when imported (and when exported again).

Importing

POST /api/laps/import (or the Import lap… button) accepts the envelope above:

  • If no session is active, an imported session is created to hold the lap.
  • Events and aid-usage metrics are recomputed from the samples on import (so imports benefit from detector improvements), while engine-health aggregates and gearing and wheelbase_m are carried over verbatim.
  • The clock anchor is brought up to date: a file from before the half-gap anchor (t from exactly 0) is converted on import, like a stored lap is on read.
  • Verdicts are carried, not re-derived: salvaged, full_lap and any best_override / exclude_reason. The span guard needs the rest of the session to judge a lap, which an import doesn't have — so a pit out-lap stays partial and an excluded lap stays excluded, including a race-start exclusion. Files written before these fields existed fall back to counts_for_best, which was the span guard's verdict at the time.
  • v1 files (from older versions) import cleanly — the newer per-corner channels are simply absent and the charts skip them; events stay empty since the columns they need aren't there.

Session archive (ZIP)

GET /api/sessions/{id}/export.zip (Sessions → Export session) packs a whole session into one file:

gt7-session-12/
  session.json
  analysis.json
  laps/lap-001-345.json
  laps/lap-002-346.json

Each laps/ file is the JSON export above, unchanged — lap number first so a file browser lists them in driving order, lap id after it because a session can hold two laps with one number (GT7 re-reporting a lap after a rewind). Every one of them imports on its own with Import lap….

session.json describes what they belong to:

{
  "format": "gt7-datalogger-session",
  "version": 1,
  "exported_at": "2026-09-16T16:38:23+00:00",
  "session": { "id": 12, "car_name": "...", "track_name": "...", "tags": ["wet"],
               "note": "...", "bests_excluded": false, "final_position": 3, "...": "..." },
  "laps": [ { "file": "laps/lap-001-345.json", "id": 345, "number": 1,
              "time_ms": 92450, "counts_for_best": true } ],
  "analysis": "analysis.json"
}

analysis.json is the session's lap analysis document: every lap measured corner by corner against the session's best. It is derived from the lap files beside it, so nothing reads it back on import. A session without laps has none, and session.json then has no analysis key.

session is the row exactly as GET /api/sessions lists it. Importing a whole archive back — recreating the session rather than adding its laps to the current one — is not supported yet; import the lap files one at a time for now.

CSV export (MoTeC-compatible)

GET /api/laps/{id}/export.csv — a CSV that MoTeC i2's CSV file import (and Excel) understands:

  • Header rows: Format, Device, Vehicle (the car name), Comment, Log Date, and Sample Rate: 60.000
  • Then a channel-name row and a unit row, followed by one row per tick

Every column the lap has, with explicit units: Time (s), Distance (m), Ground Speed (km/h), Throttle Pos (%), Brake Pos (%), Gear, Suggested Gear (15 = none; only on a lap where GT7 suggested one), Engine RPM (rpm), Boost Pressure (bar), Tyre Slip Ratio, Yaw Rate (rad/s), Pos X/Y/Z (m), Ride Height (mm), Fuel Level (L), Tyre Slip FL/FR/RL/RR, Tyre Temp FL/FR/RL/RR (C), Susp Travel FL/FR/RL/RR (mm), Driver Aids and Surface Mask (bitmasks) — and, where the recording carries them, the optional columns: Steering Angle (rad), Accel Lateral / Longitudinal / Vertical (raw), Throttle / Brake Applied (%), Race Position and Body Slip Angle (deg).

Tip

In MoTeC i2, create a new workspace, then File → Import and choose the CSV. The distance channel makes distance-based overlays work the same way they do in the built-in Analysis view.