MenuCookbook

IsFIT(), CheckIntegrity(), and Read() Methods

The example projects and the cookbook recipes use the IsFIT(), CheckIntegrity(), and Read() methods when decoding FIT files. These three methods work together to ensure that the file being read is a valid FIT file, and provide a high level of confidence that the data contained in the file is complete and can be trusted.

This recipe covers:

  • The definitions of the IsFIT(), CheckIntegrity(), and Read() methods.
  • Real world examples explaining when to use or maybe not use these methods.

IsFIT()

All valid FIT files should include a 12 or 14 byte file header. The 14 byte header is the preferred header size and the most common size used. Bytes 8–11 of the header contain the ASCII values “.FIT”. This string can easily be spotted when opening a binary FIT file in a text or hex editor.

  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000: 0E 10 43 08 78 06 09 00 2E 46 49 54 96 85 40 00    ..C.x....FIT..@.
00000010: 00 00 00 07 03 04 8C 04 04 86 07 04 86 01 02 84    ................
00000020: 02 02 84 05 02 84 00 01 00 00 19 28 7E C5 95 B0    ...........(~E.0

The IsFIT() method reads the file header and returns true if bytes 8–11 are equal to the ACSII values “.FIT”. IsFIT() provides a quick way to check that the file is a FIT file before attempting to decode the file.

CheckIntegrity()

The CheckIntegrity() method performs three checks on a FIT file:

  1. Checks that bytes 8–11 of the header contain the ASCII values “.FIT”
  2. Checks that the 32-bit data size found in bytes 4–7 in the header is greater than 0
  3. Reads the contents of the file, computes the CRC, and then checks that the computed CRC matches the file CRC.

A file must pass all three of these tests to be considered a valid FIT file. See the best practices section below for use cases where the CheckIntegrity() method should be used and cases when it might be better to avoid it.

Read()

The Read() method reads the contents of the file, creates generic Message and Message Definition objects, and then broadcasts these objects to the registered message listeners. The Read() method returns after all file contents have been read. If an error is encountered while reading the data the Read() method will thrown an exception; therefore, it is recommend to call the Read() method from within a try/catch block.

The Read() method also checks that bytes 8–11 of the header contain the ASCII values “.FIT” and throws an exception if is does not. The Read() method has the option to skip this check when the file does not contain a header.

Best Practices for using the IsFIT(), CheckIntegrity(), and Read() Methods

It is a best practice to use the CheckIntegrity() method when decoding file types where any corrupt or missing data invalidates the entire file. For example, when reading a Course file that contains turn-by-turn directions to help a person navigate from Point A to Point B it is important to have confidence that the data in the file is has not been corrupted in any way. It would be a poor user experience to guide a person halfway to their destination or to have an error in the file direct the person along an unintended route. The same applies for Workout, User Profile, Device Settings, and other file types where it is critical that contents of the entire file can be validated.

There are use cases where it might not be critical that the file passes the integrity check or where calling CheckIntegrity() is not efficient. Activity files are one example of this.

If an Activity file is truncated or corrupted during the recording or syncing process, the file will not have a valid CRC and the CheckIntegrity() method will return false. This does not mean that the contents of the entire file are corrupt, and it may be acceptable, or even desirable, to recover as much data as possible from the file. When given the option, users would rather see part of their activity data versus none of their activity data. Skipping the call to the CheckIntegrity() method will allow the file to be decoded up to the point where it is corrupt.

Calling CheckIntegrity() also means that the full contents of the file will be read twice. The CheckIntegrity() method needs to read the entire file in order to calculate the CRC. The file is then read a second time by the Read() method to decode messages. When working with large Activities files, reading the file twice may be inefficient. Knowing that the Read() method checks that bytes 8–11 of the header contain the ASCII values “.FIT”, means that both the IsFIT() and CheckIntegrity() methods can be skipped when parsing Activity files. Not only does this speed up the file parsing by only reading the file once, it also allows recovery of data from corrupt files. Adding a local try/catch block around the Read() method and handling the FitException type will provide an indication that an error occurred while reading the file which can be communicated to the end user.

// Skip the Integrity check, and read as much data as possible from the file.
try
{
    decoder.Read(fileStream);
}
catch (FitException ex)
{
   // Even if there is an error there may be
   // decoded messages that can be used.
}
catch (Exception ex)
{
   // Even if there is an error there may be
   // decoded messages that can be used.
}

// Parse the decoded messages
...

See the Decoding FIT Activity Files recipe for an example of decoding activity files and working with truncated and corrupt files.