<Top of this site> <Top of cycling pages (including japanese texts)>

Format of activity .FIT files created by Garmin Edge 500 (incomplete)


.FIT SDK is now publicly available via

THIS IS ANT - FIT SDK.

You need not read obsolete information below.
(2011-01-09 05:00:00 JST)
(2012-12-10 07:33:44 JST, link updated)


I received .FIT SDK last week. Here are the 1st outputs based on the SDK.

Thanks, Garmin!
(2010-01-21 20:31:16 JST)


Now .FIT SDK seems available from Garmin. Please look at Garmin Developers forum. I keep this document for historical reason:-)
(2010-01-09 08:24:26 JST)


Related workouts are performed

I examined files posted into Garmin Edge 500 and Forerunner 310XT forum as well.

Format analysis is incomplete, and may be totally wrong. Contents below are provided with ABSOLUTELY NO WARRANTY.

..., and thanks to the document

Garmin .FIT File Format,

I got (almost) complete structure of a .FIT file.

As described in the above document:

You can not assume that rec_id's of descriptors are increasing in a file, or that the rec_id of the 1st descriptor is 64. The 1st descriptor's rec_id of any of Forerunner 310XT's .FIT files found in Garmin forum, is always 79, and 2nd one's is 64.

Below, I describe the formats of the records with C struct style. In the description, uint8 represents unsigned 8 bits integer, sint16 represents signed 16 bits integer, and so on. Multi-octets datum seems to be little endian, that is, the octet which appears first in data stream represnts the lowest significant octet of the datum.

/* header */
struct fit_header {
  uint8 rec_id; /* 12, or simply length of header? */
  uint8 xxx0; /* ??? */
  uint8 xxx1; /* ??? */
  uint8 xxx2; /* ??? */
  uint32 file_size; /* actually file size (in octets) - 14, excluding this header and trailing CRC-16? */
  uint8 mark[4]; /* always ".FIT" */
};

/* descriptor of fields of data records */
struct fit_field_desc {
  uint8 xxx0; /* ??? */
  uint8 size; /* size of this field in octets */
  uint8 field_type; /* type of single datum:
                         140 --> unit ID, unsigned 32 bit integer,
                         139 --> ANT+ sensor ID, unsigned 16 bit integer,
                         134 --> unsigned 32 bit integer,
                         133 --> signed 32 bit integer,
                         132 --> unsigned 16 bit integer,
                         131 --> signed 16 bit integer,
                          10 --> ???, 8 bit integer, appears in the last fit_field_desc of descriptor with rec_type 37,
                           7 --> whole field is a zero-terminated string of (printable?) 8 bit integer, 
                           2 --> unsigned 8 bit integer,
                           1 --> signed 8 bit integer,
                           0 --> C's enum like data type?
                       field may be an array of data of this type,
                       so value of size field must be multiple of size of this type
                       (applicative to multi-octets datum?) */
};

/* record descriptor, variable length */
struct fit_record_descriptor {
  uint8 rec_id; /* >= 64, rec_id of data record defined by this descriptor is this value minus 64 */
  uint8 xxx0; /* ??? */
  uint8 xxx1; /* ??? */
  uint16 rec_type; /* type of data record defined by this descriptor
                         0 --> file info.
                         3 --> user profile
                         4 --> heart rate monitor profile
                         6 --> bike profile
                         8 --> heart rate zone
                         9 --> power zone
                        18 --> whole activity
                        19 --> lap
                        20 --> track point
                        21 --> start and end of paused time?
                        22 --> status of sensors?
                        23 --> unit info.?
                        33 --> total workout time, total distance, and more?
                        34 --> ???, appears before CRC
                        37 --> directory info. of storage of this unit?
                        49 --> firmware version?
                   */
  uint8 nfields; /* number of succeeding fit_field_desc's */
  struct fit_field_desc filedv[0];
};

/* defined by record descriptor with rec_type 0 */
struct fit_file_info {
  uint8 rec_id;
  uint32 unit_id;
  uint32 date; /* when start button pressed, seconds since 1989-12-31 00:00:00 GMT */
  uint16 xxx0; /* ??? */
  uint16 product_id;
  uint16 xxx1; /* ???, always max.? */
  uint8 file_type; /* activity .FIT files: 4
                      values of other .FIT files of Edge 500 are
                        Sports/Cycling.fit: 3
                        Device.fit: 1
                        Elevations/Elevations.fit: 8
                        Settings/Settings.fit: 2
                        Totals/Totals.fit: 10
                    */
};

