Skip to main content
zerologszerologs
Back to Learn Hub
Telemetry export

Telemetry Export for Video Overlays

How to turn your Zero's diagnostic logs into a CSV or GPX you can drag into Telemetry Overlay, RaceRender, Dashware, Insta360 Studio, Garmin VIRB Edit, and friends.

Last updated: July 30, 2026

TL;DR

No GPS in your video editor? Use CSV (no GPS) — sync to your video by aligning t_seconds = 0 to a known frame. Have GPS from your phone or Garmin? Use GPX merge — we attach Zero metrics as <extensions> to your trackpoints.

Densification (resampling to 1Hz) is optional and off by default. Read the "When to densify" section before turning it on.

Which format should I use?

Quick decision tree based on what you have and what you want.

CSV — fast, no GPS needed

Pure telemetry: timestamp + speed + SOC + voltage + temperatures + everything else, one row per sample.

Use CSV when:
  • You don't have a separate GPS recording
  • You only need data gauges (speed, kW, temps), not a map
  • Your editor is Telemetry Overlay, RaceRender, or Dashware
  • You're syncing manually to a video (drag to position in timeline)
How sync works:
  1. Identify a known moment in your video (engine start, throttle wide-open, etc.)
  2. Find the same moment in the CSV (e.g. speed_kmh jumps from 0 to non-zero)
  3. Note the t_seconds at that moment
  4. In your video editor, align the data clip so that the moment lands on the same video frame
One sample a minute, so align on the clock

Zero logs telemetry once every 60 seconds (measured here), so “the frame where speed leaves zero” is only ever known to within about half a minute. Use the absolute timestamp_iso against your camera's clock for the offset, and treat the data-feature method above as a sanity check rather than the sync itself.

GPX — your GPS + Zero metrics

Your GPS track from a phone app or Garmin, enriched per-trackpoint with Zero telemetry as XML extensions.

Use GPX merge when:
  • You recorded GPS with a phone app (Strava, OsmAnd, Wahoo, GaiaGPS, etc.)
  • You have a Garmin Edge / Forerunner / VIRB recording the ride
  • You want a map overlay in the video (not just gauges)
  • Your editor is Insta360 Studio, Garmin VIRB Edit, or Telemetry Overlay
Why we don't generate GPX without your GPS

Zero motorcycles don't broadcast GPS coordinates in their diagnostic logs. A GPX without lat/lon is invalid. Some tools fake coordinates from speed integration — we don't, because it lies about where you were.

Compatibility with video tools

Which exports work with which editors. = works, ~ = partial / manual mapping needed, — = not supported.

ToolCSVGPXNeeds GPSNotes
Telemetry OverlaynoMost flexible. Reads CSV without GPS, GPX with extensions, gauges + graphs + maps.
RaceRendernoDrag CSV in, map columns to gauges. GPS optional.
Dashware~noCSV-first; you map columns manually to gauges. GPS not required.
GoPro Quik (native)yesReads only GPMF embedded by GoPro hardware. Use Telemetry Overlay alongside it.
DJI native overlayyesLocked to DJI SRT/DAT. Use Telemetry Overlay for Zero data on DJI footage.
Insta360 StudioyesGPX with GPS for path overlay. Use the GPX merge mode.
Garmin VIRB EdityesGPX or FIT. Reads Garmin TrackPointExtension (atemp emitted by us).
Race ChronoyesLap timer + overlay. Needs GPS to be useful.
Harry's Lap TimeryesLap analysis app. CSV import for non-GPS data + paired GPX.
TrackAddictyesLap timer; CSV import works but GPS-centric.

For best results across most editors we recommend Telemetry Overlay — it accepts both CSV and GPX, supports the Zero zero: namespace via custom field mappings, and ships per-frame interpolation.

Gen2 vs Gen3: why Gen3 may need two files

How telemetry coverage differs by generation, and what we do about it.

Three different things are called a “generation” here

