Indoor Virtual Run Data Sources

To run or ride a virtual version of a real-world route on a treadmill or smart trainer, the app needs a sequence of GPS coordinates with elevation. The data exists for free; the industry standards are GPX (GPS Exchange Format) and TCX (Training Center XML), both plain XML text files.

Where to get route data

Filling in missing elevation

If a GPX file has coordinates but no elevation (a common gap), elevation can be queried from a public elevation API and stitched back in:

The workflow: parse the GPX, iterate over the trackpoints, query the elevation API in batches, and write a new GPX with elevation populated. Smoothing the elevation curve with a moving average (5–10 points) tends to give a more realistic treadmill experience — raw SRTM data is noisy and produces a sawtooth incline pattern that no real road has.

Translating a route to the equipment

Treadmill (running)

Direct 1:1 translation. Calculate the gradient of the next short segment (e.g. next 50 m) as rise over run, then command the treadmill to that incline. Most apps round to the machine’s incline step granularity.

Smart bike (cycling)

Cycling apps simulate physics. Wheel speed at the trainer does not equal virtual speed on the route. Instead:

  1. Read the rider’s wattage from the bike’s 0x2AD2 characteristic.
  2. Read the current gradient from the GPX file.
  3. Calculate virtual speed by balancing forces:
Power = F_gravity + F_rolling + F_drag

The standard simplified cycling physics:

Solve for v given Power. As gradient steepens, virtual v drops sharply for the same wattage.

In addition, command the bike’s physical resistance (opcode 0x04) so that the pedals feel heavier on virtual hills. The relationship between commanded resistance and felt difficulty varies by bike — needs empirical tuning per machine.

Uploading the virtual session to Strava

Strava’s API supports virtual activities via the /uploads endpoint. Steps:

  1. Each second during the workout, log GPS point from the GPX track + the rider’s HR / cadence / power / speed.
  2. At end of session, write a TCX or FIT file. TCX is easier to generate (plain XML). FIT is binary and smaller, but needs a library.
  3. POST the file to /uploads with trainer=1 and sport_type=VirtualRide or VirtualRun.

Strava processes the file, generates the map and elevation profile, and matches segments — exactly as it would for a Zwift or MyWhoosh upload.

See also