From 79b6506004e7d6548ea424b3bd96bbe8561e856a Mon Sep 17 00:00:00 2001 From: Andy Froncioni Date: Sun, 21 Feb 2010 13:23:54 -0500 Subject: [PATCH] Added headwind to Aerolab calculation for iAero Added a headwind data field, which is available when using an iAero head unit, to dramatically improve the calculation of Chung analysis for users of more recent iAero devices. All other data files than the iAero have the headwind term set to zero when they append a point. --- src/Aerolab.cpp | 6 +++++- src/Computrainer3dpFile.cpp | 3 ++- src/CsvRideFile.cpp | 5 ++++- src/GcRideFile.cpp | 3 ++- src/ManualRideFile.cpp | 2 +- src/PolarRideFile.cpp | 2 +- src/QuarqParser.cpp | 4 ++-- src/RawRideFile.cpp | 4 ++-- src/RideFile.cpp | 5 +++-- src/RideFile.h | 14 +++++++------- src/RideSummaryWindow.cpp | 2 +- src/SplitRideDialog.cpp | 2 +- src/SrmRideFile.cpp | 2 +- src/TcxParser.cpp | 6 ++++-- src/TcxParser.h | 1 + src/WkoRideFile.cpp | 2 +- 16 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/Aerolab.cpp b/src/Aerolab.cpp index 3c4bd99ea..84c31cf06 100644 --- a/src/Aerolab.cpp +++ b/src/Aerolab.cpp @@ -158,6 +158,10 @@ Aerolab::setData(RideItem *_rideItem, bool new_zoom) { // Unpack: double power = max(0, p1->watts); double v = p1->kph/vfactor; + double headwind = v; + if( dataPresent->headwind ) { + headwind = p1->headwind/vfactor; + } double f = 0.0; double a = 0.0; d += v * dt; @@ -172,7 +176,7 @@ Aerolab::setData(RideItem *_rideItem, bool new_zoom) { } f *= eta; // adjust for drivetrain efficiency if using a crank-based meter - double s = slope( f, a, m, crr, cda, rho, v ); + double s = slope( f, a, m, crr, cda, rho, headwind ); double de = s * v * dt; e += de; diff --git a/src/Computrainer3dpFile.cpp b/src/Computrainer3dpFile.cpp index 3b0ad9bae..77855f266 100644 --- a/src/Computrainer3dpFile.cpp +++ b/src/Computrainer3dpFile.cpp @@ -237,7 +237,7 @@ RideFile *Computrainer3dpFileReader::openRideFile(QFile & file, // special case first data point rideFile->appendPoint((double) ms/1000, (double) cad, (double) hr, km, speed, 0.0, watts, - altitude, 0, 0, 0); + altitude, 0, 0, 0.0, 0); } // while loop since an interval in the .3dp file might // span more than one CT_EMIT_MS interval @@ -285,6 +285,7 @@ RideFile *Computrainer3dpFileReader::openRideFile(QFile & file, interpol_alt, 0, // lon 0, // lat + 0.0, // headwind 0); // reset averaging sums diff --git a/src/CsvRideFile.cpp b/src/CsvRideFile.cpp index 26922176c..03c12c80e 100644 --- a/src/CsvRideFile.cpp +++ b/src/CsvRideFile.cpp @@ -142,6 +142,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const else if (lineno > unitsHeader) { double minutes,nm,kph,watts,km,cad,alt,hr,dfpm; double lat = 0.0, lon = 0.0; + double headwind = 0.0; int interval; int pause; if (!ergomo && !iBike) { @@ -175,6 +176,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const if( iBikeVersion >= 11 && ( dfpm > 0.0 || dfpmExists ) ) { dfpmExists = true; watts = dfpm; + headwind = line.section(',', 1, 1).toDouble(); } else { watts = line.section(',', 2, 2).toDouble(); @@ -195,6 +197,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const km *= KM_PER_MILE; kph *= KM_PER_MILE; alt *= METERS_PER_FOOT; + headwind *= KM_PER_MILE; } } else { @@ -234,7 +237,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const watts = 0; rideFile->appendPoint(minutes * 60.0, cad, hr, km, - kph, nm, watts, alt, lat, lon, interval); + kph, nm, watts, alt, lat, lon, headwind, interval); } ++lineno; } diff --git a/src/GcRideFile.cpp b/src/GcRideFile.cpp index cfe8ed3a2..ef5326148 100644 --- a/src/GcRideFile.cpp +++ b/src/GcRideFile.cpp @@ -89,6 +89,7 @@ GcFileReader::openRideFile(QFile &file, QStringList &errors) const for (QDomElement sample = samples.firstChildElement("sample"); !sample.isNull(); sample = sample.nextSiblingElement("sample")) { double secs, cad, hr, km, kph, nm, watts, alt, lon, lat; + double headwind = 0.0; secs = sample.attribute("secs", "0.0").toDouble(); cad = sample.attribute("cad", "0.0").toDouble(); hr = sample.attribute("hr", "0.0").toDouble(); @@ -101,7 +102,7 @@ GcFileReader::openRideFile(QFile &file, QStringList &errors) const lat = sample.attribute("lat", "0.0").toDouble(); while ((interval < intervalStops.size()) && (secs >= intervalStops[interval])) ++interval; - rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lon, lat, interval); + rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind, interval); if (!recIntSet) { rideFile->setRecIntSecs(sample.attribute("len").toDouble()); recIntSet = true; diff --git a/src/ManualRideFile.cpp b/src/ManualRideFile.cpp index adbca7100..690870801 100644 --- a/src/ManualRideFile.cpp +++ b/src/ManualRideFile.cpp @@ -125,7 +125,7 @@ RideFile *ManualFileReader::openRideFile(QFile &file, QStringList &errors) const interval = 0; rideFile->appendPoint(minutes * 60.0, cad, hr, km, - kph, nm, watts, alt, 0.0, 0.0, interval); + kph, nm, watts, alt, 0.0, 0.0, 0.0, interval); QMap bsm; bsm.insert("value", QString("%1").arg(bs)); rideFile->metricOverrides.insert("skiba_bike_score", bsm); diff --git a/src/PolarRideFile.cpp b/src/PolarRideFile.cpp index c4fce8298..1bf4a35aa 100644 --- a/src/PolarRideFile.cpp +++ b/src/PolarRideFile.cpp @@ -200,7 +200,7 @@ this differently next_interval = intervals.at(interval); } } - rideFile->appendPoint(seconds, cad, hr, km, kph, nm, watts, alt, 0.0, 0.0, interval); + rideFile->appendPoint(seconds, cad, hr, km, kph, nm, watts, alt, 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); } diff --git a/src/QuarqParser.cpp b/src/QuarqParser.cpp index 1a99c526b..c7ecd6608 100644 --- a/src/QuarqParser.cpp +++ b/src/QuarqParser.cpp @@ -52,7 +52,7 @@ QuarqParser::incrementTime( const double new_time ) while (time_diff > seconds_from_start) { rideFile->appendPoint(seconds_from_start, cad, hr, km, - kph, nm, watts, 0, 0.0, 0.0, 0); + kph, nm, watts, 0, 0.0, 0.0, 0.0, 0); seconds_from_start += SAMPLE_INTERVAL; } @@ -114,7 +114,7 @@ QuarqParser::endElement( const QString&, const QString&, const QString& qName) // flush one last data point if (qName == "Qollector") { rideFile->appendPoint(seconds_from_start, cad, hr, km, - kph, nm, watts, 0, 0.0, 0.0, 0); + kph, nm, watts, 0, 0.0, 0.0, 0.0, 0); } return TRUE; diff --git a/src/RawRideFile.cpp b/src/RawRideFile.cpp index 922092c48..a9378a7ec 100644 --- a/src/RawRideFile.cpp +++ b/src/RawRideFile.cpp @@ -70,7 +70,7 @@ time_cb(struct tm *, time_t since_epoch, void *context) double secs = since_epoch - state->start_since_epoch; state->rideFile->appendPoint(secs, 0.0, 0.0, state->last_miles * KM_PER_MILE, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, state->last_interval); + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, state->last_interval); state->last_secs = secs; } @@ -84,7 +84,7 @@ data_cb(double secs, double nm, double mph, double watts, double miles, double a ReadState *state = (ReadState*) context; state->rideFile->appendPoint(secs, cad, hr, miles * KM_PER_MILE, - mph * KM_PER_MILE, nm, watts, alt, 0.0, 0.0, interval); + mph * KM_PER_MILE, nm, watts, alt, 0.0, 0.0, 0.0, interval); state->last_secs = secs; state->last_miles = miles; state->last_interval = interval; diff --git a/src/RideFile.cpp b/src/RideFile.cpp index e65a51d95..d8ad45409 100644 --- a/src/RideFile.cpp +++ b/src/RideFile.cpp @@ -231,10 +231,10 @@ QStringList RideFileFactory::listRideFiles(const QDir &dir) const void RideFile::appendPoint(double secs, double cad, double hr, double km, double kph, double nm, double watts, double alt, - double lon, double lat, int interval) + double lon, double lat, double headwind, int interval) { dataPoints_.append(new RideFilePoint(secs, cad, hr, km, kph, - nm, watts, alt, lon, lat, interval)); + nm, watts, alt, lon, lat, headwind, interval)); dataPresent.secs |= (secs != 0); dataPresent.cad |= (cad != 0); dataPresent.hr |= (hr != 0); @@ -245,5 +245,6 @@ void RideFile::appendPoint(double secs, double cad, double hr, double km, dataPresent.alt |= (alt != 0); dataPresent.lon |= (lon != 0); dataPresent.lat |= (lat != 0); + dataPresent.headwind |= (headwind != 0); dataPresent.interval |= (interval != 0); } diff --git a/src/RideFile.h b/src/RideFile.h index f2365f32e..b35afcc1b 100644 --- a/src/RideFile.h +++ b/src/RideFile.h @@ -43,23 +43,23 @@ struct RideFilePoint { - double secs, cad, hr, km, kph, nm, watts, alt, lon, lat;; + double secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind;; int interval; RideFilePoint() : secs(0.0), cad(0.0), hr(0.0), km(0.0), kph(0.0), - nm(0.0), watts(0.0), alt(0.0), lon(0.0), lat(0.0), interval(0) {} + nm(0.0), watts(0.0), alt(0.0), lon(0.0), lat(0.0), headwind(0.0), interval(0) {} RideFilePoint(double secs, double cad, double hr, double km, double kph, - double nm, double watts, double alt, double lon, double lat, int interval) : + double nm, double watts, double alt, double lon, double lat, double headwind, int interval) : secs(secs), cad(cad), hr(hr), km(km), kph(kph), nm(nm), - watts(watts), alt(alt), lon(lon), lat(lat), interval(interval) {} + watts(watts), alt(alt), lon(lon), lat(lat), headwind(headwind), interval(interval) {} }; struct RideFileDataPresent { - bool secs, cad, hr, km, kph, nm, watts, alt, lon, lat, interval; + bool secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind, interval; // whether non-zero data of each field is present RideFileDataPresent(): secs(false), cad(false), hr(false), km(false), - kph(false), nm(false), watts(false), alt(false), lon(false), lat(false), interval(false) {} + kph(false), nm(false), watts(false), alt(false), lon(false), lat(false), headwind(false), interval(false) {} }; struct RideFileInterval @@ -106,7 +106,7 @@ class RideFile void appendPoint(double secs, double cad, double hr, double km, double kph, double nm, double watts, double alt, - double lon, double lat, int interval); + double lon, double lat, double headwind, int interval); const QList &intervals() const { return intervals_; } void addInterval(double start, double stop, const QString &name) { diff --git a/src/RideSummaryWindow.cpp b/src/RideSummaryWindow.cpp index 1f1c559f7..3b4e063be 100644 --- a/src/RideSummaryWindow.cpp +++ b/src/RideSummaryWindow.cpp @@ -184,7 +184,7 @@ RideSummaryWindow::htmlSummary() const if (p->secs >= interval.stop) break; f.appendPoint(p->secs, p->cad, p->hr, p->km, p->kph, p->nm, - p->watts, p->alt, p->lon, p->lat, 0); + p->watts, p->alt, p->lon, p->lat, p->headwind, 0); } if (f.dataPoints().size() == 0) { // Interval empty, do not compute any metrics diff --git a/src/SplitRideDialog.cpp b/src/SplitRideDialog.cpp index b93e67e69..e92e7dbc7 100644 --- a/src/SplitRideDialog.cpp +++ b/src/SplitRideDialog.cpp @@ -140,7 +140,7 @@ SplitRideDialog::CreateNewRideFile(const RideFile *ride, int nRecStart, int nRec { RideFilePoint *pPoint = ride->dataPoints().at(nItem); newRideFile->appendPoint(pPoint->secs-pointStart->secs, pPoint->cad, pPoint->hr, pPoint->km - pointStart->km, - pPoint->kph, pPoint->nm, pPoint->watts, pPoint->alt, pPoint->lon, pPoint->lat, pPoint->interval-pointStart->interval); + pPoint->kph, pPoint->nm, pPoint->watts, pPoint->alt, pPoint->lon, pPoint->lat, pPoint->headwind, pPoint->interval-pointStart->interval); } newRideFile->setDeviceType(ride->deviceType()); diff --git a/src/SrmRideFile.cpp b/src/SrmRideFile.cpp index be07f7ad8..09ec22cc3 100644 --- a/src/SrmRideFile.cpp +++ b/src/SrmRideFile.cpp @@ -202,7 +202,7 @@ RideFile *SrmFileReader::openRideFile(QFile &file, QStringList &errorStrings) co km += result->recIntSecs() * kph / 3600.0; double nm = watts / 2.0 / PI / cad * 60.0; - result->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, 0.0, 0.0, interval); + result->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, 0.0, 0.0, 0.0, interval); ++blkidx; if ((blkidx == blockhdrs[blknum].chunkcnt) && (blknum + 1 < blockcnt)) { diff --git a/src/TcxParser.cpp b/src/TcxParser.cpp index f2e9f0173..cfaaadbd1 100644 --- a/src/TcxParser.cpp +++ b/src/TcxParser.cpp @@ -132,10 +132,11 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName) // for smart recording, the delta_t will not be constant // average all the calculations based on the previous // point. + headwind = 0.0; if(rideFile->dataPoints().empty()) { // first point rideFile->appendPoint(secs, cadence, hr, distance, - speed, torque, power, alt, lon, lat, lap); + speed, torque, power, alt, lon, lat, headwind, lap); } else { // assumption that the change in ride is linear... :) @@ -153,7 +154,7 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName) if(deltaSecs == 1) { // no smart recording, just insert the data rideFile->appendPoint(secs, cadence, hr, distance, - speed, torque, power, alt, lon, lat, lap); + speed, torque, power, alt, lon, lat, headwind, lap); } else { for(int i = 1; i <= deltaSecs; i++) { @@ -176,6 +177,7 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName) prevPoint->alt + (deltaAlt * weight), lon, // lon lat, // lat + headwind, // headwind lap); } prevPoint = rideFile->dataPoints().back(); diff --git a/src/TcxParser.h b/src/TcxParser.h index 7c15b291b..d970f7ce6 100644 --- a/src/TcxParser.h +++ b/src/TcxParser.h @@ -56,6 +56,7 @@ private: double alt; double lat; double lon; + double headwind; }; #endif // _TcxParser_h diff --git a/src/WkoRideFile.cpp b/src/WkoRideFile.cpp index b53ac07da..93a0e665f 100644 --- a/src/WkoRideFile.cpp +++ b/src/WkoRideFile.cpp @@ -414,7 +414,7 @@ WKO_UCHAR *WkoParseRawData(WKO_UCHAR *fb, RideFile *rideFile, QStringList &error // !! needs to be modified to support the new alt patch rideFile->appendPoint((double)rtime/1000, cad, hr, km, - kph, nm, watts, alt, lon, lat, 0); + kph, nm, watts, alt, lon, lat, 0.0, 0); } // increment time - even for null records (perhaps especially for null