This page is about the log format — which binary layout the file uses, and therefore which channels the export can possibly contain. That is not the same as the motorcycle's product generation, and neither is the same as the number our parser stores.

  • Product generation — what Zero built, per the owner manuals. FX (2014–2025) and FXS (2016–2021) are generation 1 on the XMX platform. The FXE (2022–2025) is generation 2 on XMX · Cypher II, as are the S, SR, DS and DSR through 2023. Generation 3 (Cypher III) is the SR/F and SR/S from 2020, the DSR/X from 2023, the S, SR, DS and DSR from 2024, and the 2026 LS1, XE and XB.
  • Log format — the layout on disk. This is what the cards and tables below describe.
  • Routing generation — the log_generation our parser stores, which is a statement about which decoder ran, not about the machine.

The FXE is where all three collide, and all three are true at once: it is a Cypher II product, our router flags it Gen3, and its files decode through the 538XX path — a Gen2-lineage record format. Gen2 vs Gen3: Log Differences works through the routing rule in full.

Two gaps we are explicit about. A 2024-or-later S, SR, DS or DSR is a generation-3 FST motorcycle by the manuals, but the parser's Gen2-only veto list is keyed on the bare model name with no year term, so such a bike is routed down the Gen2 path on its name alone. The 2026 LS1, XE and XB have no route at all. We hold no log from any of those bikes, so we do not know what their files contain — do not read the tables below as covering them.

Gen1 / Gen2 format

S, SR, DS and DSR through 2023; FX 2014–2025; FXS 2016–2021. The FX and FXS are product generation 1, but they write the same MBB\0 format as the Gen2 street bikes, which is why one card covers both.

One log file is enough for motor and pack data: the MBB log carries both, because the BMS publishes onto the bike's CAN bus and the MBB logs what it hears. Per-cell voltages are the exception — those live only in the BMS dump.

Sample rate: one telemetry row per 60 seconds. Measured over every 0x2c “Riding” record in our 352-file corpus: 152,427 intervals, of which 138,084 are exactly 60 s, and not one is 2 s or shorter. That is about 0.017 Hz. See what a one-minute cadence means for a video overlay before you plan one.

Gen3 — two flavours

Our router sends two quite different families down the Gen3 path, and they behave nothing alike:

  • FXE — a Cypher II motorcycle on the XMX platform whose MBB firmware writes the 538XX length-prefixed variant. We route it as Gen3 and decode it with the 538XX decoder, which reads a Gen2-lineage record. Its MBB log is rich: RPM, pack voltage, pack current, SOC, odometer, and pack/motor/controller/ambient temperatures. Road speed is the one channel it does not carry — the speed we used to publish for these bikes was derived from RPM through a fitted constant, and that fit has been withdrawn. Cell voltages and the power limits are not in this record either.
  • SR/F · SR/S · DSR/X (FST, Cypher III) — the native Gen3 ring buffer. Motor telemetry moved into the BMS. The MBB log carries bike state and, on a small fraction of records, the discharge/charge power limits — but no road speed and no odometer, because the field once decoded as an odometer turned out to be a 12 V accessory rail. The BMS log carries voltage, current, SOC, cell data and temperatures.

If you upload both MBB and BMS for an FST bike, we automatically merge them by timestamp (within 30 seconds). The Export dialog shows "Gen3 MBB+BMS merged" and the CSV filename includes _merged.

Hardware quirk for FST bikes

On SR/F · SR/S · DSR/X the MBB telemetry record has no road-speed, RPM or throttle field at all — not "those bytes read zero", but no room for them in the record. Speed/RPM/throttle won't appear in the CSV no matter what you upload. (The record does carry a pack-voltage and a pack-current word; they are present but undecoded, and the BMS log is our source for those channels.) FXE bikes are unaffected.

Gen3 ring buffer caveat

Gen3 logs use a ring buffer that a parked bike fills in about 3 days — the MBB wakes once an hour and writes ~31 entries per cycle, so roughly 78 hibernation cycles is all it takes. If you're trying to export a ride from two weeks ago and the data isn't there, the buffer overwrote it. See Why Your Gen3 Log Shows No Rides for the full story.

What you'll get per generation, in practice