/* defined by record descriptor with rec_type 49 */
struct fit_firmware_version {
  uint8 rec_id;
  uint16 fw_ver; /* major * 100 + minor */
  uint8 xxx0; /* ??? */
};

/* defined by record descriptor with rec_type 21 */
struct fit_pause {
  uint8 rec_id;
  uint32 date; /* seconds since 1989-12-31 00:00:00 GMT */
  uint8 xxx5[7]; /* ??? */
};

/* defined by record descriptor with rec_type 23 */
struct fit_unit_info {
  uint8 rec_id;
  uint32 date; /* seconds since 1989-12-31 00:00:00 GMT */
  uint32 unit_id; /* or 0 */
  uint32 xxx0; /* ???, always max.? */
  uint32 xxx1; /* ???, always max.? */
  uint16 xxx2; /* ??? */
  uint16 product_id; /* or max. */
  uint16 fw_ver; /* major * 100 + minor, or max. */
  uint16 xxx3; /* ???, always max.? */
  uint8 xxx4; /* ??? */
  uint8 xxx5; /* ??? */
  uint8 xxx6; /* ??? */
  uint8 xxx7; /* ??? */
};

/* defined by record descriptor with rec_type 22 */
struct fit_sensor {
  uint8 rec_id;
  uint32 date; /* seconds since 1989-12-31 00:00:00 GMT */
  uint8 xxx0; /* status of power meter or cadence sensor?
                 max. if either power or cadence is lost,
                 2 if either power or cadence is found */
  uint8 xxx1; /* status of power meter or cadence sensor?
                 max. if either power or cadence is lost,
                 2 if either power or cadence is found */
  uint8 xxx2; /* ??? */
  uint8 xxx3; /* ??? */
  uint8 xxx4; /* ??? */
  uint8 xxx5; /* ??? */
  uint8 xxx6; /* ??? */
  uint8 xxx7; /* ??? */
  uint8 xxx8; /* ??? */
};

/* appears before CRC, defined by record descriptor with rec_type 34 */
struct fit_file_trailer {
  uint8 rec_id;
  uint32 date; /* date when reset completed?, seconds since 1989-12-31 00:00:00 GMT */
  uint32 time; /* stop watch time, mili seconds */
  uint16 xxx0;
  uint8 xxx1;
  uint8 xxx2;
  uint8 xxx3;
};


/***********************************/
/* edge500_* are Edge 500 specific */
/***********************************/

/* track point, defined by record descriptor with rec_type 20 */
struct edge500_track_point {
  uint8 rec_id;
  uint32 date; /* seconds since 1989-12-31 00:00:00 GMT */
  sint32 latitude; /* 180 / 2^31 degree, north if positive, max. if GPS is off */
  sint32 longitude; /* 180 / 2^31 degree, east if positive, max. if GPS is off */
  uint32 dist; /* cm */
  sint32 xxx0; /* ???, always max.? */
  uint16 alt; /* uncertain, m / 0.2 + 2500?
                 (cf. max. is 12607m and altitude of toropospher is about 11000m) */
  uint16 spd; /* mm/s, max. if no speed source */
  uint16 pw; /* watt, max. unless sensor present */
  sint16 xxx1; /* ???, always max.? */
  uint8 hr; /* bpm, max. unless sensor present */
  uint8 cad; /* rpm, max. unless sensor present */
  uint8 xxx2; /* ???, always max.? */
  sint8 temperature; /* degree C */
};

