From 32603fbfea9e4b0ec972b67cb78f29f13e412018 Mon Sep 17 00:00:00 2001 From: Paul Colby Date: Mon, 5 Jan 2015 18:20:13 +1100 Subject: [PATCH] Include power data in TCX output (#31, #43, #47) TCX has no support for power data per se, however, both the Garmin Activity Extension (#31) and the Garmin Course Extension (#47) support power data. This change includes power data for both extensions. Note, the Garmin Course Extension is currently not unit tested (will add unit tests shortly). Also, there are a few code paths added in this change that cannot be tested until we get a data set that includes *both* power data and lap data (#46). --- src/polar/v2/trainingsession.cpp | 53 ++++++++++++++++++- src/polar/v2/trainingsession.h | 2 +- .../training-sessions-1.all-extensions.tcx | 2 +- .../training-sessions-1.garmin-activity.tcx | 2 +- ...ining-sessions-19401412.all-extensions.tcx | 7 ++- ...ning-sessions-19401412.garmin-activity.tcx | 2 +- ...ining-sessions-19946380.all-extensions.tcx | 3 +- ...ning-sessions-19946380.garmin-activity.tcx | 2 +- .../training-sessions-2.all-extensions.tcx | 2 +- .../training-sessions-2.garmin-activity.tcx | 2 +- ...ining-sessions-22165267.all-extensions.tcx | 12 ++++- ...ning-sessions-22165267.garmin-activity.tcx | 2 +- ...ining-sessions-42261903.all-extensions.tcx | 32 ++++++++++- ...ning-sessions-42261903.garmin-activity.tcx | 31 ++++++++++- test/polar/v2/testtrainingsession.cpp | 1 + test/polar/v2/testtrainingsession.h | 4 ++ 16 files changed, 144 insertions(+), 15 deletions(-) diff --git a/src/polar/v2/trainingsession.cpp b/src/polar/v2/trainingsession.cpp index 3be9de30..b98f0386 100644 --- a/src/polar/v2/trainingsession.cpp +++ b/src/polar/v2/trainingsession.cpp @@ -1794,6 +1794,10 @@ QDomDocument TrainingSession::toTCX(const QString &buildTime) const tcx.setAttribute(QLatin1String("xmlns:ax2"), QLatin1String("http://www.garmin.com/xmlschemas/ActivityExtension/v2")); } + if (tcxOptions.testFlag(GarminCourseExtension)) { + tcx.setAttribute(QLatin1String("xmlns:cx1"), + QLatin1String("http://www.garmin.com/xmlschemas/CourseExtension/v1")); + } doc.appendChild(tcx); QDomElement activities = doc.createElement(QLatin1String("Activities")); @@ -1825,6 +1829,8 @@ QDomDocument TrainingSession::toTCX(const QString &buildTime) const // Get the "samples" samples. const QVariantList altitude = samples.value(QLatin1String("altitude")).toList(); const QVariantList cadence = samples.value(QLatin1String("cadence")).toList(); + const QVariantList powerLeft = samples.value(QLatin1String("left-pedal-power")).toList(); + const QVariantList powerRight = samples.value(QLatin1String("right-pedal-power")).toList(); const QVariantList distance = samples.value(QLatin1String("distance")).toList(); const QVariantList heartrate = samples.value(QLatin1String("heartrate")).toList(); const QVariantList speed = samples.value(QLatin1String("speed")).toList(); @@ -1955,7 +1961,9 @@ QDomDocument TrainingSession::toTCX(const QString &buildTime) const lap.appendChild(track); // Add any enabled extensions. - if (tcxOptions.testFlag(GarminActivityExtension)) { + if (tcxOptions.testFlag(GarminActivityExtension) || + tcxOptions.testFlag(GarminCourseExtension)) + { QDomElement extensions = doc.createElement(QLatin1String("Extensions")); lap.appendChild(extensions); @@ -1996,7 +2004,37 @@ QDomDocument TrainingSession::toTCX(const QString &buildTime) const .arg(first(cadence.value(QLatin1String("maximum"))).toUInt()))); } - /// @todo AvgWatts and MaxWatts when power data is available. + /// @todo Steps + + // Note, AvgWatts is defined by both the Garmin Activity + // Extension and the Garmin Course Extension schemas. + const QVariantMap power = firstMap(base.value(QLatin1String("power"))); + if (power.contains(QLatin1String("average"))) { + lx.appendChild(doc.createElement(QLatin1String("AvgWatts"))) + .appendChild(doc.createTextNode(QString::fromLatin1("%1") + .arg(first(power.value(QLatin1String("average"))).toUInt()))); + } + if (power.contains(QLatin1String("maximum"))) { + lx.appendChild(doc.createElement(QLatin1String("MaxWatts"))) + .appendChild(doc.createTextNode(QString::fromLatin1("%1") + .arg(first(power.value(QLatin1String("maximum"))).toUInt()))); + } + } + + if (tcxOptions.testFlag(GarminCourseExtension)) { + QDomElement cx = doc.createElement(QLatin1String("CX")); + cx.setAttribute(QLatin1String("xmlns"), + QLatin1String("http://www.garmin.com/xmlschemas/CourseExtension/v1")); + extensions.appendChild(cx); + + // Note, AvgWatts is defined by both the Garmin Activity + // Extension and the Garmin Course Extension schemas. + const QVariantMap power = firstMap(base.value(QLatin1String("power"))); + if (power.contains(QLatin1String("average"))) { + cx.appendChild(doc.createElement(QLatin1String("AvgWatts"))) + .appendChild(doc.createTextNode(QString::fromLatin1("%1") + .arg(first(power.value(QLatin1String("average"))).toUInt()))); + } } } } @@ -2061,6 +2099,17 @@ QDomDocument TrainingSession::toTCX(const QString &buildTime) const .appendChild(doc.createTextNode(cadence.at(index).toString())); } } + + const int currentPowerLeft = (index < powerLeft.length()) ? + first(powerLeft.at(index).toMap().value(QLatin1String("current-power"))).toInt() : 0; + const int currentPowerRight = (index < powerRight.length()) ? + first(powerRight.at(index).toMap().value(QLatin1String("current-power"))).toInt() : 0; + const int currentPower = currentPowerLeft + currentPowerRight; + if (currentPower != 0) { + tpx.appendChild(doc.createElement(QLatin1String("Watts"))) + .appendChild(doc.createTextNode(QString::fromLatin1("%1") + .arg(currentPower))); + } } if (trackPoint.hasChildNodes()) { diff --git a/src/polar/v2/trainingsession.h b/src/polar/v2/trainingsession.h index 873a7874..16072bc2 100644 --- a/src/polar/v2/trainingsession.h +++ b/src/polar/v2/trainingsession.h @@ -66,7 +66,7 @@ class TrainingSession : public QObject { enum TcxOption { ForceTcxUTC = 0x0001, GarminActivityExtension = 0x0100, - //GarminCourseExtension = 0x0200, //< Needs power support. + GarminCourseExtension = 0x0200, }; Q_DECLARE_FLAGS(TcxOptions, TcxOption) diff --git a/test/polar/v2/testdata/training-sessions-1.all-extensions.tcx b/test/polar/v2/testdata/training-sessions-1.all-extensions.tcx index 7c0f79c7..c193671d 100644 --- a/test/polar/v2/testdata/training-sessions-1.all-extensions.tcx +++ b/test/polar/v2/testdata/training-sessions-1.all-extensions.tcx @@ -1,5 +1,5 @@ - + Bipolar diff --git a/test/polar/v2/testdata/training-sessions-1.garmin-activity.tcx b/test/polar/v2/testdata/training-sessions-1.garmin-activity.tcx index 7c0f79c7..102cbc1b 100644 --- a/test/polar/v2/testdata/training-sessions-1.garmin-activity.tcx +++ b/test/polar/v2/testdata/training-sessions-1.garmin-activity.tcx @@ -1,5 +1,5 @@ - + Bipolar diff --git a/test/polar/v2/testdata/training-sessions-19401412.all-extensions.tcx b/test/polar/v2/testdata/training-sessions-19401412.all-extensions.tcx index 0da8397c..72bbd96b 100644 --- a/test/polar/v2/testdata/training-sessions-19401412.all-extensions.tcx +++ b/test/polar/v2/testdata/training-sessions-19401412.all-extensions.tcx @@ -1,5 +1,5 @@ - + 2014-07-13T07:26:23+10:00 @@ -27082,6 +27082,7 @@ 5.87056 + @@ -49498,6 +49499,7 @@ 4.90671 + @@ -52924,6 +52926,7 @@ 8.38207 + @@ -76375,6 +76378,7 @@ 5.982 + @@ -78691,6 +78695,7 @@ 9.00117 + diff --git a/test/polar/v2/testdata/training-sessions-19401412.garmin-activity.tcx b/test/polar/v2/testdata/training-sessions-19401412.garmin-activity.tcx index 0da8397c..3abf69fb 100644 --- a/test/polar/v2/testdata/training-sessions-19401412.garmin-activity.tcx +++ b/test/polar/v2/testdata/training-sessions-19401412.garmin-activity.tcx @@ -1,5 +1,5 @@ - + 2014-07-13T07:26:23+10:00 diff --git a/test/polar/v2/testdata/training-sessions-19946380.all-extensions.tcx b/test/polar/v2/testdata/training-sessions-19946380.all-extensions.tcx index f620669d..d3e4bced 100644 --- a/test/polar/v2/testdata/training-sessions-19946380.all-extensions.tcx +++ b/test/polar/v2/testdata/training-sessions-19946380.all-extensions.tcx @@ -1,5 +1,5 @@ - + 2014-07-18T07:48:56+10:00 @@ -58315,6 +58315,7 @@ 4.21967 + diff --git a/test/polar/v2/testdata/training-sessions-19946380.garmin-activity.tcx b/test/polar/v2/testdata/training-sessions-19946380.garmin-activity.tcx index f620669d..6b00a8ee 100644 --- a/test/polar/v2/testdata/training-sessions-19946380.garmin-activity.tcx +++ b/test/polar/v2/testdata/training-sessions-19946380.garmin-activity.tcx @@ -1,5 +1,5 @@ - + 2014-07-18T07:48:56+10:00 diff --git a/test/polar/v2/testdata/training-sessions-2.all-extensions.tcx b/test/polar/v2/testdata/training-sessions-2.all-extensions.tcx index 7c0f79c7..c193671d 100644 --- a/test/polar/v2/testdata/training-sessions-2.all-extensions.tcx +++ b/test/polar/v2/testdata/training-sessions-2.all-extensions.tcx @@ -1,5 +1,5 @@ - + Bipolar diff --git a/test/polar/v2/testdata/training-sessions-2.garmin-activity.tcx b/test/polar/v2/testdata/training-sessions-2.garmin-activity.tcx index 7c0f79c7..102cbc1b 100644 --- a/test/polar/v2/testdata/training-sessions-2.garmin-activity.tcx +++ b/test/polar/v2/testdata/training-sessions-2.garmin-activity.tcx @@ -1,5 +1,5 @@ - + Bipolar diff --git a/test/polar/v2/testdata/training-sessions-22165267.all-extensions.tcx b/test/polar/v2/testdata/training-sessions-22165267.all-extensions.tcx index ec4803ab..6046b4f4 100644 --- a/test/polar/v2/testdata/training-sessions-22165267.all-extensions.tcx +++ b/test/polar/v2/testdata/training-sessions-22165267.all-extensions.tcx @@ -1,5 +1,5 @@ - + 2014-08-07T17:25:01+10:00 @@ -4565,6 +4565,7 @@ 80 84 + @@ -9187,6 +9188,7 @@ 83 85 + @@ -13733,6 +13735,7 @@ 83 86 + @@ -17082,6 +17085,7 @@ 85 89 + @@ -22122,6 +22126,7 @@ 81 84 + @@ -27048,6 +27053,7 @@ 82 85 + @@ -30606,6 +30612,7 @@ 84 86 + @@ -35741,6 +35748,7 @@ 81 83 + @@ -40230,6 +40238,7 @@ 81 84 + @@ -40717,6 +40726,7 @@ + diff --git a/test/polar/v2/testdata/training-sessions-22165267.garmin-activity.tcx b/test/polar/v2/testdata/training-sessions-22165267.garmin-activity.tcx index ec4803ab..d7ec6486 100644 --- a/test/polar/v2/testdata/training-sessions-22165267.garmin-activity.tcx +++ b/test/polar/v2/testdata/training-sessions-22165267.garmin-activity.tcx @@ -1,5 +1,5 @@ - + 2014-08-07T17:25:01+10:00 diff --git a/test/polar/v2/testdata/training-sessions-42261903.all-extensions.tcx b/test/polar/v2/testdata/training-sessions-42261903.all-extensions.tcx index 4c3a4c6c..f7724765 100644 --- a/test/polar/v2/testdata/training-sessions-42261903.all-extensions.tcx +++ b/test/polar/v2/testdata/training-sessions-42261903.all-extensions.tcx @@ -1,5 +1,5 @@ - + 2014-12-29T12:58:44+01:00 @@ -54,6 +54,7 @@ 0 + 30 @@ -64,6 +65,7 @@ 0 + 25 @@ -74,6 +76,7 @@ 0 + 37 @@ -84,6 +87,7 @@ 0 + 55 @@ -94,6 +98,7 @@ 0 + 61 @@ -104,6 +109,7 @@ 0 + 31 @@ -114,6 +120,7 @@ 0 + 71 @@ -124,6 +131,7 @@ 0 + 101 @@ -134,6 +142,7 @@ 0 + 142 @@ -144,6 +153,7 @@ 0 + 123 @@ -154,6 +164,7 @@ 0 + 119 @@ -164,6 +175,7 @@ 0 + 47 @@ -174,6 +186,7 @@ 0 + 77 @@ -184,6 +197,7 @@ 0 + 105 @@ -194,6 +208,7 @@ 0 + 136 @@ -204,6 +219,7 @@ 0 + 141 @@ -214,6 +230,7 @@ 0 + 142 @@ -224,6 +241,7 @@ 0 + 132 @@ -234,6 +252,7 @@ 0 + 128 @@ -244,6 +263,7 @@ 0 + 136 @@ -254,6 +274,7 @@ 0 + 113 @@ -264,6 +285,7 @@ 0 + 51 @@ -274,6 +296,7 @@ 0 + 15 @@ -284,6 +307,7 @@ 0 + 19 @@ -294,6 +318,7 @@ 0 + 93 @@ -304,6 +329,7 @@ 0 + 271 @@ -314,6 +340,7 @@ 0 + 287 @@ -324,6 +351,7 @@ 0 + 249 @@ -334,6 +362,7 @@ 0 + 94 @@ -373,6 +402,7 @@ 106 62 + diff --git a/test/polar/v2/testdata/training-sessions-42261903.garmin-activity.tcx b/test/polar/v2/testdata/training-sessions-42261903.garmin-activity.tcx index 4c3a4c6c..4ebb13aa 100644 --- a/test/polar/v2/testdata/training-sessions-42261903.garmin-activity.tcx +++ b/test/polar/v2/testdata/training-sessions-42261903.garmin-activity.tcx @@ -1,5 +1,5 @@ - + 2014-12-29T12:58:44+01:00 @@ -54,6 +54,7 @@ 0 + 30 @@ -64,6 +65,7 @@ 0 + 25 @@ -74,6 +76,7 @@ 0 + 37 @@ -84,6 +87,7 @@ 0 + 55 @@ -94,6 +98,7 @@ 0 + 61 @@ -104,6 +109,7 @@ 0 + 31 @@ -114,6 +120,7 @@ 0 + 71 @@ -124,6 +131,7 @@ 0 + 101 @@ -134,6 +142,7 @@ 0 + 142 @@ -144,6 +153,7 @@ 0 + 123 @@ -154,6 +164,7 @@ 0 + 119 @@ -164,6 +175,7 @@ 0 + 47 @@ -174,6 +186,7 @@ 0 + 77 @@ -184,6 +197,7 @@ 0 + 105 @@ -194,6 +208,7 @@ 0 + 136 @@ -204,6 +219,7 @@ 0 + 141 @@ -214,6 +230,7 @@ 0 + 142 @@ -224,6 +241,7 @@ 0 + 132 @@ -234,6 +252,7 @@ 0 + 128 @@ -244,6 +263,7 @@ 0 + 136 @@ -254,6 +274,7 @@ 0 + 113 @@ -264,6 +285,7 @@ 0 + 51 @@ -274,6 +296,7 @@ 0 + 15 @@ -284,6 +307,7 @@ 0 + 19 @@ -294,6 +318,7 @@ 0 + 93 @@ -304,6 +329,7 @@ 0 + 271 @@ -314,6 +340,7 @@ 0 + 287 @@ -324,6 +351,7 @@ 0 + 249 @@ -334,6 +362,7 @@ 0 + 94 diff --git a/test/polar/v2/testtrainingsession.cpp b/test/polar/v2/testtrainingsession.cpp index f2f864db..091f2727 100644 --- a/test/polar/v2/testtrainingsession.cpp +++ b/test/polar/v2/testtrainingsession.cpp @@ -1364,6 +1364,7 @@ void TestTrainingSession::toTCX_AllExtensions() polar::v2::TrainingSession session(baseName); QVERIFY(session.parse()); session.setTcxOption(polar::v2::TrainingSession::GarminActivityExtension); + session.setTcxOption(polar::v2::TrainingSession::GarminCourseExtension); QDomDocument tcx = session.toTCX(QLatin1String("Jul 17 2014 21:02:38")); // Write the result to an XML file for optional post-mortem investigations. diff --git a/test/polar/v2/testtrainingsession.h b/test/polar/v2/testtrainingsession.h index e1e406b2..886a7ae7 100644 --- a/test/polar/v2/testtrainingsession.h +++ b/test/polar/v2/testtrainingsession.h @@ -92,6 +92,10 @@ private slots: void toTCX_GarminActivity_data(); void toTCX_GarminActivity(); + /// @todo Include Garmin Course Extension tests. + //void toTCX_GarminCourse_data(); + //void toTCX_GarminCourse(); + void toTCX_UTC_data(); void toTCX_UTC();