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
- contains the Trackpoint data with additional/augmented values, such as:
- Use class GpxHandler to parse GPX files
- contains the Trackpoint data with additional/augmented values, such as:
- "seq", "elapsedtime", "elapsedseconds"
- contains the Trackpoint data with additional/augmented values, such as:
- 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.2
- 2026/05/29, version 1.0.2 Bump the m26 lib to 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
Metadata
Release files for ggps 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ggps-1.0.2.tar.gz | 17.5 kB | Without provenance | Details for ggps-1.0.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ggps-1.0.2-py3-none-any.whl | Python 3 | none | any | Details for ggps-1.0.2-py3-none-any.whl |
Total release size: 30.1 kB
Release files / ggps-1.0.2.tar.gz
| Download URL | ggps-1.0.2.tar.gz |
|---|---|
| Size | 17.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1b65f0ead67a2a7cdc642b9ee7720f3844d741fede34eead4a1f335b1232dd4e
|
|
BLAKE2b-256 checksum How to use checksums |
4c2b0c81bef54460bad63320517d97264b15317547f24912af316edbe5d2fdb1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / ggps-1.0.2-py3-none-any.whl
| Download URL | ggps-1.0.2-py3-none-any.whl |
|---|---|
| Size | 12.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b31ee50f1ee42481dae593aa0e5b1a31c49ae2cc96640dfacaf43538800f7ebd
|
|
BLAKE2b-256 checksum How to use checksums |
95dfbab80e980b1d128fe6f7f1a9c35f6844b683664a8b2c2217132a49cca35a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Security for ggps 1.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.