/* lap, defined by record descriptor with rec_type 19 */
struct edge500_lap {
  uint8 rec_type;
  uint32 end_date; /* seconds since 1989-12-31 00:00:00 GMT,
                      equals the date of the last track point in this lap
                      if data recording rate is 1Hz
                      and if the lap stopped in the 1st half of the track point */
  uint32 start_date; /* seconds since 1989-12-31 00:00:00 GMT */
  sint32 start_latitude; /* 180 / 2^31 degree, north if positive, max. if GPS is off */
  sint32 start_longitude; /* 180 / 2^31 degree, east if positive, max. if GPS is off */
  sint32 end_latitude; /* 180 / 2^31 degree, north if positive, max. if GPS is off */
  sint32 end_longitude; /* 180 / 2^31 degree, east if positive, max. if GPS is off */
  uint32 time_elapsed; /* mili seconds */
  uint32 time; /* stop watch time, mili seconds */
  uint32 dist; /* cm */
  uint32 xxx0; /* ???, always max.? */
  sint32 xxx1; /* ???, always max.? */
  sint32 xxx2; /* ???, always max.? */
  sint32 xxx3; /* ???, always max.? */
  sint32 xxx4; /* ???, always max.? */
  uint16 nth; /* lap number (0, 1, 2, ...) */
  uint16 kcal; /* based on what? */
  uint16 xxx5; /* ???, always max.?? */
  uint16 spd_ave; /* mm/s, max. if no speed source */
  uint16 spd_max; /* mm/s, max. if no speed source */
  uint16 pw_ave; /* watt,
                    excluding data during a power meter lost
                    regardless zero-averaging setting,
                    max. unless sensor present */
  uint16 pw_max; /* watt, max. unless sensor present */
  uint16 total_ascent; /* m */
  uint16 total_descent; /* m */
  uint8 xxx6; /* ??? */
  uint8 xxx7; /* ??? */
  uint8 hr_ave; /* bpm, max. unless sensor present */
  uint8 hr_max; /* bpm, max. unless sensor present */
  uint8 cad_ave; /* rpm, max. unless sensor present */
  uint8 cad_max; /* rpm, max. unless sensor present */
  uint8 xxx8; /* ??? */
  uint8 lap_trigger; /* 0 --> manual,
                        2 --> distance,
                        3 --> position where LAP pressed,
                        ... */
  uint8 xxx9; /* ??? */
};

/* whole activity, defined by record descriptor with rec_type 18 */
struct edge500_activity {
  uint8 rec_id;
  uint32 reset_date; /* seconds since 1989-12-31 00:00:00 GMT */
  uint32 start_date; /* seconds since 1989-12-31 00:00:00 GMT */
  sint32 start_latitude; /* 180 / 2^31 degree, north if positive, max. if GPS is off */
  sint32 start_longitude; /* 180 / 2^31 degree, east if positive, max. if GPS is off */
  uint32 time_elapsed; /* mili seconds */
  uint32 time; /* stop watch time, mili seconds */
  uint32 dist; /* cm */
  uint32 xxx0; /* ???, always max.? */
  sint32 xxx1; /* ???, always max.? */
  sint32 xxx2; /* ???, always max.? */
  sint32 xxx3; /* ???, always max.? */
  sint32 xxx4; /* ???, always max.? */
  uint16 xxx5; /* ??? */
  uint16 kcal;
  uint16 xxx6; /* ???, always max.? */
  uint16 spd_ave; /* mm/s, max. if no speed source */
  uint16 spd_max; /* mm/s, max. if no speed source */
  uint16 pw_ave; /* watt, max. unless sensor present */
  uint16 pw_max; /* watt, max. unless sensor present */
  uint16 total_ascent; /* m, 0xFFFF should be treated as 0? */
  uint16 total_descent; /* m, 0xFFFF should be treated as 0? */
  uint16 xxx7; /* ??? */
  uint16 xxx8; /* ??? */
  uint8 xxx9; /* ??? */
  uint8 xxx10; /* ??? */
  uint8 xxx11; /* ??? */
  uint8 xxx12; /* ???, always max.?? */
  uint8 hr_ave; /* bpm, max. unless sensor present */
  uint8 hr_max; /* bpm, max. unless sensor present */
  uint8 cad_ave; /* rpm, max. unless sensor present */
  uint8 cad_max; /* rpm, max. unless sensor present */
};


/*******************************************/
/* fr310xt_* are Forerunner 310XT specific */
/*******************************************/

/*

They are based on files posted by other people into Garmin forum,
and on the assumption that data formats may be analogous to those of Edge 500,
so less reliable than edge500_*'s.

*/

/* track point of location data only, defined by record descriptor with rec_type 20 */
struct fr310xt_location {
  uint8 rec_id;
  uint32 date; /* seconds since 1989-12-31 00:00:00 GMT */
  sint32 latitude; /* 180 / 2^31 degree, north if positive, max. if GPS is off */
  sint32 longitude; /* 180 / 2^31 degree, east if positive, max. if GPS is off */
  uint16 alt; /* uncertain, m / 0.2 + 2500?
                 (cf. max. is 12607m and altitude of toropospher is about 11000m) */
};