FieldGen1 / Gen2FXE (538XX)SR/F · SR/S · DSR/X
speed_kmhderivednono (hw)
motor_rpmno (hw)
throttle_pctnonono
pack_voltage_v✓ BMS
pack_current_a✓ BMS
power_kw✓ BMS
soc_pct✓ BMS
battery_temp_c✓ BMS
motor_temp_c~
controller_temp_c~
odometer_kmno (hw)
cell_voltage_min/max/deltaBMS fileBMS file✓ BMS
bike_statederived
discharge_limit_kw / charge_limit_kwno~

The FXE is routed as Gen3, but it is a Cypher II motorcycle on the older XMX platform — so its MBB log keeps the rich Gen2-style motor telemetry, road speed excepted. The FST bikes (SR/F, SR/S, DSR/X) consolidated motor instrumentation into the BMS, which is why their MBB logs carry neither speed nor RPM. Always upload both MBB and BMS for FST bikes to get the full picture.

no (hw)” means the bytes are physically absent from the binary on that hardware. “sparse” means present on a fraction of samples — densification helps fill gaps inside an active ride. The two FST power limits are the sparsest of all: they are written as text lines, not on every telemetry record. On a 2021 SR/F dump 190 of 2,238 records carry them; on our DSR/X dump, 94 of 2,535.

derived” means we compute it rather than read it. speed_kmh on a Gen1/Gen2 bike is motor_rpm × a per-model constant (0.0314 for the FX family, 0.0345 for S/SR, 0.028 for DS/DSR) — no Zero log of any generation contains a road-speed field, and this is the same fitted construction we withdrew for the FXE. Treat it as an estimate, not a measurement. bike_state on Gen1/Gen2 is inferred from the event text, so it only ever takes the values RUN, CHRG, STOP and WAKE; the eight-state vocabulary in the column reference below belongs to Gen3.

BMS file” means the channel is not in the MBB record at all. Per-cell voltages come from the bike's BMS dump, which is a separate file and a separate report. The automatic MBB+BMS merge only runs on reports our parser classified generation 3, so on a Gen1/Gen2 bike you export the BMS report on its own to get cell data.

throttle_pct ships as a CSV column and a GPX extension, but nothing in our code ever writes it — no parser path, in any generation, assigns a throttle value. The column is always empty. It is listed here only so you do not go hunting for it.

Densification: when (and why not) to use it

Native sample rates vs resampling to a regular grid.

Start from the real cadence. A Zero writes one telemetry record per minute while riding — measured at exactly 60 s on 138,084 of the 152,427 intervals in our corpus, with no interval anywhere shorter than 10 s. A 30-minute ride is therefore about 30 telemetry rows, not thousands. (On a real 24-minute Gen2 ride: 25 telemetry rows, inside 40 CSV rows total — the extra rows are event lines whose telemetry columns are empty.)

Native (default): we hand you exactly what your bike recorded. Timestamps are irregular (whatever the firmware decided to log), but every value is real.

Resampled to 1 Hz: we lay down a regular one-sample-per-second grid and fill it with linear interpolation between your real samples for numeric values (speed, voltage, temps) and forward-fill for state values (RUN/STOP/CHRG, charging on/off).

We never interpolate across gaps larger than 60 seconds — if your bike sat in HIB (deep sleep) for 10 minutes, the CSV will show that gap as a gap, not as fabricated data.

Put those two facts together before you turn densification on: at a 60-second cadence, resampling to 1 Hz manufactures 59 interpolated rows for every real one. That is fine for a smooth gauge needle and useless as evidence. It also sits right on the interpolation limit — a sample pair 61 s apart is left as a gap, so a densified export is not guaranteed to be continuous.

Turn densification ON when

  • You want smooth needle/gauge animations in the overlay
  • Your editor expects regular-rate input (some legacy tools)
  • You're building a chart that doesn't tolerate timestamp gaps
  • You're using RaceRender's generic data import (prefers regular cadence)

Leave densification OFF when

  • You want forensic accuracy (incident analysis, warranty disputes)
  • Your tool natively interpolates (Telemetry Overlay does)
  • You're feeding the CSV to other software for analysis (pandas, R)
  • You'd rather see real gaps than synthetic smoothness

Step-by-step: CSV in Telemetry Overlay

