Skip to content

Instantly share code, notes, and snippets.

@takatakamanbou
Last active January 1, 2016 11:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takatakamanbou/c9aafe8d2d63c1a80530 to your computer and use it in GitHub Desktop.
Save takatakamanbou/c9aafe8d2d63c1a80530 to your computer and use it in GitHub Desktop.
##### parsing the .tcx file created by Runtastic #####
import xml.etree.ElementTree as ET
import datetime
import dateutil.parser
import pytz
import numpy as np
# namespace
ns = { 'TCDv2': 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2' }
def readTCD( fn ):
tree = ET.parse( fn )
dictHeader = {}
# <TrainingCenterDatabase>
# <Activities>
# <Activity Sport="running">
# :
# </Activity>
# </Activities>
# </TrainingCenterDatabase>
TrainingCenterDatabase = tree.getroot()
Activities = TrainingCenterDatabase.find( 'TCDv2:Activities', ns )
Activity = Activities.find( 'TCDv2:Activity', ns )
dictHeader['Sport'] = Activity.attrib['Sport']
# <Activity Sport="running">
# <Id>2015-12-31T02:10:16.000Z</Id>
# <Lap StartTime="2015-12-31T02:10:16.000Z">
# <TotalTimeSeconds>11191</TotalTimeSeconds>
# <DistanceMeters>27001</DistanceMeters>
# <Calories>1715</Calories>
# <AverageHeartRateBpm>
# <Value>150</Value>
# </AverageHeartRateBpm>
# <MaximumHeartRateBpm>
# <Value>189</Value>
# </MaximumHeartRateBpm>
# <MaximumSpeed>4.255025</MaximumSpeed>
# <TriggerMethod>Manual</TriggerMethod>
Id = Activity.find( 'TCDv2:Id', ns )
Lap = Activity.find( 'TCDv2:Lap', ns )
Lap_StartTime = Lap.attrib['StartTime']
TotalTimeSeconds = Lap.find( 'TCDv2:TotalTimeSeconds', ns )
DistanceMeters = Lap.find( 'TCDv2:DistanceMeters', ns )
Calories = Lap.find( 'TCDv2:Calories', ns )
startTimeUTC = dateutil.parser.parse( Lap_StartTime )
startTimeJST = startTimeUTC.astimezone( pytz.timezone( 'Asia/Tokyo' ) )
totaltime = float( TotalTimeSeconds.text )
distance = float( DistanceMeters.text )
averagePace = totaltime * 1000 / distance
dictHeader['Id'] = Id.text
dictHeader['TotalTimeSeconds'] = int( TotalTimeSeconds.text )
dictHeader['DistanceMeters'] = int( DistanceMeters.text )
dictHeader['Calories'] = int( Calories.text )
dictHeader['StartTime'] = startTimeJST.isoformat()
dictHeader['AveragePace'] = averagePace
# <Track>
# <Trackpoint>
# ...
# </Trackpoint>
# </Track>
Track = Lap.find( 'TCDv2:Track', ns )
Trackpoints = Track.findall( 'TCDv2:Trackpoint', ns )
npoint = len( Trackpoints )
pointArray = np.empty( npoint, dtype = object )
time_prev = 0.0
time_pause = 0.0
for i, Trackpoint in enumerate( Trackpoints ):
# <Time>2015-12-31T05:22:32.000Z</Time>
Time = Trackpoint.find( 'TCDv2:Time', ns )
delta = dateutil.parser.parse( Time.text ) - startTimeUTC
time = delta.total_seconds()
# <DistanceMeters>27001</DistanceMeters>
DistanceMeters = Trackpoint.find( 'TCDv2:DistanceMeters', ns )
distance = float( DistanceMeters.text )
# <AltitudeMeters>85.8173828125</AltitudeMeters>
AltitudeMeters = Trackpoint.find( 'TCDv2:AltitudeMeters', ns )
altitude = float( AltitudeMeters.text )
# <Position>
# <LatitudeDegrees>34.9740676879882812</LatitudeDegrees>
# <LongitudeDegrees>135.9050292968750000</LongitudeDegrees>
# </Position>
Position = Trackpoint.find( 'TCDv2:Position', ns )
LatitudeDegrees = Position.find( 'TCDv2:LatitudeDegrees', ns )
LongitudeDegrees = Position.find( 'TCDv2:LongitudeDegrees', ns )
latitude = float( LatitudeDegrees.text )
longitude = float( LongitudeDegrees.text )
# considering the time for pausing
if time - time_prev > 30:
time_pause += time - time_prev - 3
#print i, time, time_pause
d = { 'time':time - time_pause, 'distance':distance, 'altitude':altitude,
'latitude':latitude, 'longitude':longitude }
# <HeartRateBpm> occasionally inserted
# <Value>126</Value>
# </HeartRateBpm>
HeartRateBpm = Trackpoint.find( 'TCDv2:HeartRateBpm', ns )
if HeartRateBpm != None:
hr = HeartRateBpm.find( 'TCDv2:Value', ns ).text
d['heartrate'] = float( hr )
pointArray[i] = d
time_prev = time
return dictHeader, pointArray
def compData( pointArray ):
npoint = pointArray.shape[0]
pointData = np.empty( ( npoint, 5 ) )
heartList = []
for i, d in enumerate( pointArray ):
mtime = d['time'] / 60
pointData[i, 0] = mtime
pointData[i, 1] = d['distance']
pointData[i, 2] = d['altitude']
pointData[i, 3] = d['latitude']
pointData[i, 4] = d['longitude']
if 'heartrate' in d:
heartList.append( [ mtime, d['distance'], d['heartrate'] ] )
heartData = np.array( heartList )
dt = 10
time = pointData[dt:, 0] - pointData[:npoint - dt, 0]
dist = pointData[dt:, 1] - pointData[:npoint - dt, 1]
pace = time * 1000.0 / dist
paceData = np.vstack( ( pointData[dt:, 0], pointData[dt:, 1], pace ) ).T
return pointData, paceData, heartData
if __name__ == '__main__':
import matplotlib.pyplot as plt
fn = 'runtastic_20151231_1431.tcx'
dh, pt = readTCD( fn )
print 'Session Type:', dh['Sport']
print 'Start:', dh['StartTime']
print 'Distance: %.2f km' % ( dh['DistanceMeters'] / 1000.0 )
s = dh['TotalTimeSeconds']
th = s / 3600
tm = ( s - th * 3600 ) / 60
ts = s - th * 3600 - tm * 60
print 'Time: %02d:%02d:%02d' % ( th, tm, ts )
s = dh['AveragePace']
tm = np.floor( s / 60 )
ts = np.round( s - tm * 60 )
print 'Pace: %02d:%02d min/km' % ( tm, ts )
print 'Calories: %d kcal' % dh['Calories']
pointData, paceData, heartData = compData( pt )
np.savetxt( 'point.txt', pointData )
np.savetxt( 'pace.txt', paceData )
np.savetxt( 'heart.txt', heartData )
fig, ax1 = plt.subplots()
ax1.plot( pointData[:, 0], pointData[:, 2], 'b-' )
ax1.plot( heartData[:, 0], heartData[:, 2], 'r-' )
ax1.set_xlabel( 'time [min]' )
ax1.set_ylabel( 'altitude [m] & heartrate [Bpm]' )
ax1.set_xticks( np.arange( 0, np.max( pointData[:, 0] ), 30 ) )
ax1.set_ylim( bottom = -100 )
ax2 = ax1.twinx()
ax2.plot( paceData[:, 0], paceData[:, 2], 'g-' )
ax2.set_ylabel( 'pace [min/km]', color = 'g' )
ax2.set_yticks( [ 5, 10, 15, 20 ] )
for tl in ax2.get_yticklabels():
tl.set_color('g')
ax2.set_ylim( top = 50 )
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment