mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 16:39:57 +00:00
Porting the codebase to QT 5 (5.2) to get the latest bug fixes, performance and improved platform support. This first part is to fixup the codebase to compile on Qt 5, but some aspects have been broken (video). The second part is to migrate from Qwt 6.0.1 to the latest Qwt for multiaxis support. The third part will be to fixup any platform specific issues or issues identified at runtime.
261 lines
7.8 KiB
C++
261 lines
7.8 KiB
C++
/*
|
|
* Copyright (c) 2011 Mark Liversedge (liversedge@gmail.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 "FitlogParser.h"
|
|
#include "TimeUtils.h"
|
|
|
|
// use stc strtod to bypass Qt toDouble() issues
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
FitlogParser::FitlogParser (RideFile* rideFile, QList<RideFile*> *rides)
|
|
: rideFile(rideFile), rides(rides)
|
|
{
|
|
first = true;
|
|
}
|
|
|
|
bool
|
|
FitlogParser::startElement( const QString&, const QString&,
|
|
const QString& qName,
|
|
const QXmlAttributes& qAttributes)
|
|
{
|
|
buffer.clear();
|
|
|
|
if (qName == "Activity") {
|
|
|
|
lap = 0;
|
|
|
|
if (first == true) first = false;
|
|
else {
|
|
|
|
rideFile = new RideFile();
|
|
rideFile->setRecIntSecs(1.0);
|
|
//rideFile->setDeviceType("SportTracks");
|
|
rideFile->setFileFormat("SportTracks (*.fitlog)");
|
|
}
|
|
|
|
rideFile->setStartTime(start_time = convertToLocalTime(qAttributes.value("StartTime")));
|
|
|
|
// if caller is looking for rides...
|
|
if (rides) rides->append(rideFile);
|
|
|
|
} else if (qName == "Lap") {
|
|
|
|
lap++;
|
|
double start = start_time.secsTo(convertToLocalTime(qAttributes.value("StartTime")));
|
|
double stop = start + qAttributes.value("DurationSeconds").toDouble();
|
|
rideFile->addInterval(start, stop, QString("%1").arg(lap));
|
|
|
|
} else if (qName == "Track") {
|
|
|
|
// Use the time of the first lap as the time of the activity.
|
|
track_offset = start_time.secsTo(convertToLocalTime(qAttributes.value("StartTime")));
|
|
|
|
} else if (qName == "Category") {
|
|
|
|
rideFile->setTag("Sport", qAttributes.value("Name"));
|
|
|
|
} else if (qName == "Metadata") {
|
|
|
|
QString source = qAttributes.value("Source");
|
|
if (source != "") rideFile->setDeviceType(source);
|
|
|
|
} else if (qName == "pt") {
|
|
|
|
// set point values to zero
|
|
RideFilePoint point;
|
|
|
|
// extract from the attributes
|
|
for (int i=0; i<qAttributes.count(); i++) {
|
|
QString m = qAttributes.qName(i);
|
|
|
|
if (m == "tm") point.secs = track_offset + qAttributes.value(i).toInt();
|
|
else if (m == "dist") point.km = qAttributes.value(i).toFloat() / 1000.00; // meters to km
|
|
else if (m == "ele") point.alt = qAttributes.value(i).toFloat();
|
|
else if (m == "hr") point.hr = qAttributes.value(i).toFloat();
|
|
else if (m == "cadence") point.cad = qAttributes.value(i).toFloat();
|
|
else if (m == "power") point.watts = qAttributes.value(i).toFloat();
|
|
else if (m == "lat") point.lat = qAttributes.value(i).toFloat();
|
|
else if (m == "lon") point.lon = qAttributes.value(i).toFloat();
|
|
}
|
|
|
|
// now add
|
|
rideFile->appendPoint(point.secs,point.cad,point.hr,point.km,point.kph,point.nm,
|
|
point.watts,point.alt,point.lon,point.lat, point.headwind,
|
|
0.0, RideFile::noTemp, 0.0, point.interval);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
FitlogParser::endElement( const QString&, const QString&, const QString& qName)
|
|
{
|
|
if (qName == "Activity") {
|
|
|
|
// DERIVE DISTANCE FROM GPS
|
|
if (!rideFile->areDataPresent()->km &&
|
|
rideFile->areDataPresent()->lat &&
|
|
rideFile->areDataPresent()->lon) {
|
|
|
|
RideFilePoint last;
|
|
bool first = true;
|
|
double rdist = 0;
|
|
|
|
foreach(RideFilePoint *point, rideFile->dataPoints()) {
|
|
|
|
if (first == true) {
|
|
if (point->lat && point->lon) first = false;
|
|
} else {
|
|
|
|
if (last.lat && last.lon && point->lat && point->lon) {
|
|
|
|
if (rideFile->areDataPresent()->alt)
|
|
rdist += distanceBetween(last.lat, last.lon, last.alt,
|
|
point->lat, point->lon, point->alt);
|
|
else
|
|
rdist += distanceBetween(last.lat, last.lon, point->lat, point->lon);
|
|
}
|
|
point->km = rdist;
|
|
}
|
|
last = *point;
|
|
}
|
|
rideFile->setDataPresent(RideFile::km, (rdist > 0));
|
|
}
|
|
|
|
// DERIVE SPEED
|
|
if (rideFile->areDataPresent()->km && !rideFile->areDataPresent()->kph) {
|
|
|
|
RideFilePoint last;
|
|
bool first = true;
|
|
|
|
foreach(RideFilePoint *point, rideFile->dataPoints()) {
|
|
|
|
if (first == true) {
|
|
first = false;
|
|
} else {
|
|
double distdelta = point->km - last.km;
|
|
double timedelta = point->secs - last.secs;
|
|
|
|
if (timedelta) {
|
|
point->kph = (distdelta / timedelta) * 3600; // km/s to km/h
|
|
|
|
} else {
|
|
point->kph = 0;
|
|
}
|
|
}
|
|
last = *point;
|
|
}
|
|
rideFile->setDataPresent(RideFile::kph, true);
|
|
}
|
|
|
|
// DERIVE RECINTSECS
|
|
QMap<double, int> ints;
|
|
|
|
bool first = true;
|
|
double last = 0;
|
|
|
|
foreach(RideFilePoint *p, rideFile->dataPoints()) {
|
|
|
|
if (first) {
|
|
last = p->secs;
|
|
first = false;
|
|
} else {
|
|
double delta = p->secs-last;
|
|
last = p->secs;
|
|
|
|
// lookup
|
|
int count = ints.value(delta);
|
|
count++;
|
|
ints.insert(delta, count);
|
|
}
|
|
}
|
|
|
|
// which is most popular?
|
|
double populardelta=1.0;
|
|
int count=0;
|
|
QMapIterator<double, int> i(ints);
|
|
while (i.hasNext()) {
|
|
i.next();
|
|
|
|
if (i.value() > count) {
|
|
count = i.value();
|
|
populardelta = i.key();
|
|
}
|
|
}
|
|
rideFile->setRecIntSecs(populardelta);
|
|
|
|
} else if (qName == "Notes") {
|
|
|
|
rideFile->setTag("Notes", buffer);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool FitlogParser::characters( const QString& str )
|
|
{
|
|
buffer += str;
|
|
return true;
|
|
}
|
|
|
|
static const double EARTH_RADIUS = 6378.140; // in km
|
|
static const double PI = 3.14159265358979323846264;
|
|
static const double DEG2RAD = PI/180.00 ;
|
|
|
|
// calculate distance in kilometers between point(lat1,lon1) and point(lat2, lon2)
|
|
double
|
|
FitlogParser::distanceBetween(double lat1, double lon1, double lat2, double lon2)
|
|
{
|
|
double distance;
|
|
|
|
if ((lat1 == lat2) && (lon1 == lon2)) {
|
|
|
|
// same position
|
|
distance = 0.0;
|
|
|
|
} else {
|
|
|
|
lat1 *= DEG2RAD;
|
|
lat2 *= DEG2RAD;
|
|
lon1 *= DEG2RAD;
|
|
lon2 *= DEG2RAD;
|
|
|
|
distance = acos(cos(lat1)*cos(lon1)*cos(lat2)*cos(lon2) +
|
|
cos(lat1)*sin(lon1)*cos(lat2)*sin(lon2) +
|
|
sin(lat1)*sin(lat2)) * EARTH_RADIUS;
|
|
|
|
}
|
|
return distance;
|
|
}
|
|
|
|
double
|
|
FitlogParser::distanceBetween(double lat1, double lon1, double alt1,
|
|
double lat2, double lon2, double alt2)
|
|
{
|
|
double distance = distanceBetween(lat1, lon1, lat2, lon2);
|
|
|
|
// now take in slope
|
|
double delta = alt2-alt1;
|
|
delta *= delta < 0 ? -0.001 : 0.001; // make pos and convert to kms
|
|
distance = sqrt(pow(distance, 2) + pow(delta, 2));
|
|
|
|
return distance;
|
|
}
|