Files
GoldenCheetah/src/TcxParser.cpp
Jamie Kimberley 4d3c208908 Show power and cadence in stationary tcx files
Minor change to handle how duplicate records in tcx files are handled.
This allows for power and cadence to be properly displayed for rides
with no distance information (e.g. trainer rides).
2010-01-13 08:23:41 -08:00

204 lines
6.1 KiB
C++

/*
* Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net),
* J.T Conklin (jtc@acorntoolworks.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <QString>
#include "TcxParser.h"
#include "TimeUtils.h"
TcxParser::TcxParser (RideFile* rideFile)
: rideFile(rideFile)
{
}
bool
TcxParser::startElement( const QString&, const QString&,
const QString& qName,
const QXmlAttributes& qAttributes)
{
buffer.clear();
if (qName == "Activity")
{
lap = 0;
}
else if (qName == "Lap")
{
// Use the time of the first lap as the time of the activity.
if (lap == 0)
{
start_time = convertToLocalTime(qAttributes.value("StartTime"));
rideFile->setStartTime(start_time);
lastDistance = 0.0;
last_time = start_time;
}
lap++;
}
else if (qName == "Trackpoint")
{
power = 0.0;
cadence = 0.0;
hr = 0.0;
alt = 0.0;
distance = -1; // nh - we set this to -1 so we can detect if there was a distance in the trackpoint.
}
return TRUE;
}
bool
TcxParser::endElement( const QString&, const QString&, const QString& qName)
{
if (qName == "Time")
{
time = convertToLocalTime(buffer);
}
else if (qName == "DistanceMeters")
{
distance = buffer.toDouble() / 1000;
}
else if (qName == "Watts")
{
power = buffer.toDouble();
}
else if (qName == "Value")
{
hr = buffer.toDouble();
}
else if (qName == "Cadence")
{
cadence = buffer.toDouble();
}
else if (qName == "AltitudeMeters")
{
alt = buffer.toDouble();
}
else if (qName == "LongitudeDegrees")
{
lon = buffer.toDouble();
}
else if (qName == "LatitudeDegrees")
{
lat = buffer.toDouble();
}
else if (qName == "Trackpoint")
{
// nh - there are track points that don't have any distance info. We need to ignore them
if (distance>=0)
{
// compute the elapsed time and distance traveled since the
// last recorded trackpoint
double delta_t = last_time.secsTo(time);
double delta_d = distance - lastDistance;
if (delta_d<0)
{
delta_d=0;
}
// compute speed for this trackpoint by dividing the distance
// traveled by the elapsed time. The elapsed time will be 0.0
// for the first trackpoint -- so set speed to 0.0 instead of
// dividing by zero.
double speed = 0.0;
if (delta_t > 0.0)
{
speed=delta_d / delta_t * 3600.0;
}
// Don't know what to do about torque
double torque = 0.0;
// Time from beginning of activity
double secs = start_time.secsTo(time);
// Work around bug in 705 firmware where cadence and
// power values repeat when stopped.
if (delta_d == 0.0 && distance != 0.0)
{
power = 0.0;
cadence = 0.0;
}
// Record trackpoint
// for smart recording, the delta_t will not be constant
// average all the calculations based on the previous
// point.
if(rideFile->dataPoints().empty()) {
// first point
rideFile->appendPoint(secs, cadence, hr, distance,
speed, torque, power, alt, lon, lat, lap);
}
else {
// assumption that the change in ride is linear... :)
RideFilePoint *prevPoint = rideFile->dataPoints().back();
double deltaSecs = secs - prevPoint->secs;
double deltaCad = cadence - prevPoint->cad;
double deltaHr = hr - prevPoint->hr;
double deltaDist = distance - prevPoint->km;
double deltaSpeed = speed - prevPoint->kph;
double deltaTorque = torque - prevPoint->nm;
double deltaPower = power - prevPoint->watts;
double deltaAlt = alt - prevPoint->alt;
double deltaLon = lon - prevPoint->lon;
double deltaLat = lat - prevPoint->lat;
if(deltaSecs == 1) {
// no smart recording, just insert the data
rideFile->appendPoint(secs, cadence, hr, distance,
speed, torque, power, alt, lon, lat, lap);
}
else {
for(int i = 1; i <= deltaSecs; i++) {
double weight = i/ deltaSecs;
double kph = prevPoint->kph + (deltaSpeed *weight);
// need to make sure speed goes to zero
kph = kph > 0.35 ? kph : 0;
double cad = prevPoint->cad + (deltaCad * weight);
cad = cad > 0.35 ? cad : 0;
double lat = prevPoint->lat + (deltaLat * weight);
double lon = prevPoint->lon + (deltaLon * weight);
rideFile->appendPoint(
prevPoint->secs + (deltaSecs * weight),
prevPoint->cad + (deltaCad * weight),
prevPoint->hr + (deltaHr * weight),
prevPoint->km + (deltaDist * weight),
kph,
prevPoint->nm + (deltaTorque * weight),
prevPoint->watts + (deltaPower * weight),
prevPoint->alt + (deltaAlt * weight),
lon, // lon
lat, // lat
lap);
}
prevPoint = rideFile->dataPoints().back();
}
}
lastDistance = distance;
}
last_time = time;
}
return TRUE;
}
bool TcxParser::characters( const QString& str )
{
buffer += str;
return TRUE;
}