PeakPowerIndex, UserChartData and Overview Fixups

.. working with user charts to plot configured CP versus the trend
   in power indexes led to some new code and a few fixups:

   * new metric PeakPowerIndex is the best PowerIndex value for the
     ride based upon power mean maximal data. It is not computed for
     intervals.

   * overview chart default config shows the PeakPowerIndex instead
     of Power Model which was blank and unimplemented.

   * the overview chart was not in the window registry, it must have
     been removed by accident in a cut and paste incident

   * the 'activity' function is userchartdata was not called, the code
     was not included (it iterates over samples, but not activities)
This commit is contained in:
Mark Liversedge
2020-04-19 10:05:05 +01:00
parent 29fcc6aafe
commit b97eca37ff
7 changed files with 98 additions and 12 deletions

View File

@@ -200,7 +200,7 @@ OverviewWindow::setConfiguration(QString config)
newCard(tr("Intensity"), 3, 0, 9, Card::METRIC, "coggan_if");
newCard(tr("Power"), 3, 1, 5, Card::METRIC, "average_power");
newCard(tr("Power Zones"), 3, 2, 11, Card::ZONE, RideFile::watts);
newCard(tr("Power Model"), 3, 3, 17);
newCard(tr("Peak Power Index"), 3, 3, 17, Card::METRIC, "peak_power_index");
// column 4
newCard(tr("Distance"), 4, 0, 9, Card::METRIC, "total_distance");

View File

@@ -153,7 +153,7 @@ UserChart::setRide(RideItem *item)
// re-create program (may be edited)
if (series.user1 != NULL) delete static_cast<UserChartData*>(series.user1);
series.user1 = new UserChartData(context, this, series.string1);
series.user1 = new UserChartData(context, this, series.string1, rangemode);
connect(static_cast<UserChartData*>(series.user1)->program, SIGNAL(annotateLabel(QStringList&)), this, SLOT(annotateLabel(QStringList&)));
// cast so we can work with it

View File

