From b97eca37ff3e7ad9b38389749b69f16db6c5457e Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Sun, 19 Apr 2020 10:05:05 +0100 Subject: [PATCH] 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) --- src/Charts/OverviewWindow.cpp | 2 +- src/Charts/UserChart.cpp | 2 +- src/Charts/UserChartData.cpp | 36 +++++++++++++++++---- src/Charts/UserChartData.h | 3 +- src/Gui/GcWindowRegistry.cpp | 3 +- src/Metrics/Banister.cpp | 61 ++++++++++++++++++++++++++++++++++- src/Metrics/RideMetric.cpp | 3 +- 7 files changed, 98 insertions(+), 12 deletions(-) diff --git a/src/Charts/OverviewWindow.cpp b/src/Charts/OverviewWindow.cpp index 78b1f3636..8a7ec1d6e 100644 --- a/src/Charts/OverviewWindow.cpp +++ b/src/Charts/OverviewWindow.cpp @@ -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"); diff --git a/src/Charts/UserChart.cpp b/src/Charts/UserChart.cpp index 082408940..2aa959bb6 100644 --- a/src/Charts/UserChart.cpp +++ b/src/Charts/UserChart.cpp @@ -153,7 +153,7 @@ UserChart::setRide(RideItem *item) // re-create program (may be edited) if (series.user1 != NULL) delete static_cast(series.user1); - series.user1 = new UserChartData(context, this, series.string1); + series.user1 = new UserChartData(context, this, series.string1, rangemode); connect(static_cast(series.user1)->program, SIGNAL(annotateLabel(QStringList&)), this, SLOT(annotateLabel(QStringList&))); // cast so we can work with it diff --git a/src/Charts/UserChartData.cpp b/src/Charts/UserChartData.cpp index 0299341a6..c1fbfa71b 100644 --- a/src/Charts/UserChartData.cpp +++ b/src/Charts/UserChartData.cpp @@ -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(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(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(item), point, NULL, spec, dr); + } + } + } // finalise computation diff --git a/src/Charts/UserChartData.h b/src/Charts/UserChartData.h index 5417de762..0963b2f8b 100644 --- a/src/Charts/UserChartData.h +++ b/src/Charts/UserChartData.h @@ -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; diff --git a/src/Gui/GcWindowRegistry.cpp b/src/Gui/GcWindowRegistry.cpp index b08ea4a9e..9af58eef3 100644 --- a/src/Gui/GcWindowRegistry.cpp +++ b/src/Gui/GcWindowRegistry.cpp @@ -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 }, diff --git a/src/Metrics/Banister.cpp b/src/Metrics/Banister.cpp index ddf784be5..1df3764e9 100644 --- a/src/Metrics/Banister.cpp +++ b/src/Metrics/Banister.cpp @@ -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 &) { + + // 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) { + + QVectorvector; + 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; secsisRun); + 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()); diff --git a/src/Metrics/RideMetric.cpp b/src/Metrics/RideMetric.cpp index cef930ecc..9c2592ebc 100644 --- a/src/Metrics/RideMetric.cpp +++ b/src/Metrics/RideMetric.cpp @@ -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 RideMetricFactory::noDeps;