A worked example with the most common workflow.

  1. 1
    Open your report

    Pick the log file on your reports page. If it's a Gen3 ride, make sure both the MBB and BMS files for that day are uploaded — they'll auto-merge.

  2. 2
    (Optional) Click into the specific ride session

    If you only want a fragment, click the ride session card. The Export dialog will pre-fill that ride's start/end as a custom range — saves you typing.

  3. 3
    Open Export → Telemetry (CSV / GPX)

    Top right of the report page. Stay on the CSV tab. Leave "Resample to 1 Hz" off unless your editor needs it. Click Download CSV.

  4. 4
    Drop the CSV into Telemetry Overlay

    Drag the file onto the Telemetry Overlay window. It auto-detects ISO timestamps. Pick the columns you want as gauges — soc_pct, pack_voltage_v and power_kw are good defaults on any bike. Add speed_kmh only on a Gen1/Gen2 machine; on an FXE or an FST bike that column is empty.

  5. 5
    Sync to your video

    Line the two clocks up: read timestamp_iso on the first row and compare it with your camera's creation time, then set the data offset from the difference. Because the bike samples only once a minute, an identifiable moment in the footage (first hard pull; arriving home) is worth a sanity check but will not get you closer than about half a minute on its own. Render.

CSV column reference

All columns in the exported CSV. Empty cell = the field wasn't recorded for that timestamp.

ColumnUnitSourceWhat it tells you
timestamp_isoISO 8601bothAbsolute time of the sample (with timezone)
t_secondssderivedSeconds from the first row — your sync handle
speed_kmhkm/hderived (Gen1 / Gen2)Not a recorded channel. No Zero log contains road speed; this is motor_rpm × a per-model constant. Empty on the FXE and on the FST bikes
throttle_pct%Always empty. The column exists, but no parser path in any generation ever writes it
motor_rpmRPMMBB (Gen1 / Gen2 / FXE)Motor revolutions per minute — absent from the FST record
torque_nmNmderivedCalculated as power × 9549 / RPM
pack_voltage_vVBMS / Gen1-Gen2 MBB / FXE MBBTotal pack voltage
pack_current_aABMS / Gen1-Gen2 MBB / FXE MBBPositive when discharging, negative when charging. Regen therefore reads negative too, which is how you spot it. (Measured over eight real logs from five bikes: of 2,404 charging samples, 2,397 are negative; of 6,149 samples taken above 500 motor RPM, 4,726 are positive and the rest are regen)
power_kwkWderivedV × A / 1000, so it follows the current sign — negative on regen. One exception: the native Gen3 BMS decoder emits it as a magnitude, always positive
soc_pct%BMS / Gen2 MBBState of charge
motor_temp_c°CMBBMotor temperature
battery_temp_c°CBMS / Gen2 MBBBattery pack temperature
controller_temp_c°CMBBMotor controller temperature
ambient_temp_c°CbothAmbient temperature (where available)
odometer_kmkmMBB (Gen1 / Gen2 / FXE)Lifetime odometer reading — absent from the FST (SR/F, SR/S, DSR/X) record
discharge_limit_kwkWFST MBB onlyBMS-imposed power ceiling (low SOC, hot pack, etc.). Read from text lines, so present on only a few percent of records — and never on an FXE, whose decoder does not produce it
charge_limit_kwkWFST MBB onlyMaximum allowed charge rate at this moment. Same sparsity and the same FXE caveat as above
cell_voltage_min_vVBMSLowest individual cell voltage. BMS log only — no MBB log of any generation carries per-cell data
cell_voltage_max_vVBMSHighest individual cell voltage
cell_voltage_delta_vVBMSSpread between min and max — health indicator
cell_voltage_ocv_min_vVBMSOpen-circuit (unloaded) voltage of the lowest cell
bike_stateGen3 MBB; derived on Gen1 / Gen2Gen3 records RUN / STOP / CHRG / HIB / WAIT / WAKE / STRT / PWSU. On Gen1/Gen2 we infer it from the event text and it only ever reads RUN, CHRG, STOP or WAKE
is_charging0/1BMS (any gen) / Gen3 MBBCharging session active. Empty on a Gen1/Gen2 MBB-only export — that branch never writes the flag, so use the BMS log if you need it
is_under_load0/1Gen3 BMSBMS sees significant current draw

