diff --git a/src/DataFilter.cpp b/src/DataFilter.cpp index a809cbb73..063386d3f 100644 --- a/src/DataFilter.cpp +++ b/src/DataFilter.cpp @@ -21,6 +21,7 @@ #include "Context.h" #include "Athlete.h" #include "RideNavigator.h" +#include "RideFileCache.h" #include #include "DataFilter_yacc.h" @@ -39,6 +40,22 @@ extern int DataFilterparse(); 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("cadence", Qt::CaseInsensitive)) return RideFile::cad; + if (!name.compare("hr", Qt::CaseInsensitive)) return RideFile::hr; + if (!name.compare("speed", Qt::CaseInsensitive)) return RideFile::kph; + if (!name.compare("torque", Qt::CaseInsensitive)) return RideFile::nm; + if (!name.compare("NP", Qt::CaseInsensitive)) return RideFile::NP; + if (!name.compare("xPower", Qt::CaseInsensitive)) return RideFile::xPower; + if (!name.compare("VAM", Qt::CaseInsensitive)) return RideFile::vam; + if (!name.compare("wpk", Qt::CaseInsensitive)) return RideFile::wattsKg; + + return RideFile::none; + +} + void Leaf::print(Leaf *leaf, int level) { qDebug()<<"LEVEL"<type) { case Leaf::String : delete leaf->lvalue.s; break; @@ -136,6 +154,9 @@ void Leaf::validateFilter(DataFilter *df, Leaf *leaf) if (leaf->function == "tiz" && !tizValidSymbols.exactMatch(symbol)) DataFiltererrors << QString("invalid data series for tiz(): %1").arg(symbol); + + // now set the series type + leaf->seriesType = nameToSeries(symbol); } break; @@ -221,9 +242,10 @@ QStringList DataFilter::parseFilter(QString query) for (int i=0; ieval(this, treeRoot, allRides.at(i)); + QString f= allRides.at(i).getFileName(); + double result = treeRoot->eval(this, treeRoot, allRides.at(i), f); if (result) { - filenames << allRides.at(i).getFileName(); + filenames << f; } } emit results(filenames); @@ -266,7 +288,7 @@ void DataFilter::configUpdate() } } -double Leaf::eval(DataFilter *df, Leaf *leaf, SummaryMetrics m) +double Leaf::eval(DataFilter *df, Leaf *leaf, SummaryMetrics m, QString f) { switch(leaf->type) { @@ -274,13 +296,13 @@ double Leaf::eval(DataFilter *df, Leaf *leaf, SummaryMetrics m) { switch (leaf->op) { case AND : - return (eval(df, leaf->lvalue.l, m) && eval(df, leaf->rvalue.l, m)); + return (eval(df, leaf->lvalue.l, m, f) && eval(df, leaf->rvalue.l, m, f)); case OR : - return (eval(df, leaf->lvalue.l, m) || eval(df, leaf->rvalue.l, m)); + return (eval(df, leaf->lvalue.l, m, f) || eval(df, leaf->rvalue.l, m, f)); default : // parenthesis - return (eval(df, leaf->lvalue.l, m)); + return (eval(df, leaf->lvalue.l, m, f)); } } break; @@ -295,7 +317,7 @@ double Leaf::eval(DataFilter *df, Leaf *leaf, SummaryMetrics m) default: case Leaf::Function : { - duration = eval(df, leaf->lvalue.l, m); // duration + duration = eval(df, leaf->lvalue.l, m, f); // duration } break; @@ -326,10 +348,15 @@ double Leaf::eval(DataFilter *df, Leaf *leaf, SummaryMetrics m) break; } - //qDebug()<<"best for series"<<*(leaf->series->lvalue.n)<function == "best") + return RideFileCache::best(df->context, f, leaf->seriesType, duration); + + if (leaf->function == "tiz") // duration is really zone number + return RideFileCache::tiz(df->context, f, leaf->seriesType, duration); + + // unknown function!? + return 0 ; } break; @@ -346,7 +373,7 @@ double Leaf::eval(DataFilter *df, Leaf *leaf, SummaryMetrics m) default: case Leaf::Function : { - lhsdouble = eval(df, leaf->lvalue.l, m); // duration + lhsdouble = eval(df, leaf->lvalue.l, m, f); // duration lhsisNumber=true; } break; @@ -391,7 +418,7 @@ double Leaf::eval(DataFilter *df, Leaf *leaf, SummaryMetrics m) default: case Leaf::Function : { - rhsdouble = eval(df, leaf->rvalue.l, m); + rhsdouble = eval(df, leaf->rvalue.l, m, f); rhsisNumber=true; } break; diff --git a/src/DataFilter.h b/src/DataFilter.h index 189b503d1..4538b23e3 100644 --- a/src/DataFilter.h +++ b/src/DataFilter.h @@ -21,6 +21,7 @@ #include #include #include +#include "RideFile.h" //for SeriesType class Context; class RideMetric; @@ -35,7 +36,7 @@ class Leaf { Leaf() : type(none),op(0),series(NULL) { } // evaluate against a SummaryMetric - double eval(DataFilter *df, Leaf *, SummaryMetrics); + double eval(DataFilter *df, Leaf *, SummaryMetrics, QString filename); // tree traversal etc void print(Leaf *, int level); // print leaf and all children @@ -54,6 +55,7 @@ class Leaf { int op; QString function; Leaf *series; // is a symbol + RideFile::SeriesType seriesType; // for ridefilecache }; class DataFilter : public QObject @@ -62,6 +64,8 @@ class DataFilter : public QObject public: DataFilter(QObject *parent, Context *context); + + Context *context; QStringList &files() { return filenames; } // used by Leaf @@ -82,7 +86,6 @@ class DataFilter : public QObject void results(QStringList); private: - Context *context; Leaf *treeRoot; QStringList errors; diff --git a/src/RideFile.h b/src/RideFile.h index c98ca529c..831a6aee9 100644 --- a/src/RideFile.h +++ b/src/RideFile.h @@ -105,7 +105,7 @@ class RideFile : public QObject // QObject to emit signals virtual ~RideFile(); // Working with DATASERIES - enum seriestype { secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind, slope, temp, interval, NP, xPower, vam, wattsKg, lrbalance, none }; + enum seriestype { secs=0, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind, slope, temp, interval, NP, xPower, vam, wattsKg, lrbalance, none }; enum specialValues { noTemp = -255 }; typedef enum seriestype SeriesType; diff --git a/src/RideFileCache.cpp b/src/RideFileCache.cpp index 36f70e42c..67e0ac75e 100644 --- a/src/RideFileCache.cpp +++ b/src/RideFileCache.cpp @@ -1140,3 +1140,151 @@ void RideFileCache::doubleArray(QVector &into, QVector &from, Rid return; } +// returns offset from end of head +static long offsetForMeanMax(RideFileCacheHeader head, RideFile::SeriesType series) +{ + long offset = 0; + + switch (series) { + case RideFile::wattsKg : offset += head.vamMeanMaxCount * sizeof(float); + case RideFile::vam : offset += head.npMeanMaxCount * sizeof(float); + case RideFile::NP : offset += head.xPowerMeanMaxCount * sizeof(float); + case RideFile::xPower : offset += head.kphMeanMaxCount * sizeof(float); + case RideFile::kph : offset += head.nmMeanMaxCount * sizeof(float); + case RideFile::nm : offset += head.cadMeanMaxCount * sizeof(float); + case RideFile::cad : offset += head.hrMeanMaxCount * sizeof(float); + case RideFile::hr : offset += head.wattsMeanMaxCount * sizeof(float); + case RideFile::watts : offset += 0; + default: + break; + } + + return offset; +} + +//offset to tiz table +static long offsetForTiz(RideFileCacheHeader head, RideFile::SeriesType series) +{ + long offset = 0; + + // skip past the mean max arrays + offset += head.wattsKgMeanMaxCount * sizeof(float); + offset += head.vamMeanMaxCount * sizeof(float); + offset += head.npMeanMaxCount * sizeof(float); + offset += head.xPowerMeanMaxCount * sizeof(float); + offset += head.kphMeanMaxCount * sizeof(float); + offset += head.nmMeanMaxCount * sizeof(float); + offset += head.cadMeanMaxCount * sizeof(float); + offset += head.hrMeanMaxCount * sizeof(float); + offset += head.wattsMeanMaxCount * sizeof(float); + + // skip past the distribution arrays + offset += head.wattsDistCount * sizeof(float); + offset += head.hrDistCount * sizeof(float); + offset += head.cadDistCount * sizeof(float); + offset += head.nmDistrCount * sizeof(float); + offset += head.kphDistCount * sizeof(float); + offset += head.xPowerDistCount * sizeof(float); + offset += head.npDistCount * sizeof(float); + offset += head.wattsKgDistCount * sizeof(float); + + // Watts then HR + if (series == RideFile::hr) offset += 10 * sizeof(float); + + return offset; +} + + +// returns offset from end of head +static long countForMeanMax(RideFileCacheHeader head, RideFile::SeriesType series) +{ + switch (series) { + case RideFile::wattsKg : return head.wattsKgMeanMaxCount; + case RideFile::vam : return head.vamMeanMaxCount; + case RideFile::NP : return head.npMeanMaxCount; + case RideFile::xPower : return head.xPowerMeanMaxCount; + case RideFile::kph : return head.kphMeanMaxCount; + case RideFile::nm : return head.nmMeanMaxCount; + case RideFile::cad : return head.cadMeanMaxCount; + case RideFile::hr : return head.hrMeanMaxCount; + case RideFile::watts : return head.wattsMeanMaxCount; + default: + break; + } + + return 0; +} +double +RideFileCache::best(Context *context, QString filename, RideFile::SeriesType series, int duration) +{ + // read the header + QFileInfo rideFileInfo(context->athlete->home.absolutePath() + "/" + filename); + QString cacheFileName(context->athlete->home.absolutePath() + "/" + rideFileInfo.baseName() + ".cpx"); + QFileInfo cacheFileInfo(cacheFileName); + + // head + RideFileCacheHeader head; + QFile cacheFile(cacheFileName); + + if (cacheFile.open(QIODevice::ReadOnly) == true) { + QDataStream inFile(&cacheFile); + inFile.readRawData((char *) &head, sizeof(head)); + + // out of date or not enough samples + if (head.version != RideFileCacheVersion || duration > countForMeanMax(head, series)) { + cacheFile.close(); + return 0; + } + + // jump to correct offset + long offset = offsetForMeanMax(head, series) + (sizeof(float) * (duration-1)); + inFile.skipRawData(offset); + + float readhere = 0; + inFile.readRawData((char*)&readhere, sizeof(float)); + cacheFile.close(); + + return readhere; // will convert to double + } + + return 0; +} + +int +RideFileCache::tiz(Context *context, QString filename, RideFile::SeriesType series, int zone) +{ + if (zone < 1 || zone > 10) return 0; + + // read the header + QFileInfo rideFileInfo(context->athlete->home.absolutePath() + "/" + filename); + QString cacheFileName(context->athlete->home.absolutePath() + "/" + rideFileInfo.baseName() + ".cpx"); + QFileInfo cacheFileInfo(cacheFileName); + + // head + RideFileCacheHeader head; + QFile cacheFile(cacheFileName); + + if (cacheFile.open(QIODevice::ReadOnly) == true) { + QDataStream inFile(&cacheFile); + inFile.readRawData((char *) &head, sizeof(head)); + + // out of date + if (head.version != RideFileCacheVersion) { + cacheFile.close(); + return 0; + } + + // jump to correct offset + long offset = offsetForTiz(head, series) + (sizeof(float) * (zone-1)); + inFile.skipRawData(offset); + + float readhere = 0; + inFile.readRawData((char*)&readhere, sizeof(float)); + cacheFile.close(); + + return readhere; // will convert to double + } + + return 0; +} + diff --git a/src/RideFileCache.h b/src/RideFileCache.h index 62632ea74..7e25405ce 100644 --- a/src/RideFileCache.h +++ b/src/RideFileCache.h @@ -129,6 +129,11 @@ class RideFileCache // not actually a copy constructor -- but we call it IN the constructor. RideFileCache(RideFileCache *other) { *this = *other; } + // get a single best or time in zone value from the cache file + // intended to be very fast (using lseek to jump direct to the value requested + static double best(Context *context, QString fileName, RideFile::SeriesType series, int duration); + static int tiz(Context *context, QString fileName, RideFile::SeriesType series, int zone); + static int decimalsFor(RideFile::SeriesType series); // get data