Skip to main content

gps file parsing utilities for garmin connect and garmin devices

Project description

ggps

ggps - gps file parsing utilities for garmin connect and garmin devices

Urls

Features

  • Parse gpx and tcx files downloaded from Garmin Connect (https://connect.garmin.com)
  • Use class TcxHandler to parse TCX files
    • contains the Trackpoint data with additional/augmented values, such as:
      • "elapsedtime", "elapsedseconds", "altitudefeet", "distancemiles", "distancekilometers", and "cadencex2"
    • also includes an event "stats" summary with heartbeat_data and cadence_data sections
    • the stats contains value histograms and computed values such as averages
    • cadence_data includes run/walk/idle time percentages
  • Use class GpxHandler to parse GPX files
    • contains the Trackpoint data with additional/augmented values, such as:
      • "seq", "elapsedtime", "elapsedseconds"
  • Discover the structure of the TCX/GPX/XML files with class PathHandler

Quick start

Installation

$ pip install ggps

Use

Sample Program

See the following sample-program.py in the GitHub repo.

See the data/ directory in the GitHub repo for the sample gpx and tcx files processed by this sample program.

import json

import ggps

def parse_file(infile, handler_type):
    print("parsing file: {} type: {}".format(infile, handler_type))
    handler = None
    opts = dict()
    opts["run_walk_separator_cadence"] = 150  # 150 is the default
    # opts["no-stats"] = "any value"

    if handler_type == "tcx":
        handler = ggps.TcxHandler(opts)
    elif handler_type == "gpx":
        handler = ggps.GpxHandler(opts)
    else:
        handler = ggps.PathHandler(opts)

    handler.parse(infile)
    # print(str(handler))
    write_json_file(handler)


def write_json_file(handler, pretty=True, verbose=True) -> None:
    """Write the parsed handler data to a file in the same directory, with a .json filetype."""
    outfile = "{}.{}.json".format(handler.filename.strip(), handler.handler_type)
    jstr = None
    if pretty is True:
        jstr = json.dumps(handler.get_data(), sort_keys=False, indent=2)
    else:
        jstr = json.dumps(handler.get_data())

    with open(file=outfile, encoding="utf-8", mode="w") as file:
        file.write(jstr)
        if verbose is True:
            print(f"file written: {outfile}")


if __name__ == "__main__":

    print("ggps version {}".format(ggps.VERSION))

    # Latest files produced in 2024 with Forerunner 265S
    parse_file("data/activity_17075053124_lorimer_avinger.tcx", "tcx")
    parse_file("data/activity_17075053124_lorimer_avinger.tcx", "path")
    parse_file("data/activity_17075053124_lorimer_avinger.gpx", "gpx")
    parse_file("data/activity_17075053124_lorimer_avinger.gpx", "path")

    # Twin Cities Marathon files
    parse_file("data/twin_cities_marathon.tcx", "tcx")
    parse_file("data/twin_cities_marathon.tcx", "path")
    parse_file("data/twin_cities_marathon.gpx", "gpx")
    parse_file("data/twin_cities_marathon.gpx", "path")

    # New River 50K files
    parse_file("data/new_river_50k.tcx", "tcx")
    parse_file("data/new_river_50k.tcx", "path")
    parse_file("data/new_river_50k.gpx", "gpx")
    parse_file("data/new_river_50k.gpx", "path")

    # Davidson Track 5K files
    parse_file("data/dav_track_5k.tcx", "tcx")
    parse_file("data/dav_track_5k.tcx", "path")
    parse_file("data/dav_track_5k.gpx", "gpx")
    parse_file("data/dav_track_5k.gpx", "path")

    # activity_4564516081 files
    parse_file("data/activity_4564516081.tcx", "tcx")
    parse_file("data/activity_4564516081.tcx", "path")
    parse_file("data/activity_4564516081.gpx", "gpx")
    parse_file("data/activity_4564516081.gpx", "path")

    # activity_4564516081 files
    parse_file("data/activity_607442311.tcx", "tcx")
    parse_file("data/activity_607442311.tcx", "path")
    parse_file("data/activity_607442311.gpx", "gpx")
    parse_file("data/activity_607442311.gpx", "path")

    # other files
    parse_file("data/activity_19650143389.tcx", "tcx")

Executing the Sample Program

Execute it with the uv command:

uv run sample-program.py

Sample Program Output

ggps version 0.5.0
parsing file: data/activity_17075053124_lorimer_avinger.tcx type: tcx
file written: data/activity_17075053124_lorimer_avinger.tcx.tcx.json
parsing file: data/activity_17075053124_lorimer_avinger.tcx type: path
file written: data/activity_17075053124_lorimer_avinger.tcx.path.json
parsing file: data/activity_17075053124_lorimer_avinger.gpx type: gpx
file written: data/activity_17075053124_lorimer_avinger.gpx.gpx.json
parsing file: data/activity_17075053124_lorimer_avinger.gpx type: path
file written: data/activity_17075053124_lorimer_avinger.gpx.path.json
parsing file: data/twin_cities_marathon.tcx type: tcx
file written: data/twin_cities_marathon.tcx.tcx.json
parsing file: data/twin_cities_marathon.tcx type: path
file written: data/twin_cities_marathon.tcx.path.json
parsing file: data/twin_cities_marathon.gpx type: gpx
file written: data/twin_cities_marathon.gpx.gpx.json
parsing file: data/twin_cities_marathon.gpx type: path
file written: data/twin_cities_marathon.gpx.path.json
parsing file: data/new_river_50k.tcx type: tcx
file written: data/new_river_50k.tcx.tcx.json
parsing file: data/new_river_50k.tcx type: path
file written: data/new_river_50k.tcx.path.json
parsing file: data/new_river_50k.gpx type: gpx
file written: data/new_river_50k.gpx.gpx.json
parsing file: data/new_river_50k.gpx type: path
file written: data/new_river_50k.gpx.path.json
parsing file: data/dav_track_5k.tcx type: tcx
file written: data/dav_track_5k.tcx.tcx.json
parsing file: data/dav_track_5k.tcx type: path
file written: data/dav_track_5k.tcx.path.json
parsing file: data/dav_track_5k.gpx type: gpx
file written: data/dav_track_5k.gpx.gpx.json
parsing file: data/dav_track_5k.gpx type: path
file written: data/dav_track_5k.gpx.path.json
parsing file: data/activity_4564516081.tcx type: tcx
file written: data/activity_4564516081.tcx.tcx.json
parsing file: data/activity_4564516081.tcx type: path
file written: data/activity_4564516081.tcx.path.json
parsing file: data/activity_4564516081.gpx type: gpx
file written: data/activity_4564516081.gpx.gpx.json
parsing file: data/activity_4564516081.gpx type: path
file written: data/activity_4564516081.gpx.path.json
parsing file: data/activity_607442311.tcx type: tcx
file written: data/activity_607442311.tcx.tcx.json
parsing file: data/activity_607442311.tcx type: path
file written: data/activity_607442311.tcx.path.json
parsing file: data/activity_607442311.gpx type: gpx
file written: data/activity_607442311.gpx.gpx.json
parsing file: data/activity_607442311.gpx type: path
file written: data/activity_607442311.gpx.path.json

Sample Output File - TcxParser

{
  "filename": "data/twin_cities_marathon.tcx",
  "ggps_version": "1.0.0",
  "ggps_parsed_at": "2024-09-30 09:44:13.594348",
  "device_name": "Garmin Forerunner 620",
  "device_id": "3875991210",
  "trackpoint_count": 2256,
  "trackpoints": [
    {
      "type": "Trackpoint",
      "time": "2014-10-05T13:07:53.000Z",
      "latitudedegrees": 44.97431952506304,
      "longitudedegrees": -93.26310088858008,
      "altitudemeters": 259.20001220703125,
      "distancemeters": 0.0,
      "heartratebpm": 85,
      "speed": 0.0,
      "cadence": 89,
      "altitudefeet": 850.3937408367167,
      "distancemiles": 0.0,
      "distancekilometers": 0.0,
      "cadencex2": 178,
      "elapsedtime": "00:00:00",
      "seq": 1,
      "elapsedseconds": 0.0
    },

    ...

    {
      "type": "Trackpoint",
      "time": "2014-10-05T17:22:17.000Z",
      "latitudedegrees": 44.95180849917233,
      "longitudedegrees": -93.10493202880025,
      "altitudemeters": 263.6000061035156,
      "distancemeters": 42635.44921875,
      "heartratebpm": 161,
      "speed": 3.5460000038146977,
      "cadence": 77,
      "altitudefeet": 864.8294163501167,
      "distancemiles": 26.492439912628992,
      "distancekilometers": 42.63544921875,
      "cadencex2": 154,
      "elapsedtime": "04:14:24",
      "seq": 2256,
      "elapsedseconds": 15264.0
    }
  ],
  "stats": {
    "heartbeat_data": {
      "histogram": {
        "85": 2,
        "89": 2,
        "90": 2,
        "91": 3,
        "93": 1,
        "96": 2,
        "98": 1,
        "100": 1,
        "104": 1,
        "106": 1,
        "107": 1,
        "108": 3,
        "109": 2,
        "111": 1,
        "112": 1,
        "114": 4,
        "115": 2,
        "116": 1,
        "117": 7,
        "118": 2,
        "119": 3,
        "120": 3,
        "121": 5,
        "122": 3,
        "123": 4,
        "124": 8,
        "125": 15,
        "126": 5,
        "127": 17,
        "128": 22,
        "129": 12,
        "130": 17,
        "131": 23,
        "132": 37,
        "133": 39,
        "134": 34,
        "135": 49,
        "136": 70,
        "137": 82,
        "138": 113,
        "139": 223,
        "140": 221,
        "141": 195,
        "142": 192,
        "143": 124,
        "144": 81,
        "145": 72,
        "146": 75,
        "147": 61,
        "148": 77,
        "149": 55,
        "150": 47,
        "151": 39,
        "152": 32,
        "153": 29,
        "154": 21,
        "155": 12,
        "156": 16,
        "157": 17,
        "158": 8,
        "159": 5,
        "160": 12,
        "161": 8,
        "162": 5,
        "163": 7,
        "164": 3,
        "165": 3,
        "166": 1,
        "167": 4,
        "168": 3,
        "170": 7
      },
      "total_readings": 2256,
      "highest_bpm": 170,
      "average_bpm": 141.18262411347519,
      "most_frequent_bpm": 139
    },
    "cadence_data": {
      "histogram": {
        "88": 2,
        "92": 1,
        "94": 4,
        "100": 3,
        "102": 2,
        "104": 3,
        "106": 1,
        "108": 5,
        "110": 7,
        "112": 13,
        "114": 11,
        "116": 19,
        "118": 57,
        "120": 51,
        "122": 30,
        "124": 22,
        "126": 13,
        "128": 8,
        "130": 6,
        "132": 2,
        "134": 2,
        "136": 1,
        "138": 3,
        "140": 2,
        "142": 3,
        "144": 2,
        "146": 3,
        "148": 2,
        "152": 1,
        "154": 1,
        "156": 1,
        "158": 2,
        "160": 2,
        "162": 5,
        "164": 7,
        "166": 62,
        "168": 496,
        "170": 826,
        "172": 380,
        "174": 90,
        "176": 53,
        "178": 24,
        "180": 10,
        "182": 3,
        "184": 7,
        "186": 2,
        "188": 1,
        "234": 1
      },
      "running_avg_cadence": 170.28571428571428,
      "walking_avg_cadence": 119.6978417266187,
      "run_walk_separator_cadence": 150,
      "total_readings": 2252,
      "running_count": 1974,
      "walking_count": 278,
      "idle_count": 0,
      "running_pct": 87.65541740674956,
      "walking_pct": 12.344582593250445,
      "idle_pct": 0.0
    }
  }
}    

Sample Output File - PathParser

{
  "filename": "data/twin_cities_marathon.tcx",
  "ggps_version": "1.0.0",
  "ggps_parsed_at": "2024-09-30 09:44:14.336540",
  "path_counter": {
    "TrainingCenterDatabase": 1,
    "TrainingCenterDatabase@xsi:schemaLocation": 1,
    "TrainingCenterDatabase@xmlns:ns5": 1,
    "TrainingCenterDatabase@xmlns:ns3": 1,
    "TrainingCenterDatabase@xmlns:ns2": 1,
    "TrainingCenterDatabase@xmlns": 1,
    "TrainingCenterDatabase@xmlns:xsi": 1,
    "TrainingCenterDatabase@xmlns:ns4": 1,
    "TrainingCenterDatabase|Activities": 1,
    "TrainingCenterDatabase|Activities|Activity": 1,
    "TrainingCenterDatabase|Activities|Activity@Sport": 1,
    "TrainingCenterDatabase|Activities|Activity|Id": 1,
    "TrainingCenterDatabase|Activities|Activity|Lap": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap@StartTime": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|TotalTimeSeconds": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|DistanceMeters": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|MaximumSpeed": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|Calories": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|AverageHeartRateBpm": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|AverageHeartRateBpm|Value": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|MaximumHeartRateBpm": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|MaximumHeartRateBpm|Value": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|Intensity": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|TriggerMethod": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Time": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Position": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Position|LatitudeDegrees": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Position|LongitudeDegrees": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|AltitudeMeters": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|DistanceMeters": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|HeartRateBpm": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|HeartRateBpm|Value": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Extensions": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Extensions|TPX": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Extensions|TPX@xmlns": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Extensions|TPX|Speed": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Track|Trackpoint|Extensions|TPX|RunCadence": 2256,
    "TrainingCenterDatabase|Activities|Activity|Lap|Extensions": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|Extensions|LX": 108,
    "TrainingCenterDatabase|Activities|Activity|Lap|Extensions|LX@xmlns": 108,
    "TrainingCenterDatabase|Activities|Activity|Lap|Extensions|LX|MaxRunCadence": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|Extensions|LX|AvgRunCadence": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|Extensions|LX|AvgSpeed": 27,
    "TrainingCenterDatabase|Activities|Activity|Lap|Extensions|LX|Steps": 27,
    "TrainingCenterDatabase|Activities|Activity|Creator": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator@xsi:type": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator|Name": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator|UnitId": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator|ProductID": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator|Version": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator|Version|VersionMajor": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator|Version|VersionMinor": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator|Version|BuildMajor": 1,
    "TrainingCenterDatabase|Activities|Activity|Creator|Version|BuildMinor": 1,
    "TrainingCenterDatabase|Author": 1,
    "TrainingCenterDatabase|Author@xsi:type": 1,
    "TrainingCenterDatabase|Author|Name": 1,
    "TrainingCenterDatabase|Author|Build": 1,
    "TrainingCenterDatabase|Author|Build|Version": 1,
    "TrainingCenterDatabase|Author|Build|Version|VersionMajor": 1,
    "TrainingCenterDatabase|Author|Build|Version|VersionMinor": 1,
    "TrainingCenterDatabase|Author|Build|Version|BuildMajor": 1,
    "TrainingCenterDatabase|Author|Build|Version|BuildMinor": 1,
    "TrainingCenterDatabase|Author|LangID": 1,
    "TrainingCenterDatabase|Author|PartNumber": 1
  }
}

Changelog

Current version: 1.0.0

-  2026/05/29, version 1.0.1   Enhanced exception handling
-  2026/05/29, version 1.0.0   Transition to from pip to uv, pyproject.toml, and python 3.13
-  2024/10/15, version 0.5.1   Enabled support for python 3.11
-  2024/09/30, version 0.5.0   Output as JSON files with numeric attributes
                               stats added to TCX output by default with heartbeat_data and cadence_data sections
                               configurable run_walk_separator_cadence
                               new sample program
-  2024/09/23, version 0.4.1,  Fix pyproject.toml project description
-  2024/09/23, version 0.4.0,  Upgraded to python 3.12, pyproject.toml build mechanism, latest m26 >=0.3.1
-  2020/02/22, version 0.3.0,  Parsing improvements, normalize 'cadence' and 'heartratebpm' attribute names
-  2020/02/19, version 0.2.1,  Upgraded the m26 and Jinga2 libraries
-  2017/09/27, version 0.2.0,  Converted to the pytest testing framework
-  2017/09/26, version 0.1.13, packagin.
-  2016/11/07, version 0.1.12, updated packaging
-  2016/11/07, version 0.1.11, updated packaging
-  2016/11/07, version 0.1.10, updated packaging
-  2016/11/07, version 0.1.9,  updated packaging
-  2016/11/07, version 0.1.8,  updated packaging
-  2016/11/06, version 0.1.7,  updated description
-  2016/11/06, version 0.1.6,  republished
-  2016/11/06, version 0.1.5,  refactored ggps/ dir
-  2016/11/06, version 0.1.4,  refactored ggps/ dir. nose2 for tests
-  2015/11/07, version 0.1.3,  Added README.rst
-  2015/11/07, version 0.1.1   Initial release

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ggps-1.0.1.tar.gz (17.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ggps-1.0.1-py3-none-any.whl (12.6 kB view details)

Uploaded Python 3

File details

Details for the file ggps-1.0.1.tar.gz.

File metadata

  • Download URL: ggps-1.0.1.tar.gz
  • Upload date:
  • Size: 17.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for ggps-1.0.1.tar.gz
Algorithm Hash digest
SHA256 f4049256fd35c182244e62c97b97082ef1320f2dbd300684b6f0671894295e2d
MD5 e86d6eda9a03a932a5a2afd9e2f625f5
BLAKE2b-256 123d56bc153bb6684902a3856713fbf8b70256b9f96f15edb2703158b53a6181

See more details on using hashes here.

File details

Details for the file ggps-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: ggps-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 12.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for ggps-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 61b7a436b8d8124cf070de500ce3f58c5e1bf402e29cd791c4a774967e39c44e
MD5 d1358fd5c9a91c6ed1bb20e16985bab3
BLAKE2b-256 da47f812a7f204cea3d824fbcbf0f8b4366464b4e9120cf0b220e657d6ace044

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page