mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-15 00:49:55 +00:00
Read HRV into XDATA
Scan the pdd file, if it exist, for given exercise.
This commit is contained in:
@@ -25,349 +25,511 @@
|
||||
#include <assert.h>
|
||||
|
||||
static int polarFileReaderRegistered =
|
||||
RideFileFactory::instance().registerReader(
|
||||
"hrm", "Polar Precision", new PolarFileReader());
|
||||
RideFileFactory::instance().registerReader("hrm", "Polar Precision", new PolarFileReader());
|
||||
|
||||
void ScanPddFile(QFile &file, QString &hrmFile, QString &hrvFile, QString &gpxFile,
|
||||
QString &hrmFileCompare, QStringList &errors, QString &comment)
|
||||
{
|
||||
QString section = NULL;
|
||||
int lineno = 1;
|
||||
|
||||
// file.open(QFile::ReadOnly)
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
errors << ("Could not open pdd file: \""
|
||||
+ file.fileName() + "\"");
|
||||
return;
|
||||
}
|
||||
QTextStream is(&file);
|
||||
|
||||
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');
|
||||
|
||||
for (int li = 0; li < lines.size(); ++li) {
|
||||
QString line = lines[li];
|
||||
|
||||
// workaround for empty lines
|
||||
if(lines.size() == 0) {
|
||||
lineno++;
|
||||
continue;
|
||||
}
|
||||
else if (line.startsWith("[ExerciseInfo")) {
|
||||
//fprintf(stderr, "section : %s\n", line.toAscii().constData());
|
||||
lineno = 0;
|
||||
}
|
||||
else
|
||||
lineno ++;
|
||||
|
||||
if (lineno > 25 && lineno <= 27)
|
||||
{
|
||||
comment.append(line + "\n");
|
||||
}
|
||||
else if (lineno == 28)
|
||||
{
|
||||
hrmFile.replace(0, hrmFile.length(), line);
|
||||
}
|
||||
else if (lineno == 31)
|
||||
{
|
||||
gpxFile.replace(0, gpxFile.length(), line);
|
||||
}
|
||||
else if (lineno == 32)
|
||||
{
|
||||
hrvFile.replace(0, hrvFile.length(), line);
|
||||
}
|
||||
if (lineno > 32 && (hrmFile == hrmFileCompare || hrvFile == hrmFileCompare))
|
||||
{
|
||||
file.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void HrmRideFile(RideFile *rideFile, RideFile*gpxresult, bool haveGPX, XDataSeries *hrvXdata,
|
||||
QFile &file, QStringList &errors, QString ¬e)
|
||||
{
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
int igpx = 0;
|
||||
int ngpx = 0;
|
||||
double lat=0,lon=0;
|
||||
int recInterval = 1;
|
||||
|
||||
RideFilePoint *p;
|
||||
|
||||
int lineno = 1;
|
||||
|
||||
|
||||
double next_interval=0;
|
||||
double hrv_time=0;
|
||||
QList<double> intervals;
|
||||
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
errors << ("Could not open pdd file: \""
|
||||
+ file.fileName() + "\"");
|
||||
return;
|
||||
}
|
||||
QTextStream is(&file);
|
||||
QString section = NULL;
|
||||
|
||||
if (haveGPX)
|
||||
ngpx = gpxresult->dataPoints().count();
|
||||
|
||||
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);
|
||||
speed = smode.at(0) == '1';
|
||||
cadence = smode.length()>0 && smode.at(1) == '1';
|
||||
altitude = smode.length()>1 && smode.at(2) == '1';
|
||||
power = smode.length()>2 && smode.at(3) == '1';
|
||||
balance = smode.length()>3 && smode.at(4) == '1';
|
||||
// pedaling_index = smode.length()>4 && smode.at(5)=='1';
|
||||
|
||||
//
|
||||
// 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
|
||||
//
|
||||
|
||||
metric = smode.length()>6 && smode.at(7)=='1';
|
||||
|
||||
} 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){
|
||||
XDataPoint *p_hrv = new XDataPoint();
|
||||
hrv_time += hrm/1000.0;
|
||||
p_hrv->secs = hrv_time;
|
||||
p_hrv->number[0] = hrm;
|
||||
hrvXdata->datapoints.append(p_hrv);
|
||||
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();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/* Quit if file does not exist or cannot be read (use permissions instead)*/
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
errors << ("Could not open ride file: \""
|
||||
+ file.fileName() + "\"");
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
file.close();
|
||||
|
||||
return rideFile;
|
||||
}
|
||||
QString hrmFile("");
|
||||
QString hrvFile("");
|
||||
QString gpxFile("");
|
||||
|
||||
QString comment("");
|
||||
|
||||
QFile hrmfile;
|
||||
QFile hrvfile;
|
||||
QFile gpxfile;
|
||||
|
||||
RideFile *rideFile = new RideFile();
|
||||
bool haveGPX = false;
|
||||
bool haveHRV = false;
|
||||
bool haveHRM = false;
|
||||
|
||||
// Read Polar GPX file (if exist with same name as hrm file).
|
||||
RideFile *gpxresult=NULL;
|
||||
QString suffix = file.fileName();
|
||||
int n_s = suffix.length();
|
||||
/* Try to scan .pdd file if exsist */
|
||||
if (n_s > 12)
|
||||
{
|
||||
QString location = suffix.midRef(0, n_s - 12).toString();
|
||||
QString hrmFileDate = suffix.midRef(n_s - 12, 6).toString();
|
||||
QString hrmFile_orig = suffix.midRef(n_s - 12, n_s).toString();
|
||||
QFile pddfile(location + "20" + hrmFileDate + ".pdd");
|
||||
if (pddfile.exists())
|
||||
{
|
||||
/* Scan .pdd file for main hrm, R-R version and gpx. Can use
|
||||
either or both if exist */
|
||||
|
||||
ScanPddFile(pddfile, hrmFile, hrvFile, gpxFile, hrmFile_orig, errors, comment);
|
||||
|
||||
haveHRM = hrmFile.length() > 0 && hrmFile == hrmFile_orig;
|
||||
if (haveHRM)
|
||||
hrmfile.setFileName(location + hrmFile);
|
||||
|
||||
haveGPX = gpxFile.length() > 0;
|
||||
if (haveGPX)
|
||||
gpxfile.setFileName(location + gpxFile);
|
||||
|
||||
haveHRV = hrvFile.length() > 0 && haveHRM && hrmFile != hrvFile;
|
||||
if (haveHRV)
|
||||
hrvfile.setFileName(location + hrvFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Can still search for gpx */
|
||||
int dot = suffix.lastIndexOf(".");
|
||||
assert(dot >= 0);
|
||||
|
||||
gpxfile.setFileName(suffix.left(dot)+".gpx");
|
||||
haveGPX = gpxfile.exists();
|
||||
}
|
||||
|
||||
|
||||
if (haveGPX) {
|
||||
GpxFileReader reader;
|
||||
gpxresult = reader.openRideFile(gpxfile,errors,rideList);
|
||||
}
|
||||
|
||||
XDataSeries *hrvXdata = new XDataSeries();
|
||||
|
||||
hrvXdata->name = "HRV";
|
||||
hrvXdata->valuename << "R-R";
|
||||
hrvXdata->unitname << "msecs";
|
||||
if (haveHRM)
|
||||
{
|
||||
HrmRideFile(rideFile, gpxresult, haveGPX, hrvXdata, hrmfile, errors, comment);
|
||||
if (haveHRV)
|
||||
{
|
||||
// Get the HRV data
|
||||
RideFile *hrv_rideFile = new RideFile();
|
||||
HrmRideFile(hrv_rideFile, gpxresult, haveGPX, hrvXdata, hrvfile, errors, comment);
|
||||
delete hrv_rideFile;
|
||||
}
|
||||
}
|
||||
else if (hrvFile.length() > 0 && !file.fileName().contains(hrvFile))
|
||||
{
|
||||
HrmRideFile(rideFile, gpxresult, haveGPX, hrvXdata, file, errors, comment);
|
||||
}
|
||||
else
|
||||
{
|
||||
errors << ("This is a R-R file: \""
|
||||
+ file.fileName() + "\"");
|
||||
delete rideFile;
|
||||
delete hrvXdata;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (hrvXdata->datapoints.count()>0)
|
||||
{
|
||||
rideFile->addXData("HRV", hrvXdata);
|
||||
}
|
||||
else
|
||||
delete hrvXdata;
|
||||
|
||||
return rideFile;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user