@@ -19,8 +19,9 @@
#include "RideMetric.h"
#include "UserChartData.h"
#include "DataFilter.h"
#include "Athlete.h"
UserChartData::UserChartData(Context *context, UserChart *parent, QString script) : context(context), script(script)
UserChartData::UserChartData(Context *context, UserChart *parent, QString script, bool rangemode) : context(context), script(script), rangemode(rangemode)
{
// compile the program - built in a context that can close.
program = new DataFilter(NULL, context, script);
@@ -70,13 +71,36 @@ UserChartData::compute(RideItem *item, Specification spec, DateRange dr)
}
// process samples, if there are any and a function exists
if (!spec.isEmpty(item->ride()) && fsample) {
RideFileIterator it(item->ride(), spec);
if (rangemode) {
if (factivity) {
while(it.hasNext()) {
struct RideFilePoint *point = it.next();
root->eval(rt, fsample, 0, 0, const_cast<RideItem*>(item), point, NULL, spec, dr);
FilterSet fs;
fs.addFilter(context->isfiltered, context->filters);
fs.addFilter(context->ishomefiltered, context->homeFilters);
Specification spec;
spec.setFilterSet(fs);
// loop through rides for daterange
foreach(RideItem *ride, context->athlete->rideCache->rides()) {
if (!dr.pass(ride->dateTime.date())) continue; // relies upon the daterange being passed to eval...
if (!spec.pass(ride)) continue; // relies upon the daterange being passed to eval...
root->eval(rt, factivity, 0, 0, const_cast<RideItem*>(ride), NULL, NULL, spec, dr);
}
}
} else {
if (!spec.isEmpty(item->ride()) && fsample) {
RideFileIterator it(item->ride(), spec);
while(it.hasNext()) {
struct RideFilePoint *point = it.next();
root->eval(rt, fsample, 0, 0, const_cast<RideItem*>(item), point, NULL, spec, dr);
}
}
}
// finalise computation

View File

@@ -31,7 +31,7 @@ class UserChartData : public QObject {
public:
// new program
UserChartData(Context *context, UserChart *parent, QString script);
UserChartData(Context *context, UserChart *parent, QString script, bool rangemode);
~UserChartData();
// Compute from samples
@@ -44,6 +44,7 @@ public:
// script and "compiled" program
QString script;
bool rangemode;
DataFilter *program;
Leaf *root;

View File

@@ -79,7 +79,7 @@ GcWindowRegistry* GcWindows;
void
GcWindowRegistry::initialize()
{
static GcWindowRegistry GcWindowsInit[34] = {
static GcWindowRegistry GcWindowsInit[35] = {
// name GcWinID
{ VIEW_HOME|VIEW_DIARY, tr("User Chart"),GcWindowTypes::UserTrends },
{ VIEW_HOME|VIEW_DIARY, tr("Trends"),GcWindowTypes::LTM },
@@ -90,6 +90,7 @@ GcWindowRegistry::initialize()
//{ VIEW_HOME|VIEW_DIARY, tr("Performance Manager"),GcWindowTypes::PerformanceManager },
{ VIEW_ANALYSIS, tr("User Chart "),GcWindowTypes::UserAnalysis },
{ VIEW_HOME|VIEW_DIARY, tr("User Defined"),GcWindowTypes::UserTrends },
{ VIEW_ANALYSIS, tr("Overview"),GcWindowTypes::Overview },
{ VIEW_ANALYSIS|VIEW_INTERVAL, tr("Summary"),GcWindowTypes::RideSummary },
{ VIEW_ANALYSIS, tr("Details"),GcWindowTypes::MetadataWindow },
{ VIEW_ANALYSIS, tr("Summary and Details"),GcWindowTypes::Summary },

View File

@@ -33,6 +33,7 @@
#include "Banister.h"
#include "RideMetric.h"
#include "RideFileCache.h"
#include "Athlete.h"
#include "Context.h"
#include "Settings.h"
@@ -650,4 +651,62 @@ class PowerIndex : public RideMetric {
RideMetric *clone() const { return new PowerIndex(*this); }
};
static bool countAdded = RideMetricFactory::instance().addMetric(PowerIndex());
class PeakPowerIndex : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PeakPowerIndex)
public:
PeakPowerIndex()
{
setSymbol("peak_power_index");
setInternalName("PeakPowerIndex");
setPrecision(1);
setType(Peak); // not even sure aggregation makes sense
}
void initialize() {
setName(tr("PeakPowerIndex"));
setMetricUnits(tr("%"));
setImperialUnits(tr("%"));
setDescription(tr("Peak Power Index"));
}
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &) {
// no ride or no samples or is interval (metric only valid for a ride)
if (spec.isEmpty(item->ride()) || spec.secsStart() != -1) {
setValue(RideFile::NIL);
setCount(0);
return;
}
// calculate for this interval/ride
double peakpix = 0;
if (item->ride()->areDataPresent()->watts) {
QVector<float>vector;
MeanMaxComputer thread1(item->ride(), vector, RideFile::watts);
thread1.run();
thread1.wait();
// calculate peak power index, starting from 3 mins, 0=out of bounds
for (int secs=180; secs<vector.count(); secs++) {
double pix = powerIndex(vector[secs], secs, item->isRun);
if (pix > peakpix) {
peakpix=pix;
}
}
}
// we could convert to linear work time model before
// indexing, but they cancel out so no value in doing so
setValue(peakpix);
setCount(1);
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PeakPowerIndex(*this); }
};
static bool countAdded = RideMetricFactory::instance().addMetric(PowerIndex()) &&
RideMetricFactory::instance().addMetric(PeakPowerIndex());

View File

@@ -161,7 +161,8 @@
// 151 14 May 2019 Ale Martinez Added Time Recording and use it in Time in Zone Percentage
// 152 20 May 2019 Ale Martinez Fixed Time in Zone Percentages to aggregate properly
// 153 8 Dec 2019 Mark Liversedge Regenerate after v3.5 RC2/RC2X re-issue
int DBSchemaVersion = 153;
// 154 18 Apr 2020 Mark Liversedge Added PeakPowerIndex (only for full rides)
int DBSchemaVersion = 154;
RideMetricFactory *RideMetricFactory::_instance;
QVector<QString> RideMetricFactory::noDeps;