bool GPSDataParser::loadGPXFile(const KUrl& url) { QFile gpxfile(url.path()); if (!gpxfile.open(QIODevice::ReadOnly)) return false; QDomDocument gpxDoc("gpx"); if (!gpxDoc.setContent(&gpxfile)) return false; QDomElement gpxDocElem = gpxDoc.documentElement(); if (gpxDocElem.tagName()!="gpx") return false; for (QDomNode nTrk = gpxDocElem.firstChild(); !nTrk.isNull(); nTrk = nTrk.nextSibling()) { QDomElement trkElem = nTrk.toElement(); if (trkElem.isNull()) continue; if (trkElem.tagName() != "trk") continue; for (QDomNode nTrkseg = trkElem.firstChild(); !nTrkseg.isNull(); nTrkseg = nTrkseg.nextSibling()) { QDomElement trksegElem = nTrkseg.toElement(); if (trksegElem.isNull()) continue; if (trksegElem.tagName() != "trkseg") continue; for (QDomNode nTrkpt = trksegElem.firstChild(); !nTrkpt.isNull(); nTrkpt = nTrkpt.nextSibling()) { QDomElement trkptElem = nTrkpt.toElement(); if (trkptElem.isNull()) continue; if (trkptElem.tagName() != "trkpt") continue; QDateTime ptDateTime; double ptAltitude = 0.0; double ptLatitude = 0.0; double ptLongitude = 0.0; // Get GPS position. If not available continue to next point. QString lat = trkptElem.attribute("lat"); QString lon = trkptElem.attribute("lon"); if (lat.isEmpty() || lon.isEmpty()) continue; ptLatitude = lat.toDouble(); ptLongitude = lon.toDouble(); // Get metadata of track point (altitude and time stamp) for (QDomNode nTrkptMeta = trkptElem.firstChild(); !nTrkptMeta.isNull(); nTrkptMeta = nTrkptMeta.nextSibling()) { QDomElement trkptMetaElem = nTrkptMeta.toElement(); if (trkptMetaElem.isNull()) continue; if (trkptMetaElem.tagName() == QString("time")) { // Get GPS point time stamp. If not available continue to next point. QString time = trkptMetaElem.text(); if (time.isEmpty()) continue; ptDateTime = QDateTime::fromString(time, Qt::ISODate); } if (trkptMetaElem.tagName() == QString("ele")) { // Get GPS point altitude. If not available continue to next point. QString ele = trkptMetaElem.text(); if (!ele.isEmpty()) ptAltitude = ele.toDouble(); } } if (ptDateTime.isNull()) continue; GPSDataContainer gpsData(ptAltitude, ptLatitude, ptLongitude, false); m_GPSDataMap.insert( ptDateTime, gpsData ); } } } kDebug( 51001 ) << "GPX File " << url.fileName() << " parsed with " << numPoints() << " points extracted" << endl; return true; }
RideFile *PolarFileReader::openRideFile(QFile &file, QStringList &errors, QList<RideFile*>*rideList) const { /* * Polar HRM file format documented at www.polar.fi/files/Polar_HRM_file%20format.pdf */ QRegExp metricUnits("(km|kph|km/h)", Qt::CaseInsensitive); QRegExp englishUnits("(miles|mph|mp/h)", Qt::CaseInsensitive); bool metric = true; QDate date; QString note(""); double version=0; int monitor=0; double seconds=0; double distance=0; int interval = 0; int StartDelay = 0; bool speed = false; bool cadence = false; bool altitude = false; bool power = false; bool balance = false; bool haveGPX = false; int igpx = 0; int ngpx = 0; double lat=0,lon=0; int recInterval = 1; // Read Polar GPX file (if exist with same name as hrm file). RideFile *gpxresult=NULL; RideFilePoint *p; QString suffix = file.fileName(); int dot = suffix.lastIndexOf("."); assert(dot >= 0); QFile gpxfile(suffix.left(dot)+".gpx"); haveGPX = gpxfile.exists(); if (haveGPX) { GpxFileReader reader; gpxresult = reader.openRideFile(gpxfile,errors,rideList); ngpx = gpxresult->dataPoints().count(); } if (!file.open(QFile::ReadOnly)) { errors << ("Could not open ride file: \"" + file.fileName() + "\""); return NULL; } int lineno = 1; double next_interval=0; QList<double> intervals; QTextStream is(&file); RideFile *rideFile = new RideFile(); QString section = NULL; while (!is.atEnd()) { // the readLine() method doesn't handle old Macintosh CR line endings // this workaround will load the the entire file if it has CR endings // then split and loop through each line // otherwise, there will be nothing to split and it will read each line as expected. QString linesIn = is.readLine(); QStringList lines = linesIn.split('\r'); // workaround for empty lines if(lines.size() == 0) { lineno++; continue; } for (int li = 0; li < lines.size(); ++li) { QString line = lines[li]; if (line == "") { } else if (line.startsWith("[")) { //fprintf(stderr, "section : %s\n", line.toAscii().constData()); section=line; if (section == "[HRData]") { // Some systems, like the Tacx HRM exporter, do not add an [IntTimes] section, so we need to // specify that the whole ride is one big interval. if (intervals.isEmpty()) intervals.append(seconds); next_interval = intervals.at(0); } } else if (section == "[Params]"){ if (line.contains("Version=")) { QString versionString = QString(line); versionString.remove(0,8).insert(1, "."); version = versionString.toFloat(); rideFile->setFileFormat("Polar HRM v"+versionString+" (hrm)"); } else if (line.contains("Monitor=")) { QString monitorString = QString(line); monitorString.remove(0,8); monitor = monitorString.toInt(); switch (monitor) { case 1: rideFile->setDeviceType("Polar Sport Tester / Vantage XL"); break; case 2: rideFile->setDeviceType("Polar Vantage NV (VNV)"); break; case 3: rideFile->setDeviceType("Polar Accurex Plus"); break; case 4: rideFile->setDeviceType("Polar XTrainer Plus"); break; case 6: rideFile->setDeviceType("Polar S520"); break; case 7: rideFile->setDeviceType("Polar Coach"); break; case 8: rideFile->setDeviceType("Polar S210"); break; case 9: rideFile->setDeviceType("Polar S410"); break; case 10: rideFile->setDeviceType("Polar S510"); break; case 11: rideFile->setDeviceType("Polar S610 / S610i"); break; case 12: rideFile->setDeviceType("Polar S710 / S710i"); break; case 13: rideFile->setDeviceType("Polar S810 / S810i"); break; case 15: rideFile->setDeviceType("Polar E600"); break; case 20: rideFile->setDeviceType("Polar AXN500"); break; case 21: rideFile->setDeviceType("Polar AXN700"); break; case 22: rideFile->setDeviceType("Polar S625X / S725X"); break; case 23: rideFile->setDeviceType("Polar S725"); break; case 33: rideFile->setDeviceType("Polar CS400"); break; case 34: rideFile->setDeviceType("Polar CS600X"); break; case 35: rideFile->setDeviceType("Polar CS600"); break; case 36: rideFile->setDeviceType("Polar RS400"); break; case 37: rideFile->setDeviceType("Polar RS800"); break; case 38: rideFile->setDeviceType("Polar RS800X"); break; default: rideFile->setDeviceType(QString("Unknown Polar Device %1").arg(monitor)); } } else if (line.contains("SMode=")) { line.remove(0,6); QString smode = QString(line); if (smode.at(0)=='1') speed = true; if (smode.length()>0 && smode.at(1)=='1') cadence = true; if (smode.length()>1 && smode.at(2)=='1') altitude = true; if (smode.length()>2 && smode.at(3)=='1') power = true; if (smode.length()>3 && smode.at(4)=='1') balance = true; //if (smode.length()>4 && smode.at(5)=='1') pedaling_index = true; // // It appears that the Polar CS600 exports its data alays in metric when downloaded from the // polar software even when English units are displayed on the unit.. It also never sets // this bit low in the .hrm file. This will have to get changed if other software downloads // this differently // if (smode.length()>6 && smode.at(7)=='1') metric = false; } else if (line.contains("Interval=")) { recInterval = line.remove(0,9).toInt(); if (recInterval==238) { /* This R-R data */ rideFile->setRecIntSecs(1); } else { rideFile->setRecIntSecs(recInterval); } } else if (line.contains("Date=")) { line.remove(0,5); date= QDate(line.left(4).toInt(), line.mid(4,2).toInt(), line.mid(6,2).toInt()); } else if (line.contains("StartTime=")) { line.remove(0,10); QDateTime datetime(date, QTime(line.left(2).toInt(), line.mid(3,2).toInt(), line.mid(6,2).toInt())); rideFile->setStartTime(datetime); } else if (line.contains("StartDelay=")) { StartDelay = line.remove(0,11).toInt(); if (recInterval==238) { seconds = StartDelay/1000.0; } else { seconds = recInterval; } } } else if (section == "[Note]") { note.append(line); } else if (section == "[IntTimes]") { double int_seconds = line.left(2).toInt()*60*60+line.mid(3,2).toInt()*60+line.mid(6,3).toFloat(); intervals.append(int_seconds); if (lines.size()==1) { is.readLine(); is.readLine(); if (version>1.05) { is.readLine(); is.readLine(); } } else { li+=2; if (version>1.05) li+=2; } } else if (section == "[HRData]") { double nm=0,kph=0,watts=0,km=0,cad=0,hr=0,alt=0,hrm=0; double lrbalance=RideFile::NA; int i=0; hrm = line.section('\t', i, i).toDouble(); i++; if (speed) { kph = line.section('\t', i, i).toDouble()/10; distance += kph/60/60*recInterval; km = distance; i++; } if (cadence) { cad = line.section('\t', i, i).toDouble(); i++; } if (altitude) { alt = line.section('\t', i, i).toDouble(); i++; } if (power) { watts = line.section('\t', i, i).toDouble(); i++; } if (balance) { // Power LRB + PI: The value contains : // - Left Right Balance (LRB) and // - Pedaling Index (PI) // // in the following formula: // value = PI * 256 + LRB PI bits 15-8 LRB bits 7-0 // LRB is the value of left foot // for example if LRB = 45, actual balance is L45 - 55R. // PI values are percentages from 0 to 100. // For example value 12857 (= 40 * 256 + 47) // means: PI = 40 and LRB = 47 => L47 - 53R lrbalance = line.section('\t', i, i).toInt() & 0xff; i++; } if (next_interval < seconds) { interval = intervals.indexOf(next_interval); if (intervals.count()>interval+1){ interval++; next_interval = intervals.at(interval); } } if (!metric) { km *= KM_PER_MILE; kph *= KM_PER_MILE; alt *= METERS_PER_FOOT; } if (recInterval==238){ hr = 60000.0/hrm; } else { hr = hrm; } if (haveGPX && gpxresult && (igpx<ngpx)) { p = gpxresult->dataPoints()[igpx]; // Use previous value if GPS is momentarely // lost. Should have option for interpolating. if (p->lat!=0.0 && p->lon!=0.0) { lat = p->lat; lon = p->lon; // Must check if current HRM speed is zero while // we have GPX speed if (kph==0.0 && p->kph>1.0) { kph = p->kph; distance += kph/60/60*recInterval; km = distance; } } if (seconds>=p->secs) igpx += 1; } rideFile->appendPoint(seconds, cad, hr, km, kph, nm, watts, alt, lon, lat, 0.0, 0.0, RideFile::NA, lrbalance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, interval); // fprintf(stderr, " %f, %f, %f, %f, %f, %f, %f, %d\n", seconds, cad, hr, km, kph, nm, watts, alt, interval); if (recInterval==238) { seconds += hrm / 1000.0; } else { seconds += recInterval; } } ++lineno; } } rideFile->setTag("Notes", note); QRegExp rideTime("^.*/(\\d\\d\\d\\d)_(\\d\\d)_(\\d\\d)_" "(\\d\\d)_(\\d\\d)_(\\d\\d)\\.hrm$"); if (rideTime.indexIn(file.fileName()) >= 0) { QDateTime datetime(QDate(rideTime.cap(1).toInt(), rideTime.cap(2).toInt(), rideTime.cap(3).toInt()), QTime(rideTime.cap(4).toInt(), rideTime.cap(5).toInt(), rideTime.cap(6).toInt())); rideFile->setStartTime(datetime); } file.close(); return rideFile; }