gpxplotter icon indicating copy to clipboard operation
gpxplotter copied to clipboard

samsung health gpx files - heart rates

Open ag88 opened this issue 1 year ago • 0 comments

Samsung Health app is able to export a GPX file with heart rates for activities (e.g. bike) https://worksmile.zendesk.com/hc/en-us/articles/17045018603281-Samsung-Health-eksport-plik%C3%B3w-gpx

However, when I tried gpxplotter, I noted that heart rates is not extracted. It turns out the heart rates is formatted as follows in a track point

<gpx ...
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd
>
...
<trkpt lat="15.123" lon="60.123">
<ele>10.020</ele>
<time>2024-10-25T08:38:42Z</time>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>60</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>

currently I'm extracting it like (file: gpxread.py)

def extract_sam_ht_hr(point):
    ext = point.getElementsByTagName("extensions")
    if len(ext) == 0:
        return 0
    ext = ext[0].getElementsByTagName("gpxtpx:TrackPointExtension")
    if len(ext) == 0:
        return 0    
    if len(ext[0].getElementsByTagName("gpxtpx:hr")) == 0:
        return 0
    hr = ext[0].getElementsByTagName("gpxtpx:hr")[0].firstChild.nodeValue
    return int(hr)

def get_point_data(point):
    """Get basic information from a track point.

    Parameters
    ----------
    point : object like :py:class:`xml.dom.minidom.Element`
        The point on the track we are extracting information from.

    """
    data = {
        "lat": float(point.getAttribute("lat")),
        "lon": float(point.getAttribute("lon")),
    }

    ele = extract_data(point, "ele", float)
    if ele is not None:
        data["elevation"] = ele
        
    hr = extract_sam_ht_hr(point)  #<-- called here
    #if not hr == 0:
    data["hr"] = hr

    for key, val in EXTRACT.items():
        value = extract_data(point, val["key"], val["formatter"])
        if value is not None:
            data[key] = value

    return data

apparently, heart rates is missing in the samsung health gpx in the trackpoint when the tracking is paused, i.e. no heartrates in certain trackpoints). if I omit them, the returned list lengths become uneven and it'd cause some issues with matplotlib pyplot etc. Hence, I decided to simply return 0 heart rates for those trackpoints. it is fairly 'intuitive' on a plot, but that it may cause some problems doing calcs. Nevertheless, 0 'makes sense' in this case as during a long pause, it is high before the pause, zero during the pause, and low after the pause, not 'interpolating' them makes it apparent that it is missing during the pause. Apps can choose to handle that zero hr as they deem fit.

this may possibly 'co-exists' with other heart rate formats, if those formats are after all different and distinct. i.e. that heart rates is presented as one format or the other but not both

ag88 avatar Oct 26 '24 16:10 ag88