Add VAM to CP curve

Very basic start, this will now let you plot
VAM on the CP curve. VAM is a measure of climbing
speed and for comparative purposes should be
normalised to the slope climbed.

In this first pass of implementation the VAM metric
is not normalised in any way. It merely represents
the climbing rate, in meters per hour, that was
sustained over each time interval from 5mins to the
ride duration.

If the ride is undulating then only ascension is
included, any time on the flat or descending is
included but meters climbed will be zero. This is
akin to the way we handle power where we include time
when freewheeling.

More sophistication is needed, especially normalising
the value to a common gradient (e.g. 10%). But this
will prove challenging when VAM is comprised of
undulating elements (i.e. gradient is cumulatively
zero, but could contain segments with steep parts).

It may be more appropriate to only measure VAM for
sustained climbing i.e. ignore ride sections when
descending or on the flat.

More thought needed.

Fixes #414.
This commit is contained in:
Mark Liversedge
2011-08-18 19:15:20 +01:00
parent 9a523ee07c
commit dcf78eaf3a
6 changed files with 83 additions and 6 deletions

View File

@@ -39,6 +39,7 @@ RideFileCache::RideFileCache(MainWindow *main, QString fileName, RideFile *passe
kphMeanMax.resize(0);
xPowerMeanMax.resize(0);
npMeanMax.resize(0);
vamMeanMax.resize(0);
wattsDistribution.resize(0);
hrDistribution.resize(0);
cadDistribution.resize(0);
@@ -143,6 +144,10 @@ RideFileCache::meanMaxDates(RideFile::SeriesType series)
return npMeanMaxDate;
break;
case RideFile::vam:
return vamMeanMaxDate;
break;
default:
//? dunno give em power anyway
return wattsMeanMaxDate;
@@ -183,6 +188,10 @@ RideFileCache::meanMaxArray(RideFile::SeriesType series)
return npMeanMaxDouble;
break;
case RideFile::vam:
return vamMeanMaxDouble;
break;
default:
//? dunno give em power anyway
return wattsMeanMaxDouble;
@@ -283,6 +292,7 @@ void RideFileCache::RideFileCache::compute()
MeanMaxComputer thread5(ride, kphMeanMax, RideFile::kph); thread5.start();
MeanMaxComputer thread6(ride, xPowerMeanMax, RideFile::xPower); thread6.start();
MeanMaxComputer thread7(ride, npMeanMax, RideFile::NP); thread7.start();
MeanMaxComputer thread8(ride, vamMeanMax, RideFile::vam); thread8.start();
// all the different distributions
computeDistribution(wattsDistribution, RideFile::watts);
@@ -299,6 +309,7 @@ void RideFileCache::RideFileCache::compute()
thread5.wait();
thread6.wait();
thread7.wait();
thread8.wait();
}
//----------------------------------------------------------------------
@@ -525,6 +536,8 @@ MeanMaxComputer::run()
RideFile::SeriesType baseSeries = (series == RideFile::xPower || series == RideFile::NP) ?
RideFile::watts : series;
if (series == RideFile::vam) baseSeries = RideFile::alt;
// only bother if the data series is actually present
if (ride->isDataPresent(baseSeries) == false) return;
@@ -569,6 +582,30 @@ MeanMaxComputer::run()
// than 2 days, even if you are doing RAAM
if (total_secs > 2*24*60*60) return;
//
// Pre-process the data for NP, xPower and VAM
//
// VAM - adjust to Vertical Ascent per Hour
if (series == RideFile::vam) {
double lastAlt=0;
for (int i=0; i<data.points.size(); i++) {
// handle drops gracefully (and first sample too)
// if you manage to rise >5m in a second thats a data error too!
if (!lastAlt || (data.points[i].value - lastAlt) > 5) lastAlt=data.points[i].value;
// NOTE: It is 360 not 3600 because Altitude is factored for decimal places
// since it is the base data series, but we are calculating VAM
double vam = (((data.points[i].value - lastAlt) * 360)/ride->recIntSecs());
if (vam < 0) vam = 0;
lastAlt = data.points[i].value;
data.points[i].value = vam;
}
}
// NP - rolling 30s avg ^ 4
if (series == RideFile::NP) {
@@ -665,6 +702,7 @@ MeanMaxComputer::run()
// accuracy/granularity can change in here in the
// future if some fancy new algorithm arrives
//
double last = 0;
array.resize(ride_bests.count());
for (int i=ride_bests.size()-1; i; i--) {
@@ -769,6 +807,7 @@ RideFileCache::RideFileCache(MainWindow *main, QDate start, QDate end)
kphMeanMax.resize(0);
xPowerMeanMax.resize(0);
npMeanMax.resize(0);
vamMeanMax.resize(0);
wattsDistribution.resize(0);
hrDistribution.resize(0);
cadDistribution.resize(0);
@@ -801,6 +840,7 @@ RideFileCache::RideFileCache(MainWindow *main, QDate start, QDate end)
meanMaxAggregate(kphMeanMaxDouble, rideCache.kphMeanMaxDouble, kphMeanMaxDate, rideDate);
meanMaxAggregate(xPowerMeanMaxDouble, rideCache.xPowerMeanMaxDouble, xPowerMeanMaxDate, rideDate);
meanMaxAggregate(npMeanMaxDouble, rideCache.npMeanMaxDouble, npMeanMaxDate, rideDate);
meanMaxAggregate(vamMeanMaxDouble, rideCache.vamMeanMaxDouble, vamMeanMaxDate, rideDate);
distAggregate(wattsDistributionDouble, rideCache.wattsDistributionDouble);
distAggregate(hrDistributionDouble, rideCache.hrDistributionDouble);
@@ -842,6 +882,7 @@ RideFileCache::serialize(QDataStream *out)
head.kphMeanMaxCount = kphMeanMax.size();
head.xPowerMeanMaxCount = xPowerMeanMax.size();
head.npMeanMaxCount = npMeanMax.size();
head.vamMeanMaxCount = vamMeanMax.size();
head.wattsDistCount = wattsDistribution.size();
head.xPowerDistCount = xPowerDistribution.size();
@@ -860,6 +901,7 @@ RideFileCache::serialize(QDataStream *out)
out->writeRawData((const char *) kphMeanMax.data(), sizeof(float) * kphMeanMax.size());
out->writeRawData((const char *) xPowerMeanMax.data(), sizeof(float) * xPowerMeanMax.size());
out->writeRawData((const char *) npMeanMax.data(), sizeof(float) * npMeanMax.size());
out->writeRawData((const char *) vamMeanMax.data(), sizeof(float) * vamMeanMax.size());
// write dist
out->writeRawData((const char *) wattsDistribution.data(), sizeof(float) * wattsDistribution.size());
@@ -893,6 +935,7 @@ RideFileCache::readCache()
nmMeanMax.resize(head.nmMeanMaxCount);
kphMeanMax.resize(head.kphMeanMaxCount);
npMeanMax.resize(head.npMeanMaxCount);
vamMeanMax.resize(head.vamMeanMaxCount);
xPowerMeanMax.resize(head.xPowerMeanMaxCount);
wattsDistribution.resize(head.wattsDistCount);
hrDistribution.resize(head.hrDistCount);
@@ -910,6 +953,7 @@ RideFileCache::readCache()
inFile.readRawData((char *) kphMeanMax.data(), sizeof(float) * kphMeanMax.size());
inFile.readRawData((char *) xPowerMeanMax.data(), sizeof(float) * xPowerMeanMax.size());
inFile.readRawData((char *) npMeanMax.data(), sizeof(float) * npMeanMax.size());
inFile.readRawData((char *) vamMeanMax.data(), sizeof(float) * vamMeanMax.size());
// write dist
@@ -932,6 +976,7 @@ RideFileCache::readCache()
doubleArray(nmMeanMaxDouble, nmMeanMax, RideFile::nm);
doubleArray(kphMeanMaxDouble, kphMeanMax, RideFile::kph);
doubleArray(npMeanMaxDouble, npMeanMax, RideFile::NP);
doubleArray(vamMeanMaxDouble, vamMeanMax, RideFile::vam);
doubleArray(xPowerMeanMaxDouble, xPowerMeanMax, RideFile::xPower);
doubleArray(wattsDistributionDouble, wattsDistribution, RideFile::watts);
doubleArray(hrDistributionDouble, hrDistribution, RideFile::hr);