GPX extension reference

The enriched GPX is a valid GPX 1.1 file (your editor will load it). Extra Zero metrics live in two namespaces inside <extensions> blocks:

xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v2"

Garmin standard. We emit gpxtpx:atemp (ambient temperature) when present. Read by Garmin VIRB Edit, Connect, and most GPS analytics tools.

xmlns:zero="https://zerologs.bike/gpx/1"

Custom Zero namespace for everything specific to electric motorcycles:

<zero:speed_kmh>     <zero:throttle_pct>
<zero:motor_rpm>     <zero:torque_nm>
<zero:voltage_v>     <zero:current_a>
<zero:power_kw>      <zero:soc_pct>
<zero:motor_temp_c>  <zero:battery_temp_c>
<zero:controller_temp_c>
<zero:odometer_km>   <zero:state>

Editors that don't understand the namespace will silently ignore it (per XML rules) — no harm done.

Two of these carry the caveats from the column reference: zero:throttle_pct is emitted for schema completeness and is always empty, and zero:speed_kmh is our RPM-derived estimate on Gen1/Gen2 and empty everywhere else.

FAQ

My CSV opens but speeds are stuck at 0 (or the column is empty)

Check which bike it is before you go looking for a second file. On an SR/F, SR/S or DSR/X there is no road-speed field in either log, so no upload fixes it — the MBB record has no room for speed, RPM or throttle, and the BMS carries battery data only. On an FXE the same is true: its record has RPM but no speed, and the RPM-to-speed fit we used to publish has been withdrawn. Only Gen1/Gen2 bikes get a speed column, and even there it is motor_rpm times a per-model constant rather than a recorded value. What a missing MBB log genuinely costs you on an FST bike is bike_state and the power limits, not speed.

My GPX export says "no telemetry samples in the time range"

Your GPS track and the Zero log don't overlap in time. Check both clocks — phone GPS is usually correct; if your bike clock drifted, use the "Time offset (seconds)" field in the GPX merge dialog.

Why is the CSV smaller than I expected?

Because the bike logs far less often than people assume. The telemetry record is written once every 60 seconds on Gen1/Gen2 and on the FXE — measured across our whole 352-file corpus, 138,084 of 152,427 intervals are exactly 60 s and none is shorter than 10 s. Native Gen3 is event-driven on top of that (sparse in HIB, denser in RUN). So a 30-minute ride is roughly 30 telemetry rows, inside maybe 30–70 CSV rows in total; the extra rows are event lines whose telemetry columns are blank. Turn on densification if your editor wants a regular 1 Hz grid, but understand that it is inventing 59 rows for every real one.

Can I export FIT for Strava / Garmin Connect?

Yes — use the FIT tab. On its own the file is positionless (Zero logs have no GPS), so Strava shows it like an indoor ride with speed/power/temperature. Attach a GPS track (GPX) in the same tab to get a mapped activity. Regen shows as 0 W because FIT power can't be negative.

Does my GPX file ever leave my browser?

It goes to our backend for processing (we need server-side resources to query telemetry and align by timestamp), but we don't store it. The merged result is streamed back and not persisted.

What does "Match tolerance" do in the GPX merge?

For each GPS trackpoint we look for the nearest Zero telemetry sample. If the closest one is more than this many seconds away, we leave that trackpoint without telemetry rather than attaching wrong data. The field defaults to 5 seconds, which is too tight for a bike that logs once a minute: at a 60-second cadence a 5-second window reaches under a fifth of your trackpoints and the rest come out bare. Raise it to 30 — that is the smallest value that guarantees every trackpoint finds a sample when samples are 60 s apart. The dialog accepts up to 60.

Why do you not draw a fake GPS path from speed?

It would be wrong. Heading isn't logged, so any reconstruction is a straight line — which lies about your route. We'd rather you bring real GPS.

Why is densification off by default?

Honesty. Showing real samples means the viewer sees what the bike actually recorded, including gaps. Interpolation invents data that wasn't there. Most overlay tools do their own interpolation at render time anyway.

Ready to overlay your Zero ride?

Open one of your reports and click Export → Telemetry. If you don't have any logs uploaded yet, start there.