Decoding FIT files

My bicycle computer (a 1st gen Wahoo ELEMNT Bolt) is recording FIT files. I was interested how much is stored to maybe do something with it. The easiest way I found to export the data is by using the fitdecode Python library. The library already has a ready-to-use CLI command which does everything I need:

fitjson --pretty -o 20190219_094908.json 2019-02-19-094908-ELEMNT\ BOLT\ 8284-15-0.fit

A lot of meta information is in there, but I am mainly interested in the record data frames, i.e.:

{
  "frame_type": "data_message",
  "name": "record",
  "header": {
    "local_mesg_num": 0,
    "time_offset": null,
    "is_developer_data": false
  },
  "fields": [
    {
      "name": "timestamp",
      "value": "2019-02-19T09:53:34+00:00",
      "units": "",
      "def_num": 253,
      "raw_value": 919504414
    },
    {
      "name": "position_lat",
      "value": 48.733650958165526,
      "units": "deg",
      "def_num": 0,
      "raw_value": 581415103
    },
    {
      "name": "position_long",
      "value": 9.120587892830372,
      "units": "deg",
      "def_num": 1,
      "raw_value": 108812852
    },
    {
      "name": "gps_accuracy",
      "value": 2,
      "units": "m",
      "def_num": 31,
      "raw_value": 2
    },
    {
      "name": "enhanced_altitude",
      "value": 410.20000000000005,
      "units": "m",
      "def_num": 78,
      "raw_value": 410.20000000000005
    },
    {
      "name": "altitude",
      "value": 410.20000000000005,
      "units": "m",
      "def_num": 2,
      "raw_value": 4551
    },
    {
      "name": "grade",
      "value": -5.26,
      "units": "%",
      "def_num": 9,
      "raw_value": -526
    },
    {
      "name": "distance",
      "value": 1.5201300000000002,
      "units": "km",
      "def_num": 5,
      "raw_value": 152013
    },
    {
      "name": "heart_rate",
      "value": 106,
      "units": "bpm",
      "def_num": 3,
      "raw_value": 106
    },
    {
      "name": "cadence",
      "value": 0,
      "units": "rpm",
      "def_num": 4,
      "raw_value": 0
    },
    {
      "name": "enhanced_speed",
      "value": 51.9912,
      "units": "km/h",
      "def_num": 73,
      "raw_value": 14.442
    },
    {
      "name": "speed",
      "value": 51.9912,
      "units": "km/h",
      "def_num": 6,
      "raw_value": 14442
    },
    {
      "name": "temperature",
      "value": 12,
      "units": "C",
      "def_num": 13,
      "raw_value": 12
    }
  ],
  "chunk": {
    "index": 346,
    "offset": 8054,
    "size": 27
  }
},

The position is the road downhill from Vaihingen to Kaltental and was probably on my way to work that day. The speed with 51km/h is at that point realistic and 12°C for January sounds good too. Overall this is pretty nice and easy to use.

Every time the data message format changes (i.e. values are added or removed) there is a "definition message". For example there is a "definition message" announcing that the next message is about temperature and battery. Or a following message is about the device (the Wahoo) with serial number, software version and the battery charing state. Or a message about the heartrate sensor device information. Always first a "definition message" and then one or more "data message"s. A bit verbose with a lot of information, but the format is in binary format and seems pretty optimized (35K fit file vs. 2.2M (prettified) json file).

Overall this looks like less trouble than parsing XML based GPX files.