/* * 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 #include #include "GpxParser.h" #include "TimeUtils.h" #include // use stc strtod to bypass Qt toDouble() issues #include 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(toRadians(lat)) * cos(toRadians(lastLat)) * sin(dlon/2) * sin(dlon /2); //double c = 2*asin(sqrt(fabs(a))); // Alternate definition. double c = 4*atan2(sqrt(a),1+sqrt(1-fabs(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, RideFile::noTemp, 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()) || (secs == 0)) { // no smart recording, or delta exceeds HW treshold, or no time elements; just insert the data rideFile->appendPoint(secs, 0, 0, distance, speed, 0,0, alt, lon, lat, 0, 0.0, RideFile::noTemp, 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, RideFile::noTemp, 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; }