Wahoo TICKR X .fit file reading/parsing and analysis in Python

1.4k Views Asked by At

Not sure if I can post a question like this here so please redirect me if I'm in the wrong place.

I've bought a Wahoo TICKR X to monitor my heart rate during exercise. Also I would like to get more familiar with python so i decided I would like do do the analyses of my heart rate myself in python instead of in the wahoo app. I thought this would also give more freedom in the choice of visualization, testing etc.

I've recorded my heart rate for 5 minutes or so and exported the .fit file. However I can't even find a suitable library to read the .fit file. Can anyone recommend a library that works with .fit file from wahoo?

I'm using ubuntu, anaconda, python 3.7

import pyfits

# Load the FITS file into the program
hdulist = pyfits.open('/home/bradmin/Downloads/2020-03-26.fit')

# Load table data as tbdata
tbdata = hdulist[1].data



OSError                                   Traceback (most recent call last)
<ipython-input-3-a970e2cd9dee> in <module>
      2 
      3 # Load the FITS file into the program
----> 4 hdulist = pyfits.open('/home/bradmin/Downloads/2020-03-26.fit')
      5 
      6 # Load table data as tbdata

~/anaconda3/lib/python3.7/site-packages/pyfits/hdu/hdulist.py in fitsopen(name, mode, memmap, save_backup, **kwargs)
    122         raise ValueError('Empty filename: %s' % repr(name))
    123 
--> 124     return HDUList.fromfile(name, mode, memmap, save_backup, **kwargs)
    125 
    126 

~/anaconda3/lib/python3.7/site-packages/pyfits/hdu/hdulist.py in fromfile(cls, fileobj, mode, memmap, save_backup, **kwargs)
    264 
    265         return cls._readfrom(fileobj=fileobj, mode=mode, memmap=memmap,
--> 266                              save_backup=save_backup, **kwargs)
    267 
    268     @classmethod

~/anaconda3/lib/python3.7/site-packages/pyfits/hdu/hdulist.py in _readfrom(cls, fileobj, data, mode, memmap, save_backup, **kwargs)
    853             # raise and exception
    854             if mode in ('readonly', 'denywrite') and len(hdulist) == 0:
--> 855                 raise IOError('Empty or corrupt FITS file')
    856 
    857             # initialize/reset attributes to be used in "update/append" mode

OSError: Empty or corrupt FITS file



link to the file: https://wetransfer.com/downloads/6d054a5d52899aefcb1bcd22bda92ba120200326161849/b9831a

EDIT

I've tried this now but i get an error:

import fitdecode

src_file = "/home/bradmin/Downloads/2020-03-26.fit"

with fitdecode.FitReader(src_file) as fit:
    for frame in fit:
        # The yielded frame object is of one of the following types:
        # * fitdecode.FitHeader
        # * fitdecode.FitDefinitionMessage
        # * fitdecode.FitDataMessage
        # * fitdecode.FitCRC

        if isinstance(frame, fitdecode.FitDataMessage):
            # Here, frame is a FitDataMessage object.
            # A FitDataMessage object contains decoded values that
            # are directly usable in your script logic.
            print(frame.name)

file_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
developer_data_id
field_description
field_description
field_description
field_description

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-7-e8d95d3087dc> in <module>
      2 
      3 with fitdecode.FitReader(src_file) as fit:
----> 4     for frame in fit:
      5         # The yielded frame object is of one of the following types:
      6         # * fitdecode.FitHeader

~/anaconda3/lib/python3.7/site-packages/fitdecode/reader.py in __iter__(self)
    191 
    192     def __iter__(self):
--> 193         yield from self._read_next()
    194 
    195     @property

~/anaconda3/lib/python3.7/site-packages/fitdecode/reader.py in _read_next(self)
    298                 assert self._header
    299 
--> 300                 record = self._read_record()
    301                 if not record:
    302                     break

~/anaconda3/lib/python3.7/site-packages/fitdecode/reader.py in _read_record(self)
    443                     self._add_dev_data_id(message)
    444                 elif message.mesg_type.mesg_num == profile.MESG_NUM_FIELD_DESCRIPTION:
--> 445                     self._add_dev_field_description(message)
    446 
    447         return message

~/anaconda3/lib/python3.7/site-packages/fitdecode/reader.py in _add_dev_field_description(self, message)
    780         base_type_id = message.get_field('fit_base_type_id').raw_value
    781         field_name = message.get_field('field_name').raw_value
--> 782         units = message.get_field('units').raw_value
    783 
    784         try:

~/anaconda3/lib/python3.7/site-packages/fitdecode/records.py in get_field(self, field_name_or_num, idx)
    188         raise KeyError(
    189             f'field "{field_name_or_num}" (idx #{idx}) not found in ' +
--> 190             f'message "{self.name}"')
    191 
    192     def get_fields(self, field_name_or_num):

KeyError: 'field "units" (idx #0) not found in message "field_description"'





1

There are 1 best solutions below

3
On

The format seems to be this FIT format. pyfits is for an entirely different format, it seems.

The article above refers to a gpsbabel tool, which you could use to convert the FIT file to something more interoperable and usable, e.g. GPX (an XML-based format that's easy to parse).

Or, of course, if you want a pure-Python solution, you can port the FIT format reading bits from gpsbabel to Python use the fitdecode library.