diff --git a/src/CpintPlot.cpp b/src/CpintPlot.cpp index 4e145ab71..29dbe7b86 100644 --- a/src/CpintPlot.cpp +++ b/src/CpintPlot.cpp @@ -171,6 +171,10 @@ CpintPlot::setSeries(RideFile::SeriesType x) setAxisTitle(yLeft, tr("Normalized Power (watts)")); break; + case RideFile::aPower: + setAxisTitle(yLeft, tr("Altitude Power (watts)")); + break; + case RideFile::xPower: setAxisTitle(yLeft, tr("Skiba xPower (watts)")); break; @@ -355,7 +359,7 @@ CpintPlot::plot_CP_curve(CpintPlot *thisPlot, // the plot we're currently di else curve_title.sprintf("CP=%.0f w; W'=%.0f kJ", cp, cp * tau * 60.0 / 1000.0); #endif - if (series == RideFile::watts || series == RideFile::wattsKg) curveTitle.setLabel(QwtText(curve_title, QwtText::PlainText)); + if (series == RideFile::watts || series == RideFile::aPower || series == RideFile::wattsKg) curveTitle.setLabel(QwtText(curve_title, QwtText::PlainText)); if (series == RideFile::wattsKg) curveTitle.setYValue(0.6); @@ -556,7 +560,7 @@ CpintPlot::calculate(RideItem *rideItem) // // PLOT MODEL CURVE (DERIVED) // - if (series == RideFile::xPower || series == RideFile::NP || series == RideFile::watts || series == RideFile::wattsKg || series == RideFile::none) { + if (series == RideFile::aPower || series == RideFile::xPower || series == RideFile::NP || series == RideFile::watts || series == RideFile::wattsKg || series == RideFile::none) { if (bests->meanMaxArray(series).size() > 1) { // calculate CP model from all-time best data @@ -567,7 +571,7 @@ CpintPlot::calculate(RideItem *rideItem) // // CP curve only relevant for Energy or Watts (?) // - if (series == RideFile::watts || series == RideFile::wattsKg || series == RideFile::none) { + if (series == RideFile::aPower || series == RideFile::watts || series == RideFile::wattsKg || series == RideFile::none) { if (!CPCurve) plot_CP_curve(this, cp, tau, t0); else { // make sure color reflects latest config diff --git a/src/CriticalPowerWindow.cpp b/src/CriticalPowerWindow.cpp index b8bbd4099..5daccc841 100644 --- a/src/CriticalPowerWindow.cpp +++ b/src/CriticalPowerWindow.cpp @@ -361,6 +361,7 @@ CriticalPowerWindow::updateCpint(double minutes) break; default: + case RideFile::aPower: case RideFile::watts: units = "Watts"; break; @@ -436,6 +437,7 @@ void CriticalPowerWindow::addSeries() << RideFile::cad << RideFile::nm << RideFile::vam + << RideFile::aPower << RideFile::none; // this shows energy (hack) foreach (RideFile::SeriesType x, seriesList) { diff --git a/src/DataFilter.cpp b/src/DataFilter.cpp index 063386d3f..f5aa1c2b6 100644 --- a/src/DataFilter.cpp +++ b/src/DataFilter.cpp @@ -43,6 +43,7 @@ Leaf *root; // root node for parsed statement static RideFile::SeriesType nameToSeries(QString name) { if (!name.compare("power", Qt::CaseInsensitive)) return RideFile::watts; + if (!name.compare("apower", Qt::CaseInsensitive)) return RideFile::aPower; if (!name.compare("cadence", Qt::CaseInsensitive)) return RideFile::cad; if (!name.compare("hr", Qt::CaseInsensitive)) return RideFile::hr; if (!name.compare("speed", Qt::CaseInsensitive)) return RideFile::kph; @@ -145,7 +146,7 @@ void Leaf::validateFilter(DataFilter *df, Leaf *leaf) case Leaf::Function : { // is the symbol valid? - QRegExp bestValidSymbols("^(power|hr|cadence|speed|torque|vam|xpower|np|wpk)$", Qt::CaseInsensitive); + QRegExp bestValidSymbols("^(apower|power|hr|cadence|speed|torque|vam|xpower|np|wpk)$", Qt::CaseInsensitive); QRegExp tizValidSymbols("^(power|hr)$", Qt::CaseInsensitive); QString symbol = *(leaf->series->lvalue.n); diff --git a/src/LTMTool.cpp b/src/LTMTool.cpp index ed6abbd1b..da2d82c33 100644 --- a/src/LTMTool.cpp +++ b/src/LTMTool.cpp @@ -956,6 +956,7 @@ EditMetricDetailDialog::EditMetricDetailDialog(Context *context, LTMTool *ltmToo seriesList << RideFile::watts << RideFile::wattsKg << RideFile::xPower + << RideFile::aPower << RideFile::NP << RideFile::hr << RideFile::kph diff --git a/src/RideFile.cpp b/src/RideFile.cpp index b0c184e92..bc8c08c8e 100644 --- a/src/RideFile.cpp +++ b/src/RideFile.cpp @@ -586,6 +586,7 @@ RideFile::isDataPresent(SeriesType series) case kph : return dataPresent.kph; break; case nm : return dataPresent.nm; break; case watts : return dataPresent.watts; break; + case aPower : return dataPresent.apower; break; case alt : return dataPresent.alt; break; case lon : return dataPresent.lon; break; case lat : return dataPresent.lat; break; diff --git a/src/RideFileCache.cpp b/src/RideFileCache.cpp index 908084186..3451c9689 100644 --- a/src/RideFileCache.cpp +++ b/src/RideFileCache.cpp @@ -48,6 +48,7 @@ RideFileCache::RideFileCache(Context *context, QString fileName, RideFile *passe npMeanMax.resize(0); vamMeanMax.resize(0); wattsKgMeanMax.resize(0); + aPowerMeanMax.resize(0); wattsDistribution.resize(0); hrDistribution.resize(0); cadDistribution.resize(0); @@ -56,6 +57,7 @@ RideFileCache::RideFileCache(Context *context, QString fileName, RideFile *passe xPowerDistribution.resize(0); npDistribution.resize(0); wattsKgDistribution.resize(0); + aPowerDistribution.resize(0); // time in zone are fixed to 10 zone max wattsTimeInZone.resize(10); @@ -137,6 +139,7 @@ RideFileCache::decimalsFor(RideFile::SeriesType series) case RideFile::interval : return 0; break; case RideFile::vam : return 0; break; case RideFile::wattsKg : return 2; break; + case RideFile::aPower : return 0; break; case RideFile::lrbalance : return 1; break; case RideFile::none : break; } @@ -183,6 +186,10 @@ RideFileCache::meanMaxDates(RideFile::SeriesType series) return vamMeanMaxDate; break; + case RideFile::aPower: + return aPowerMeanMaxDate; + break; + case RideFile::wattsKg: return wattsKgMeanMaxDate; break; @@ -231,6 +238,10 @@ RideFileCache::meanMaxArray(RideFile::SeriesType series) return vamMeanMaxDouble; break; + case RideFile::aPower: + return aPowerMeanMaxDouble; + break; + case RideFile::wattsKg: return wattsKgMeanMaxDouble; break; @@ -267,6 +278,10 @@ RideFileCache::distributionArray(RideFile::SeriesType series) return kphDistributionDouble; break; + case RideFile::aPower: + return aPowerDistributionDouble; + break; + case RideFile::wattsKg: return wattsKgDistributionDouble; break; @@ -353,6 +368,7 @@ void RideFileCache::RideFileCache::compute() MeanMaxComputer thread7(ride, npMeanMax, RideFile::NP); thread7.start(); MeanMaxComputer thread8(ride, vamMeanMax, RideFile::vam); thread8.start(); MeanMaxComputer thread9(ride, wattsKgMeanMax, RideFile::wattsKg); thread9.start(); + MeanMaxComputer thread10(ride, aPowerMeanMax, RideFile::aPower); thread10.start(); // all the different distributions computeDistribution(wattsDistribution, RideFile::watts); @@ -361,6 +377,7 @@ void RideFileCache::RideFileCache::compute() computeDistribution(nmDistribution, RideFile::nm); computeDistribution(kphDistribution, RideFile::kph); computeDistribution(wattsKgDistribution, RideFile::wattsKg); + computeDistribution(aPowerDistribution, RideFile::aPower); // wait for them threads thread1.wait(); @@ -372,6 +389,7 @@ void RideFileCache::RideFileCache::compute() thread7.wait(); thread8.wait(); thread9.wait(); + thread10.wait(); } //---------------------------------------------------------------------- @@ -806,7 +824,6 @@ MeanMaxComputer::run() // precision by applying a multiplier array[i] = ride_bests[i] * decimals; } - } void @@ -920,6 +937,7 @@ RideFileCache::RideFileCache(Context *context, QDate start, QDate end, bool filt npMeanMax.resize(0); vamMeanMax.resize(0); wattsKgMeanMax.resize(0); + aPowerMeanMax.resize(0); wattsDistribution.resize(0); hrDistribution.resize(0); cadDistribution.resize(0); @@ -928,6 +946,7 @@ RideFileCache::RideFileCache(Context *context, QDate start, QDate end, bool filt xPowerDistribution.resize(0); npDistribution.resize(0); wattsKgDistribution.resize(0); + aPowerDistribution.resize(0); // time in zone are fixed to 10 zone max wattsTimeInZone.resize(10); @@ -960,6 +979,7 @@ RideFileCache::RideFileCache(Context *context, QDate start, QDate end, bool filt meanMaxAggregate(npMeanMaxDouble, rideCache.npMeanMaxDouble, npMeanMaxDate, rideDate); meanMaxAggregate(vamMeanMaxDouble, rideCache.vamMeanMaxDouble, vamMeanMaxDate, rideDate); meanMaxAggregate(wattsKgMeanMaxDouble, rideCache.wattsKgMeanMaxDouble, wattsKgMeanMaxDate, rideDate); + meanMaxAggregate(aPowerMeanMaxDouble, rideCache.aPowerMeanMaxDouble, aPowerMeanMaxDate, rideDate); distAggregate(wattsDistributionDouble, rideCache.wattsDistributionDouble); distAggregate(hrDistributionDouble, rideCache.hrDistributionDouble); @@ -969,6 +989,7 @@ RideFileCache::RideFileCache(Context *context, QDate start, QDate end, bool filt distAggregate(xPowerDistributionDouble, rideCache.xPowerDistributionDouble); distAggregate(npDistributionDouble, rideCache.npDistributionDouble); distAggregate(wattsKgDistributionDouble, rideCache.wattsKgDistributionDouble); + distAggregate(aPowerDistributionDouble, rideCache.aPowerDistributionDouble); // cumulate timeinzones for (int i=0; i<10; i++) { @@ -1014,7 +1035,7 @@ RideFileCache::serialize(QDataStream *out) head.npMeanMaxCount = npMeanMax.size(); head.vamMeanMaxCount = vamMeanMax.size(); head.wattsKgMeanMaxCount = wattsKgMeanMax.size(); - + head.aPowerMeanMaxCount = aPowerMeanMax.size(); head.wattsDistCount = wattsDistribution.size(); head.xPowerDistCount = xPowerDistribution.size(); head.npDistCount = xPowerDistribution.size(); @@ -1023,6 +1044,7 @@ RideFileCache::serialize(QDataStream *out) head.nmDistrCount = nmDistribution.size(); head.kphDistCount = kphDistribution.size(); head.wattsKgDistCount = wattsKgDistribution.size(); + head.aPowerDistCount = aPowerDistribution.size(); out->writeRawData((const char *) &head, sizeof(head)); @@ -1036,6 +1058,7 @@ RideFileCache::serialize(QDataStream *out) out->writeRawData((const char *) npMeanMax.data(), sizeof(float) * npMeanMax.size()); out->writeRawData((const char *) vamMeanMax.data(), sizeof(float) * vamMeanMax.size()); out->writeRawData((const char *) wattsKgMeanMax.data(), sizeof(float) * wattsKgMeanMax.size()); + out->writeRawData((const char *) aPowerMeanMax.data(), sizeof(float) * aPowerMeanMax.size()); // write dist out->writeRawData((const char *) wattsDistribution.data(), sizeof(float) * wattsDistribution.size()); @@ -1046,6 +1069,7 @@ RideFileCache::serialize(QDataStream *out) out->writeRawData((const char *) xPowerDistribution.data(), sizeof(float) * xPowerDistribution.size()); out->writeRawData((const char *) npDistribution.data(), sizeof(float) * npDistribution.size()); out->writeRawData((const char *) wattsKgDistribution.data(), sizeof(float) * wattsKgDistribution.size()); + out->writeRawData((const char *) aPowerDistribution.data(), sizeof(float) * aPowerDistribution.size()); // time in zone out->writeRawData((const char *) wattsTimeInZone.data(), sizeof(float) * wattsTimeInZone.size()); @@ -1073,6 +1097,7 @@ RideFileCache::readCache() vamMeanMax.resize(head.vamMeanMaxCount); xPowerMeanMax.resize(head.xPowerMeanMaxCount); wattsKgMeanMax.resize(head.wattsKgMeanMaxCount); + aPowerMeanMax.resize(head.aPowerMeanMaxCount); wattsDistribution.resize(head.wattsDistCount); hrDistribution.resize(head.hrDistCount); @@ -1082,6 +1107,7 @@ RideFileCache::readCache() xPowerDistribution.resize(head.xPowerDistCount); npDistribution.resize(head.npDistCount); wattsKgDistribution.resize(head.wattsKgDistCount); + aPowerDistribution.resize(head.aPowerDistCount); // read in the arrays inFile.readRawData((char *) wattsMeanMax.data(), sizeof(float) * wattsMeanMax.size()); @@ -1093,6 +1119,7 @@ RideFileCache::readCache() inFile.readRawData((char *) npMeanMax.data(), sizeof(float) * npMeanMax.size()); inFile.readRawData((char *) vamMeanMax.data(), sizeof(float) * vamMeanMax.size()); inFile.readRawData((char *) wattsKgMeanMax.data(), sizeof(float) * wattsKgMeanMax.size()); + inFile.readRawData((char *) aPowerMeanMax.data(), sizeof(float) * aPowerMeanMax.size()); // write dist @@ -1104,6 +1131,7 @@ RideFileCache::readCache() inFile.readRawData((char *) xPowerDistribution.data(), sizeof(float) * xPowerDistribution.size()); inFile.readRawData((char *) npDistribution.data(), sizeof(float) * npDistribution.size()); inFile.readRawData((char *) wattsKgDistribution.data(), sizeof(float) * wattsKgDistribution.size()); + inFile.readRawData((char *) aPowerDistribution.data(), sizeof(float) * aPowerDistribution.size()); // time in zone inFile.readRawData((char *) wattsTimeInZone.data(), sizeof(float) * 10); @@ -1119,6 +1147,7 @@ RideFileCache::readCache() doubleArray(vamMeanMaxDouble, vamMeanMax, RideFile::vam); doubleArray(xPowerMeanMaxDouble, xPowerMeanMax, RideFile::xPower); doubleArray(wattsKgMeanMaxDouble, wattsKgMeanMax, RideFile::wattsKg); + doubleArray(aPowerMeanMaxDouble, aPowerMeanMax, RideFile::aPower); doubleArray(wattsDistributionDouble, wattsDistribution, RideFile::watts); doubleArray(hrDistributionDouble, hrDistribution, RideFile::hr); @@ -1128,6 +1157,7 @@ RideFileCache::readCache() doubleArray(xPowerDistributionDouble, xPowerDistribution, RideFile::xPower); doubleArray(npDistributionDouble, npDistribution, RideFile::NP); doubleArray(wattsKgDistributionDouble, wattsKgDistribution, RideFile::wattsKg); + doubleArray(aPowerDistributionDouble, aPowerDistribution, RideFile::aPower); cacheFile.close(); } @@ -1149,6 +1179,7 @@ static long offsetForMeanMax(RideFileCacheHeader head, RideFile::SeriesType seri long offset = 0; switch (series) { + case RideFile::aPower : offset += head.wattsKgMeanMaxCount * sizeof(float); case RideFile::wattsKg : offset += head.vamMeanMaxCount * sizeof(float); case RideFile::vam : offset += head.npMeanMaxCount * sizeof(float); case RideFile::NP : offset += head.xPowerMeanMaxCount * sizeof(float); @@ -1171,6 +1202,7 @@ static long offsetForTiz(RideFileCacheHeader head, RideFile::SeriesType series) long offset = 0; // skip past the mean max arrays + offset += head.aPowerMeanMaxCount * sizeof(float); offset += head.wattsKgMeanMaxCount * sizeof(float); offset += head.vamMeanMaxCount * sizeof(float); offset += head.npMeanMaxCount * sizeof(float); @@ -1190,6 +1222,7 @@ static long offsetForTiz(RideFileCacheHeader head, RideFile::SeriesType series) offset += head.xPowerDistCount * sizeof(float); offset += head.npDistCount * sizeof(float); offset += head.wattsKgDistCount * sizeof(float); + offset += head.aPowerDistCount * sizeof(float); // Watts then HR if (series == RideFile::hr) offset += 10 * sizeof(float); @@ -1202,6 +1235,7 @@ static long offsetForTiz(RideFileCacheHeader head, RideFile::SeriesType series) static long countForMeanMax(RideFileCacheHeader head, RideFile::SeriesType series) { switch (series) { + case RideFile::aPower : return head.aPowerMeanMaxCount; case RideFile::wattsKg : return head.wattsKgMeanMaxCount; case RideFile::vam : return head.vamMeanMaxCount; case RideFile::NP : return head.npMeanMaxCount; diff --git a/src/RideFileCache.h b/src/RideFileCache.h index 17a939201..dcaee18da 100644 --- a/src/RideFileCache.h +++ b/src/RideFileCache.h @@ -40,7 +40,7 @@ typedef double data_t; // arrays when plotting CP curves and histograms. It is precoputed // to save time and cached in a file .cpx // -static const unsigned int RideFileCacheVersion = 8; +static const unsigned int RideFileCacheVersion = 9; // revision history: // version date description // 1 29-Apr-11 Initial - header, mean-max & distribution data blocks @@ -51,6 +51,7 @@ static const unsigned int RideFileCacheVersion = 8; // 6 27-Jun-12 Added W/kg mean maximals and distribution // 7 03-Dec-12 Fixed W/kg calculations! // 8 13-Feb-13 Fixed VAM calculations +// 9 06-Nov-13 Added aPower // The cache file (.cpx) has a binary format: // 1 x Header data - describing the version and contents of the cache @@ -75,6 +76,7 @@ struct RideFileCacheHeader { npMeanMaxCount, vamMeanMaxCount, wattsKgMeanMaxCount, + aPowerMeanMaxCount, wattsDistCount, hrDistCount, cadDistCount, @@ -82,7 +84,8 @@ struct RideFileCacheHeader { kphDistCount, xPowerDistCount, npDistCount, - wattsKgDistCount; + wattsKgDistCount, + aPowerDistCount; int LTHR, // used to calculate Time in Zone (TIZ) CP; // used to calculate Time in Zone (TIZ) @@ -190,6 +193,7 @@ class RideFileCache QVector npMeanMax; // RideFile::kph QVector vamMeanMax; // RideFile::vam QVector wattsKgMeanMax; // watts/kg + QVector aPowerMeanMax; // RideFile::aPower QVector wattsMeanMaxDouble; // RideFile::watts QVector hrMeanMaxDouble; // RideFile::hr @@ -200,6 +204,7 @@ class RideFileCache QVector npMeanMaxDouble; // RideFile::kph QVector vamMeanMaxDouble; // RideFile::kph QVector wattsKgMeanMaxDouble; // watts/kg + QVector aPowerMeanMaxDouble; // RideFile::aPower QVector wattsMeanMaxDate; // RideFile::watts QVector hrMeanMaxDate; // RideFile::hr @@ -210,6 +215,7 @@ class RideFileCache QVector npMeanMaxDate; // RideFile::kph QVector vamMeanMaxDate; // RideFile::vam QVector wattsKgMeanMaxDate; // watts/kg + QVector aPowerMeanMaxDate; // RideFile::aPower // // SAMPLE DISTRIBUTION @@ -227,6 +233,7 @@ class RideFileCache QVector xPowerDistribution; // RideFile::kph QVector npDistribution; // RideFile::kph QVector wattsKgDistribution; // RideFile::wattsKg + QVector aPowerDistribution; // RideFile::aPower QVector wattsDistributionDouble; // RideFile::watts QVector hrDistributionDouble; // RideFile::hr @@ -236,6 +243,7 @@ class RideFileCache QVector xPowerDistributionDouble; // RideFile::kph QVector npDistributionDouble; // RideFile::kph QVector wattsKgDistributionDouble; // RideFile::wattsKg + QVector aPowerDistributionDouble; // RideFile::aPower QVector wattsTimeInZone; // time in zone in seconds