57fdaf08479ddc2e7da0bf23d21b64b8bbd520d7 — Johann Rudloff 2 years ago feb5bd3
Support sub-second timestamp parsing for FIT files

The FIT standard unfortunately does not support sub-second accuracy for
most events.  The (awesome) Android app "GpsLogger II" exports the
fractional part of the seconds (in milliseconds) via the non-standard
field number "252", which we now support when reading FIT files.
1 files changed, 16 insertions(+), 6 deletions(-)

M activities/activity.py
M activities/activity.py => activities/activity.py +16 -6
@@ 67,19 67,29 @@ def from_gpx_bytes(content: BinaryIO):

    return Track(positions=np.array(positions), hr=np.array(hr_vals))

def from_fit_bytes(content: BinaryIO):
def from_fit_bytes(content: BinaryIO) -> Track:
    fit = FitFile(content)
    positions = []
    hr_vals = []
    for record in fit.get_messages('record'):

        timestamp = record.get('timestamp')
        if timestamp is not None:
            record_time = np.datetime64(timestamp.value, 'ms')
        if timestamp is None:

        record_time = np.datetime64(timestamp.value, 'ms')

        # special agreement with "GpsLogger II" to be able to get sub-second
        # timestamp information:
        # the fractional part of the timestamp is stored in FIT "field number
        # 252" in milliseconds
        timestamp_ms = record.get_value(252)
        if isinstance(timestamp_ms, int) and (0 <= timestamp_ms < 1000):
            record_time += np.timedelta64(timestamp_ms, 'ms')

        lat = record.get('position_lat')
        lng = record.get('position_long')

        if timestamp and lat and lng is not None:
        if (lat is not None) and (lng is not None):
            lat_deg = field_to_deg(lat)
            lng_deg = field_to_deg(lng)
            value_tuple = (record_time,

@@ 89,7 99,7 @@ def from_fit_bytes(content: BinaryIO):

        hr = record.get('heart_rate')
        if timestamp and hr is not None:
        if hr is not None: