Files
GoldenCheetah/src/GpxParser.cpp
Damien Grauser ac3112b286 Add Temperature and Slope Data Series
This patch adds support for temperature and slope
across the ridefile readers.

For the most part their is no functional change
although it is now possible to view and edit these
data series in the editor.

File formats that can provide temp or slope include;
.bin, .fit, .srm, .sync, .wko

Further updates will be required to display the data
in the ride plot and histograms.
2011-12-03 23:17:11 +00:00

233 lines
6.2 KiB
C++

/*
* Copyright (c) 2010 Greg Lonnon (greg.lonnon@gmail.com) copied from
* TcxParser.cpp
* 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 <QDebug>
#include "GpxParser.h"
#include "TimeUtils.h"
#include <math.h>
// use stc strtod to bypass Qt toDouble() issues
#include <stdlib.h>
GpxParser::GpxParser (RideFile* rideFile)
: rideFile(rideFile)
{
isGarminSmartRecording = appsettings->value(NULL, GC_GARMIN_SMARTRECORD,Qt::Checked);
GarminHWM = appsettings->value(NULL, GC_GARMIN_HWMARK);
if (GarminHWM.isNull() || GarminHWM.toInt() == 0)
GarminHWM.setValue(25); // default to 25 seconds.
distance = 0;
lastLat = lastLon = 0;
alt =0;
lon = 0;
lat = 0;
firstTime = true;
metadata = false;
}
bool GpxParser::startElement( const QString&, const QString&,
const QString& qName,
const QXmlAttributes& qAttributes)
{
buffer.clear();
if(metadata)
return true;
if(qName == "metadata")
{
metadata = true;
}
else if(qName == "trkpt")
{
int i = qAttributes.index("lat");
if(i >= 0)
{
lat = qAttributes.value(i).toDouble();
}
else
{
lat = lastLat;
}
i = qAttributes.index("lon");
if( i >= 0)
{
lon = qAttributes.value(i).toDouble();
}
else
{
lon = lastLon;
}
}
return TRUE;
}
#define PI 3.14159265
inline double toRadians(double degrees)
{
return degrees * 2 * PI / 360;
}
bool
GpxParser::endElement( const QString&, const QString&, const QString& qName)
{
if(qName == "metadata")
{
metadata = false;
}
else if(metadata == true)
{
return true;
}
else if (qName == "time")
{
time = convertToLocalTime(buffer);
if(firstTime)
{
start_time = time;
rideFile->setStartTime(time);
firstTime = false;
}
}
else if (qName == "ele")
{
alt = buffer.toDouble(); // metric
}
else if (qName == "trkpt")
{
if(lastLon == 0)
{
// update the "lasts" and find the next point
last_time = time;
lastLon = lon;
lastLat = lat;
return TRUE;
}
// we need to figure out the distance by using the lon,lat
// using teh haversine formula
double r = 6371;
double dlat = toRadians(lat -lastLat); // convert to radians
double dlon = toRadians(lon - lastLon);
double a = sin(dlat /2) * sin(dlat/2) + cos(lat) * cos(lastLat) * sin(dlon/2) * sin(dlon /2);
double c = 2 * atan2(sqrt(a),sqrt(1-a));
double delta_d = r * c;
if(lastLat != 0)
distance += delta_d;
// compute the elapsed time and distance traveled since the
// last recorded trackpoint
double delta_t = last_time.secsTo(time);
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;
}
// Time from beginning of activity
double secs = start_time.secsTo(time);
// 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, 0, 0, distance, speed, 0, 0, alt, lon, lat, 0, 0.0, 0.0, 0);
}
else {
// assumption that the change in ride is linear... :)
RideFilePoint *prevPoint = rideFile->dataPoints().back();
double deltaSecs = secs - prevPoint->secs;
double deltaDist = distance - prevPoint->km;
double deltaSpeed = speed - prevPoint->kph;
double deltaAlt = alt - prevPoint->alt;
double deltaLon = lon - prevPoint->lon;
double deltaLat = lat - prevPoint->lat;
// Smart Recording High Water Mark.
if ((isGarminSmartRecording.toInt() == 0) || (deltaSecs == 1) || (deltaSecs >= GarminHWM.toInt())) {
// no smart recording, or delta exceeds HW treshold, just insert the data
rideFile->appendPoint(secs, 0, 0, distance, speed, 0,0, alt, lon, lat, 0, 0.0, 0.0, 0);
}
else {
// smart recording is on and delta is less than GarminHWM seconds.
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 lat = prevPoint->lat + (deltaLat * weight);
double lon = prevPoint->lon + (deltaLon * weight);
rideFile->appendPoint(
prevPoint->secs + (deltaSecs * weight),
0,
0,
prevPoint->km + (deltaDist * weight),
kph,
0,
0,
prevPoint->alt + (deltaAlt * weight),
lon, // lon
lat, // lat
0,
0.0,
0.0,
0);
}
prevPoint = rideFile->dataPoints().back();
}
}
// update the "lasts" and find the next point
last_time = time;
lastLon = lon;
lastLat = lat;
}
return TRUE;
}
bool GpxParser::characters( const QString& str )
{
buffer += str;
return TRUE;
}