Histogram plot by zone for seasons

The recent update to plot histograms for seasons or other
date ranges did not support displaying by zone since the
cache did not contain zoned data. This patch fixes that
with an update to RideFileCache to pre-compute and to the
PowerHist class to retrieve and plot.

There are some minor issues that need to be addressed:

* Handling aggregation with different zone schemes

* Deciding which zone scheme to use for the bar labels
  when multiple differing schemes have been used within
  the date range selected.

* Showing a break down of time in zone by range i.e.
  how much time was spent at Threshold when CP was X
  as opposed to when it was Y (hint: do it like we
  currently display intervals when plotting a single
  ride).

* Refreshing the Time In Zone data in the .cpx file
  when CP/LTHR changes is not implemented.

The RideFileCache now checks the version of the cache to
determine if it needs to be refreshed -- so no need to
delete old .cpx files before running GC with this patch.
This commit is contained in:
Mark Liversedge
2011-05-03 16:26:40 +01:00
parent 6b1e72a3b8
commit 48e8b26347
3 changed files with 124 additions and 9 deletions

View File

@@ -18,6 +18,9 @@
#include "RideFileCache.h"
#include "MainWindow.h"
#include "Zones.h"
#include "HrZones.h"
#include <math.h> // for pow()
#include <QDebug>
#include <QFileInfo>
@@ -44,6 +47,10 @@ RideFileCache::RideFileCache(MainWindow *main, QString fileName, RideFile *passe
xPowerDistribution.resize(0);
npDistribution.resize(0);
// time in zone are fixed to 10 zone max
wattsTimeInZone.resize(10);
hrTimeInZone.resize(10);
// Get info for ride file and cache file
QFileInfo rideFileInfo(rideFileName);
cacheFileName = rideFileInfo.path() + "/" + rideFileInfo.baseName() + ".cpx";
@@ -51,11 +58,34 @@ RideFileCache::RideFileCache(MainWindow *main, QString fileName, RideFile *passe
// is it up-to-date?
if (cacheFileInfo.exists() && rideFileInfo.lastModified() < cacheFileInfo.lastModified() &&
cacheFileInfo.size() != 0) {
if (check == false) readCache(); // if check is false we aren't just checking
return;
cacheFileInfo.size() >= (int)sizeof(struct RideFileCacheHeader)) {
// we have a file, it is more recent than the ride file
// but is it the latest version?
RideFileCacheHeader head;
QFile cacheFile(cacheFileName);
if (cacheFile.open(QIODevice::ReadOnly) == true) {
// read the header
QDataStream inFile(&cacheFile);
inFile.readRawData((char *) &head, sizeof(head));
cacheFile.close();
// is it as recent as we are?
if (head.version == RideFileCacheVersion) {
// Are the CP/LTHR values still correct
// XXX todo
// WE'RE GOOD
if (check == false) readCache(); // if check is false we aren't just checking
return;
}
}
}
// NEED TO UPDATE!!
// not up-to-date we need to refresh from the ridefile
if (ride) {
@@ -566,6 +596,16 @@ RideFileCache::computeDistribution(QVector<unsigned long> &array, RideFile::Seri
// only bother if the data series is actually present
if (ride->isDataPresent(series) == false) return;
// get zones that apply, if any
int zoneRange = main->zones() ? main->zones()->whichRange(ride->startTime().date()) : -1;
int hrZoneRange = main->hrZones() ? main->hrZones()->whichRange(ride->startTime().date()) : -1;
if (zoneRange != -1) CP=main->zones()->getCP(zoneRange);
else CP=0;
if (hrZoneRange != -1) LTHR=main->hrZones()->getLT(hrZoneRange);
else LTHR=0;
// setup the array based upon the ride
int decimals = RideFile::decimalsFor(series) ? 1 : 0;
double min = RideFile::minimumFor(series) * pow(10, decimals);
@@ -580,6 +620,14 @@ RideFileCache::computeDistribution(QVector<unsigned long> &array, RideFile::Seri
double value = dp->value(series);
unsigned long lvalue = value * pow(10, decimals);
// watts time in zone
if (series == RideFile::watts && zoneRange != -1)
wattsTimeInZone[main->zones()->whichZone(zoneRange, dp->value(series))] += ride->recIntSecs();
// hr time in zone
if (series == RideFile::hr && hrZoneRange != -1)
hrTimeInZone[main->hrZones()->whichZone(hrZoneRange, dp->value(series))] += ride->recIntSecs();
int offset = lvalue - min;
if (offset >= 0 && offset < array.size()) array[offset] += ride->recIntSecs();
}
@@ -617,6 +665,7 @@ static void distAggregate(QVector<double> &into, QVector<double> &other)
{
if (into.size() < other.size()) into.resize(other.size());
for (int i=0; i<other.size(); i++) into[i] += other[i];
}
RideFileCache::RideFileCache(MainWindow *main, QDate start, QDate end)
@@ -640,6 +689,14 @@ RideFileCache::RideFileCache(MainWindow *main, QDate start, QDate end)
xPowerDistribution.resize(0);
npDistribution.resize(0);
// time in zone are fixed to 10 zone max
wattsTimeInZone.resize(10);
hrTimeInZone.resize(10);
// set cursor busy whilst we aggregate -- bit of feedback
// and less intrusive than a popup box
main->setCursor(Qt::WaitCursor);
// Iterate over the ride files (not the cpx files since they /might/ not
// exist, or /might/ be out of date.
foreach (QString rideFileName, RideFileFactory::instance().listRideFiles(main->home)) {
@@ -664,8 +721,17 @@ RideFileCache::RideFileCache(MainWindow *main, QDate start, QDate end)
distAggregate(kphDistributionDouble, rideCache.kphDistributionDouble);
distAggregate(xPowerDistributionDouble, rideCache.xPowerDistributionDouble);
distAggregate(npDistributionDouble, rideCache.npDistributionDouble);
// cumulate timeinzones
for (int i=0; i<10; i++) {
hrTimeInZone[i] += rideCache.hrTimeInZone[i];
wattsTimeInZone[i] += rideCache.wattsTimeInZone[i];
}
}
}
// set the cursor back to normal
main->setCursor(Qt::ArrowCursor);
}
//
@@ -678,6 +744,9 @@ RideFileCache::serialize(QDataStream *out)
// write header
head.version = RideFileCacheVersion;
head.CP = CP;
head.LTHR = LTHR;
head.wattsMeanMaxCount = wattsMeanMax.size();
head.hrMeanMaxCount = hrMeanMax.size();
head.cadMeanMaxCount = cadMeanMax.size();
@@ -712,6 +781,10 @@ RideFileCache::serialize(QDataStream *out)
out->writeRawData((const char *) kphDistribution.data(), sizeof(unsigned long) * kphDistribution.size());
out->writeRawData((const char *) xPowerDistribution.data(), sizeof(unsigned long) * xPowerDistribution.size());
out->writeRawData((const char *) npDistribution.data(), sizeof(unsigned long) * npDistribution.size());
// time in zone
out->writeRawData((const char *) wattsTimeInZone.data(), sizeof(unsigned long) * wattsTimeInZone.size());
out->writeRawData((const char *) hrTimeInZone.data(), sizeof(unsigned long) * hrTimeInZone.size());
}
void
@@ -759,6 +832,10 @@ RideFileCache::readCache()
inFile.readRawData((char *) xPowerDistribution.data(), sizeof(unsigned long) * xPowerDistribution.size());
inFile.readRawData((char *) npDistribution.data(), sizeof(unsigned long) * npDistribution.size());
// time in zone
inFile.readRawData((char *) wattsTimeInZone.data(), sizeof(unsigned long) * 10);
inFile.readRawData((char *) hrTimeInZone.data(), sizeof(unsigned long) * 10);
// setup the doubles the users use
doubleArray(wattsMeanMaxDouble, wattsMeanMax, RideFile::watts);
doubleArray(hrMeanMaxDouble, hrMeanMax, RideFile::hr);