/* track point without location data, also defined by record descriptor with rec_type 20 */
struct fr310xt_track_point {
  uint8 rec_id;
  uint32 date; /* seconds since 1989-12-31 00:00:00 GMT */
  uint32 dist; /* cm */
  uint16 spd; /* mm/s, max. if no speed source */
  uint16 pw; /* watt, max. unless sensor present */
  uint8 hr; /* bpm, max. unless sensor present */
  uint8 cad; /* rpm, max. unless sensor present */
};

/* lap, defined by record descriptor with rec_type 19 */
struct fr310xt_lap {
  uint8 rec_id;
  uint32 end_date; /* seconds since 1989-12-31 00:00:00 GMT,
                      equals the date of the last track point in this lap
                      if data recording rate is 1Hz
                      and if the lap stopped in the 1st half of the track point */
  uint32 start_date; /* seconds since 1989-12-31 00:00:00 GMT */
  sint32 start_latitude; /* 180 / 2^31 degree, north if positive, max. if GPS is off */
  sint32 start_longitude; /* 180 / 2^31 degree, east if positive, max. if GPS is off */
  sint32 end_latitude; /* 180 / 2^31 degree, north if positive, max. if GPS is off */
  sint32 end_longitude; /* 180 / 2^31 degree, east if positive, max. if GPS is off */
  uint32 time_elapsed; /* mili seconds */
  uint32 time; /* stop watch time, mili seconds */
  uint32 dist; /* cm */
  uint32 xxx0; /* ??? */
  uint16 nth; /* lap number (0, 1, 2, ...) */
  uint16 kcal; /* based on what? */
  uint16 xxx1; /* ???, always max.?? */
  uint16 spd_ave; /* mm/s, max. if no speed source */
  uint16 spd_max; /* mm/s, max. if no speed source */
  uint16 pw_ave; /* watt, max. unless sensor present */
  uint16 pw_max; /* watt, max. unless sensor present */
  uint16 xxx2; /* ??? */
  uint16 xxx3; /* ??? */
  uint8 xxx4; /* ??? */
  uint8 xxx5; /* ??? */
  uint8 hr_ave; /* bpm, max. unless sensor present */
  uint8 hr_max; /* bpm, max. unless sensor present */
  uint8 cad_ave; /* rpm, max. unless sensor present */
  uint8 cad_max; /* rpm, max. unless sensor present */
  uint8 xxx6; /* ??? */
  uint8 lap_trigger; /* 0 --> manual,
                        2 --> distance,
                        3 --> position where LAP pressed,
                        ... */
  uint8 xxx7; /* ??? */
};

/* whole activity, defined by record descriptor with rec_type 18 */
struct fr310xt_activity {
  uint8 rec_id;
  uint32 reset_date; /* seconds since 1989-12-31 00:00:00 GMT */
  uint32 start_date; /* seconds since 1989-12-31 00:00:00 GMT */
  sint32 start_latitude; /* 180 / 2^31 degree, north if positive, max. if GPS is off */
  sint32 start_longitude; /* 180 / 2^31 degree, east if positive, max. if GPS is off */
  uint32 time_elapsed; /* mili seconds */
  uint32 time; /* stop watch time, mili seconds */
  uint32 dist; /* cm */
  uint32 xxx0; /* ??? */
  uint16 xxx1; /* ??? */
  uint16 kcal;
  uint16 xxx2; /* ???, always max.? */
  uint16 spd_ave; /* mm/s, max. if no speed source */
  uint16 spd_max; /* mm/s, max. if no speed source */
  uint16 pw_ave; /* watt, max. unless sensor present */
  uint16 pw_max; /* watt, max. unless sensor present */
  uint16 xxx3; /* ??? */
  uint16 xxx4; /* ???, always max.?? */
  uint16 xxx5; /* ??? */
  uint16 xxx6; /* ??? */
  uint8 xxx7; /* ??? */
  uint8 xxx8; /* ??? */
  uint8 xxx9; /* ??? */
  uint8 xxx10; /* ??? */
  uint8 hr_ave; /* bpm, max. unless sensor present */
  uint8 hr_max; /* bpm, max. unless sensor present */
  uint8 cad_ave; /* rpm, max. unless sensor present */
  uint8 cad_max; /* rpm, max. unless sensor present */
  uint8 xxx63; /* ??? */
  uint8 xxx64; /* ???, always max.? */
};

Any questions or comments about this page are greatly appreciated.

Almost all contents in this site are written by Kiyokazu SUTO (i.e. me) unless especially noted. I want to put all of them into the PUBLIC DOMAIN, even though some lawyers mention that it is impossible in my country.