def generate_fitfile(data=None, endian='<'): fit_data = ( generate_messages( # local mesg 0, global mesg 0 (file_id) mesg_num=0, local_mesg_num=0, endian=endian, field_defs=[ # serial number, time_created, manufacturer (3, 'uint32z'), (4, 'uint32'), (1, 'uint16'), # product/garmin_product, number, type (2, 'uint16'), (5, 'uint16'), (0, 'enum'), ], # random serial number, random time, garmin, edge500, null, activity data=[[558069241, 723842606, 1, 1036, (2**16) - 1, 4]], )) if data: fit_data += data # Prototcol version 1.0, profile version 1.52 header = pack('<2BHI4s', 14, 16, 152, len(fit_data), b'.FIT') file_data = header + pack('<' + Crc.FMT, Crc.calculate(header)) + fit_data return file_data + pack('<' + Crc.FMT, Crc.calculate(file_data))
def _read_and_assert_crc(self, allow_zero=False): # CRC Calculation is little endian from SDK crc_computed, crc_read = self._crc.value, self._read_struct(Crc.FMT) if not self.check_crc: return if crc_computed == crc_read or (allow_zero and crc_read == 0): return raise FitCRCError('CRC Mismatch [computed: %s, read: %s]' % ( Crc.format(crc_computed), Crc.format(crc_read)))
def _read_and_assert_crc(self, allow_zero=False): # CRC Calculation is little endian from SDK crc_computed, crc_read = self._crc.value, self._read_struct(Crc.FMT) if not self.check_crc: return if crc_computed == crc_read or (allow_zero and crc_read == 0): return raise FitCRCError('CRC Mismatch [computed: %s, read: %s]' % (Crc.format(crc_computed), Crc.format(crc_read)))
def _read_and_assert_crc(self, allow_zero=False): # CRC Calculation is little endian from SDK # TODO - How to handle the case of unterminated file? Error out and have user retry with check_crc=false? crc_computed, crc_read = self._crc.value, self._read_struct(Crc.FMT) if not self.check_crc: return if crc_computed == crc_read or (allow_zero and crc_read == 0): return raise FitCRCError('CRC Mismatch [computed: %s, read: %s]' % ( Crc.format(crc_computed), Crc.format(crc_read)))
def generate_fitfile(data=None, endian='<'): fit_data = ( generate_messages( # local mesg 0, global mesg 0 (file_id) mesg_num=0, local_mesg_num=0, endian=endian, field_defs=[ # serial number, time_created, manufacturer (3, 'uint32z'), (4, 'uint32'), (1, 'uint16'), # product/garmin_product, number, type (2, 'uint16'), (5, 'uint16'), (0, 'enum'), ], # random serial number, random time, garmin, edge500, null, activity data=[[558069241, 723842606, 1, 1036, (2 ** 16) - 1, 4]], ) ) if data: fit_data += data # Prototcol version 1.0, profile version 1.52 header = pack('<2BHI4s', 14, 16, 152, len(fit_data), b'.FIT') file_data = header + pack('<' + Crc.FMT, Crc.calculate(header)) + fit_data return file_data + pack('<' + Crc.FMT, Crc.calculate(file_data))
def test_crc(self): crc = Crc() self.assertEqual(0, crc.value) crc.update(b'\x0e\x10\x98\x00(\x00\x00\x00.FIT') self.assertEqual(0xace7, crc.value) # 0 must not change the crc crc.update(0) self.assertEqual(0xace7, crc.value)
def _parse_file_header(self): # Initialize data self._accumulators = {} self._bytes_left = -1 self._complete = False self._compressed_ts_accumulator = 0 self._crc = Crc() self._local_mesgs = {} # Every FIT must have 'FIT' string in the 8th byte. So, it seems your FIT is wrong. header_data = self._read(12) if header_data[8:12] != b'.FIT': raise FitHeaderError("Invalid .FIT File Header") # Larger fields are explicitly little endian from SDK header_size, protocol_ver_enc, profile_ver_enc, data_size = self._read_struct( '2BHI4x', data=header_data) # Decode the same way the SDK does self.protocol_version = float( "%d.%d" % (protocol_ver_enc >> 4, protocol_ver_enc & ((1 << 4) - 1))) self.profile_version = float( "%d.%d" % (profile_ver_enc / 100, profile_ver_enc % 100)) # Consume extra header information extra_header_size = header_size - 12 if extra_header_size > 0: # Make sure extra field in header is at least 2 bytes to calculate CRC if extra_header_size < 2: raise FitHeaderError('Irregular File Header Size') # Consume extra two bytes of header and check CRC self._read_and_assert_crc(allow_zero=True) # Consume any extra bytes, since header size "may be increased in # "future to add additional optional information" (from SDK) self._read(extra_header_size - 2) # After we've consumed the header, set the bytes left to be read self._bytes_left = data_size
def test_crc_format(self): self.assertEqual('0x0000', Crc.format(0)) self.assertEqual('0x12AB', Crc.format(0x12AB))