Files
GoldenCheetah/src/Metrics/PaceTimeInZone.cpp
Ale Martinez 0fe28abe5a Add Time and Percent in Zones I-II-III metrics for HR, Power and Pace
In total 18 new metrics are added and 6 new charts using them are
included in charts.xml (existing users needs to remove their local
copy in config/charts.xml to activate them, reset doesn't work since
it retrieves the server copy which is v3.5 yet.)

Fixes #2555
[publish binaries]
2021-03-18 21:07:10 -03:00

1153 lines
36 KiB
C++

/*
* Copyright (c) 2010 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "RideMetric.h"
#include "RideItem.h"
#include "Specification.h"
#include "Context.h"
#include "Athlete.h"
#include "PaceZones.h"
#include <cmath>
#include <assert.h>
#include <QApplication>
class PaceZoneTime : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime)
int level;
double seconds;
public:
PaceZoneTime() : level(0), seconds(0.0)
{
setType(RideMetric::Total);
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setPrecision(0);
setConversion(1.0);
}
bool isTime() const { return true; }
void setLevel(int level) { this->level=level-1; } // zones start from zero not 1
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &) {
// no ride or no samples
if (spec.isEmpty(item->ride()) ||
// pace only makes sense for running or swimming
(!item->isRun && !item->isSwim)) {
setValue(RideFile::NIL);
setCount(0);
return;
}
double totalSecs = 0.0;
seconds = 0;
const PaceZones *zone = item->context->athlete->paceZones(item->isSwim);
int zoneRange = item->paceZoneRange;
// get zone ranges
if (zone && zoneRange >= 0) {
// iterate and compute
RideFileIterator it(item->ride(), spec);
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
totalSecs += item->ride()->recIntSecs();
if (zone->whichZone(zoneRange, point->kph) == level)
seconds += item->ride()->recIntSecs();
}
}
setValue(seconds);
setCount(totalSecs);
}
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime(*this); }
};
class PaceZoneTime1 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime1)
public:
PaceZoneTime1()
{
setLevel(1);
setSymbol("time_in_zone_P1");
setInternalName("P1 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P1 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 1."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime1(*this); }
};
class PaceZoneTime2 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime2)
public:
PaceZoneTime2()
{
setLevel(2);
setSymbol("time_in_zone_P2");
setInternalName("P2 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P2 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 2."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime2(*this); }
};
class PaceZoneTime3 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime3)
public:
PaceZoneTime3()
{
setLevel(3);
setSymbol("time_in_zone_P3");
setInternalName("P3 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P3 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 3."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime3(*this); }
};
class PaceZoneTime4 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime4)
public:
PaceZoneTime4()
{
setLevel(4);
setSymbol("time_in_zone_P4");
setInternalName("P4 Time in Pace Zone");
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
}
void initialize ()
{
setName(tr("P4 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 4."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime4(*this); }
};
class PaceZoneTime5 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime5)
public:
PaceZoneTime5()
{
setLevel(5);
setSymbol("time_in_zone_P5");
setInternalName("P5 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P5 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 5."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime5(*this); }
};
class PaceZoneTime6 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime6)
public:
PaceZoneTime6()
{
setLevel(6);
setSymbol("time_in_zone_P6");
setInternalName("P6 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P6 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 6."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime6(*this); }
};
class PaceZoneTime7 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime7)
public:
PaceZoneTime7()
{
setLevel(7);
setSymbol("time_in_zone_P7");
setInternalName("P7 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P7 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 7."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime7(*this); }
};
class PaceZoneTime8 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime8)
public:
PaceZoneTime8()
{
setLevel(8);
setSymbol("time_in_zone_P8");
setInternalName("P8 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P8 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 8."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime8(*this); }
};
class PaceZoneTime9 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime9)
public:
PaceZoneTime9()
{
setLevel(9);
setSymbol("time_in_zone_P9");
setInternalName("P9 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P9 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 9."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime9(*this); }
};
class PaceZoneTime10 : public PaceZoneTime {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTime10)
public:
PaceZoneTime10()
{
setLevel(10);
setSymbol("time_in_zone_P10");
setInternalName("P10 Time in Pace Zone");
}
void initialize ()
{
setName(tr("P10 Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone 10."));
}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTime10(*this); }
};
// Now for Time In Zone as a Percentage of Ride Time
class PaceZonePTime1 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime1)
public:
PaceZonePTime1()
{
setSymbol("percent_in_zone_P1");
setInternalName("P1 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P1 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 1."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P1"));
// compute
double time = deps.value("time_in_zone_P1")->count();
double inzone = deps.value("time_in_zone_P1")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime1(*this); }
};
class PaceZonePTime2 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime2)
public:
PaceZonePTime2()
{
setSymbol("percent_in_zone_P2");
setInternalName("P2 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P2 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 2."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P2"));
// compute
double time = deps.value("time_in_zone_P2")->count();
double inzone = deps.value("time_in_zone_P2")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime2(*this); }
};
class PaceZonePTime3 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime3)
public:
PaceZonePTime3()
{
setSymbol("percent_in_zone_P3");
setInternalName("P3 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P3 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 3."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P3"));
// compute
double time = deps.value("time_in_zone_P3")->count();
double inzone = deps.value("time_in_zone_P3")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime3(*this); }
};
class PaceZonePTime4 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime4)
public:
PaceZonePTime4()
{
setSymbol("percent_in_zone_P4");
setInternalName("P4 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P4 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 4."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P4"));
// compute
double time = deps.value("time_in_zone_P4")->count();
double inzone = deps.value("time_in_zone_P4")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime4(*this); }
};
class PaceZonePTime5 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime5)
public:
PaceZonePTime5()
{
setSymbol("percent_in_zone_P5");
setInternalName("P5 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P5 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 5."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P5"));
// compute
double time = deps.value("time_in_zone_P5")->count();
double inzone = deps.value("time_in_zone_P5")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime5(*this); }
};
class PaceZonePTime6 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime6)
public:
PaceZonePTime6()
{
setSymbol("percent_in_zone_P6");
setInternalName("P6 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P6 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 6."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P6"));
// compute
double time = deps.value("time_in_zone_P6")->count();
double inzone = deps.value("time_in_zone_P6")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime6(*this); }
};
class PaceZonePTime7 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime7)
public:
PaceZonePTime7()
{
setSymbol("percent_in_zone_P7");
setInternalName("P7 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P7 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 7."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P7"));
// compute
double time = deps.value("time_in_zone_P7")->count();
double inzone = deps.value("time_in_zone_P7")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime7(*this); }
};
class PaceZonePTime8 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime8)
public:
PaceZonePTime8()
{
setSymbol("percent_in_zone_P8");
setInternalName("P8 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P8 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 8."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P8"));
// compute
double time = deps.value("time_in_zone_P8")->count();
double inzone = deps.value("time_in_zone_P8")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime8(*this); }
};
class PaceZonePTime9 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime9)
public:
PaceZonePTime9()
{
setSymbol("percent_in_zone_P9");
setInternalName("P9 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P9 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 9."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P9"));
// compute
double time = deps.value("time_in_zone_P9")->count();
double inzone = deps.value("time_in_zone_P9")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime9(*this); }
};
class PaceZonePTime10 : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTime10)
public:
PaceZonePTime10()
{
setSymbol("percent_in_zone_P10");
setInternalName("P10 Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("P10 Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone 10."));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps) {
assert(deps.contains("time_in_zone_P10"));
// compute
double time = deps.value("time_in_zone_P10")->count();
double inzone = deps.value("time_in_zone_P10")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTime10(*this); }
};
// Time in Zones I, II and III
class PaceZoneTimeI : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTimeI)
double seconds;
public:
PaceZoneTimeI() : seconds(0.0)
{
setType(RideMetric::Total);
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setPrecision(0);
setConversion(1.0);
setSymbol("time_in_zone_PI");
setInternalName("PI Time in Pace Zone");
}
void initialize ()
{
setName(tr("PI Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone I - Below AeT"));
}
bool isTime() const { return true; }
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &)
{
// no ride or no samples
if (spec.isEmpty(item->ride()) ||
// pace only makes sense for running or swimming
(!item->isRun && !item->isSwim)) {
setValue(RideFile::NIL);
setCount(0);
return;
}
double totalSecs = 0.0;
seconds = 0;
const PaceZones *zone = item->context->athlete->paceZones(item->isSwim);
int zoneRange = item->paceZoneRange;
// get zone ranges
if (zone && zoneRange >= 0) {
double AeT = zone->getAeT(zoneRange);
// iterate and compute
RideFileIterator it(item->ride(), spec);
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
totalSecs += item->ride()->recIntSecs();
if (point->kph < AeT)
seconds += item->ride()->recIntSecs();
}
}
setValue(seconds);
setCount(totalSecs);
}
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTimeI(*this); }
};
class PaceZoneTimeII : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTimeII)
double seconds;
public:
PaceZoneTimeII() : seconds(0.0)
{
setType(RideMetric::Total);
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setPrecision(0);
setConversion(1.0);
setSymbol("time_in_zone_PII");
setInternalName("PII Time in Pace Zone");
}
void initialize ()
{
setName(tr("PII Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone II - Between AeT and CV"));
}
bool isTime() const { return true; }
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &)
{
// no ride or no samples
if (spec.isEmpty(item->ride()) ||
// pace only makes sense for running or swimming
(!item->isRun && !item->isSwim)) {
setValue(RideFile::NIL);
setCount(0);
return;
}
double totalSecs = 0.0;
seconds = 0;
const PaceZones *zone = item->context->athlete->paceZones(item->isSwim);
int zoneRange = item->paceZoneRange;
// get zone ranges
if (zone && zoneRange >= 0) {
double AeT = zone->getAeT(zoneRange);
double CV = zone->getCV(zoneRange);
// iterate and compute
RideFileIterator it(item->ride(), spec);
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
totalSecs += item->ride()->recIntSecs();
if (point->kph >= AeT && point->kph < CV)
seconds += item->ride()->recIntSecs();
}
}
setValue(seconds);
setCount(totalSecs);
}
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTimeII(*this); }
};
class PaceZoneTimeIII : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZoneTimeIII)
double seconds;
public:
PaceZoneTimeIII() : seconds(0.0)
{
setType(RideMetric::Total);
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setPrecision(0);
setConversion(1.0);
setSymbol("time_in_zone_PIII");
setInternalName("PIII Time in Pace Zone");
}
void initialize ()
{
setName(tr("PIII Time in Pace Zone"));
setMetricUnits(tr("seconds"));
setImperialUnits(tr("seconds"));
setDescription(tr("Time in Pace Zone III - Above CV"));
}
bool isTime() const { return true; }
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &)
{
// no ride or no samples
if (spec.isEmpty(item->ride()) ||
// pace only makes sense for running or swimming
(!item->isRun && !item->isSwim)) {
setValue(RideFile::NIL);
setCount(0);
return;
}
double totalSecs = 0.0;
seconds = 0;
const PaceZones *zone = item->context->athlete->paceZones(item->isSwim);
int zoneRange = item->paceZoneRange;
// get zone ranges
if (zone && zoneRange >= 0) {
double CV = zone->getCV(zoneRange);
// iterate and compute
RideFileIterator it(item->ride(), spec);
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
totalSecs += item->ride()->recIntSecs();
if (point->kph >= CV)
seconds += item->ride()->recIntSecs();
}
}
setValue(seconds);
setCount(totalSecs);
}
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZoneTimeIII(*this); }
};
// Now for Time In Zone as a Percentage of Ride Time
class PaceZonePTimeI : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTimeI)
public:
PaceZonePTimeI()
{
setSymbol("percent_in_zone_PI");
setInternalName("PI Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("PI Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone I - Below AeT"));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps)
{
assert(deps.contains("time_in_zone_PI"));
// compute
double time = deps.value("time_in_zone_PI")->count();
double inzone = deps.value("time_in_zone_PI")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTimeI(*this); }
};
class PaceZonePTimeII : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTimeII)
public:
PaceZonePTimeII()
{
setSymbol("percent_in_zone_PII");
setInternalName("PII Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("PII Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone II - Between AeT and CV"));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps)
{
assert(deps.contains("time_in_zone_PII"));
// compute
double time = deps.value("time_in_zone_PII")->count();
double inzone = deps.value("time_in_zone_PII")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTimeII(*this); }
};
class PaceZonePTimeIII : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(PaceZonePTimeIII)
public:
PaceZonePTimeIII()
{
setSymbol("percent_in_zone_PIII");
setInternalName("PIII Percent in Pace Zone");
setType(RideMetric::Average);
setMetricUnits("%");
setImperialUnits("%");
setPrecision(0);
setConversion(1.0);
}
void initialize ()
{
setName(tr("PIII Percent in Pace Zone"));
setDescription(tr("Percent of Time in Pace Zone III - Above CV"));
}
void compute(RideItem *, Specification, const QHash<QString,RideMetric*> &deps)
{
assert(deps.contains("time_in_zone_PIII"));
// compute
double time = deps.value("time_in_zone_PIII")->count();
double inzone = deps.value("time_in_zone_PIII")->value(true);
if (time && inzone) setValue((inzone / time) * 100.00);
else setValue(0);
setCount(time);
}
bool aggregateZero() const { return true; }
bool canAggregate() { return false; }
void aggregateWith(const RideMetric &) {}
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new PaceZonePTimeIII(*this); }
};
static bool addAllZones() {
RideMetricFactory::instance().addMetric(PaceZoneTime1());
RideMetricFactory::instance().addMetric(PaceZoneTime2());
RideMetricFactory::instance().addMetric(PaceZoneTime3());
RideMetricFactory::instance().addMetric(PaceZoneTime4());
RideMetricFactory::instance().addMetric(PaceZoneTime5());
RideMetricFactory::instance().addMetric(PaceZoneTime6());
RideMetricFactory::instance().addMetric(PaceZoneTime7());
RideMetricFactory::instance().addMetric(PaceZoneTime8());
RideMetricFactory::instance().addMetric(PaceZoneTime9());
RideMetricFactory::instance().addMetric(PaceZoneTime10());
QVector<QString> deps;
deps.append("time_in_zone_P1");
RideMetricFactory::instance().addMetric(PaceZonePTime1(), &deps);
deps.clear();
deps.append("time_in_zone_P2");
RideMetricFactory::instance().addMetric(PaceZonePTime2(), &deps);
deps.clear();
deps.append("time_in_zone_P3");
RideMetricFactory::instance().addMetric(PaceZonePTime3(), &deps);
deps.clear();
deps.append("time_in_zone_P4");
RideMetricFactory::instance().addMetric(PaceZonePTime4(), &deps);
deps.clear();
deps.append("time_in_zone_P5");
RideMetricFactory::instance().addMetric(PaceZonePTime5(), &deps);
deps.clear();
deps.append("time_in_zone_P6");
RideMetricFactory::instance().addMetric(PaceZonePTime6(), &deps);
deps.clear();
deps.append("time_in_zone_P7");
RideMetricFactory::instance().addMetric(PaceZonePTime7(), &deps);
deps.clear();
deps.append("time_in_zone_P8");
RideMetricFactory::instance().addMetric(PaceZonePTime8(), &deps);
deps.clear();
deps.append("time_in_zone_P9");
RideMetricFactory::instance().addMetric(PaceZonePTime9(), &deps);
deps.clear();
deps.append("time_in_zone_P10");
RideMetricFactory::instance().addMetric(PaceZonePTime10(), &deps);
RideMetricFactory::instance().addMetric(PaceZoneTimeI());
RideMetricFactory::instance().addMetric(PaceZoneTimeII());
RideMetricFactory::instance().addMetric(PaceZoneTimeIII());
deps.clear();
deps.append("time_in_zone_PI");
RideMetricFactory::instance().addMetric(PaceZonePTimeI(), &deps);
deps.clear();
deps.append("time_in_zone_PII");
RideMetricFactory::instance().addMetric(PaceZonePTimeII(), &deps);
deps.clear();
deps.append("time_in_zone_PIII");
RideMetricFactory::instance().addMetric(PaceZonePTimeIII(), &deps);
return true;
}
static bool allZonesAdded = addAllZones();