diff --git a/scripts/fitdump b/scripts/fitdump index 2af0f20..5e4a4b4 100755 --- a/scripts/fitdump +++ b/scripts/fitdump @@ -4,7 +4,9 @@ from __future__ import print_function import argparse import codecs import datetime +import itertools import json +import os.path import sys import types @@ -47,7 +49,7 @@ def parse_args(args=None): ) parser.add_argument( # TODO: csv - '-t', '--type', choices=('readable', 'json'), default='readable', + '-t', '--type', choices=('readable', 'json', 'gpx'), default='readable', help='File type to output. (DEFAULT: %(default)s)', ) parser.add_argument( @@ -93,6 +95,86 @@ class RecordJSONEncoder(json.JSONEncoder): return super(RecordJSONEncoder, self).default(obj) +def generate_gpx(records, filename=None): + # TODO: Use xml.etree.ElementTree ? + + GPX_TIME_FMT = "%Y-%m-%dT%H:%M:%SZ" # ISO 8601 format + + records = iter(records) + + # header + open tags + yield '\n' + yield '\n' + yield ' \n' + + # file creation time (if a file_id record exists) + first_record = [] + for message in records: + if message.name == "file_id": + for field_data in message: + if field_data.name == "time_created" and type(field_data.value) == datetime.datetime: + yield ' \n'.format(field_data.value.strftime(GPX_TIME_FMT)) + break + else: + # No time found in the fields, check next record + continue + break + elif message.name == "record": + first_record.append(message) + break + + if filename: + yield ' {}\n'.format(filename) + + yield ' \n' + yield ' \n' + + if filename: + yield ' {}\n'.format(filename) + + yield ' \n' + + # track points + for message in itertools.chain(first_record, records): + if message.name != "record": + continue + + trkpt = {} + + # TODO: support more data types (heart rate, cadence, etc) + for field_data in message: + if field_data.name == "position_lat": + # Units are decimal degrees + trkpt["lat"] = field_data.value + elif field_data.name == "position_long": + # Units are decimal degrees + trkpt["lon"] = field_data.value + elif field_data.name == "enhanced_altitude": + # Units are m + trkpt["ele"] = field_data.value + elif field_data.name == "timestamp" and type(field_data.value) == datetime.datetime: + trkpt["time"] = field_data.value.strftime(GPX_TIME_FMT) + elif field_data.name == "enhanced_speed" and type(field_data.value) == float: + # convert from km/h to m/s + trkpt["speed"] = field_data.value / 3.6 + + # Add trackpoint + if "lat" in trkpt and "lon" in trkpt: + yield ' \n'.format(**trkpt) + if "ele" in trkpt: + yield ' {ele}\n'.format(**trkpt) + if "time" in trkpt: + yield ' \n'.format(**trkpt) + if "speed" in trkpt: + yield ' {speed}\n'.format(**trkpt) + yield ' \n' + + # close tags + yield ' \n' + yield ' \n' + yield '\n' + + def main(args=None): options = parse_args(args) @@ -111,8 +193,14 @@ def main(args=None): if options.type == "json": json.dump(records, fp=options.output, cls=RecordJSONEncoder) elif options.type == "readable": - options.output.writelines(format_message(n, record, options) - for n, record in enumerate(records, 1)) + options.output.writelines( + format_message(n, record, options) for n, record in enumerate(records, 1) + ) + elif options.type == "gpx": + filename = getattr(options.infile, "name") + if filename: + filename = os.path.basename(filename) + options.output.writelines(generate_gpx(records, filename)) finally: try: options.output.close()