Merge pull request #3060 from amtriathlon/master

Additional Running Dynamics support
This commit is contained in:
Alejandro Martinez
2019-03-29 18:17:48 -03:00
committed by GitHub
4 changed files with 217 additions and 10 deletions

View File

@@ -584,12 +584,24 @@ struct FitFileReaderState
QString getNameForExtraNative(int native_num) {
switch (native_num) {
case 40: // STANCE_TIME_PERCENT
return "STANCETIMEPERCENT"; // Stance Time Percent
case 42: // ACTIVITY_TYPE
return "ACTIVITYTYPE"; // Activity Type
case 47: // COMBINED_PEDAL_SMOOTHNES
return "COMBINEDSMOOTHNESS"; //Combined Pedal Smoothness
case 81: // BATTERY_SOC
return "BATTERYSOC";
case 83: // VERTICAL_RATIO
return "VERTICALRATIO"; // Vertical Ratio
case 85: // STEP_LENGTH
return "STEPLENGTH"; // Step Length
default:
return QString("FIELD_%1").arg(native_num);
}
@@ -598,6 +610,13 @@ struct FitFileReaderState
float getScaleForExtraNative(int native_num) {
switch (native_num) {
case 40: // STANCE_TIME_PERCENT
case 83: // VERTICAL_RATIO
return 100.0;
case 85: // STEP_LENGTH
return 10.0;
case 47: // COMBINED_PEDAL_SMOOTHNES
case 81: // BATTERY_SOC
return 2.0;
@@ -1625,8 +1644,9 @@ struct FitFileReaderState
rvert = value / 100.0f;
break;
//case 40: // GROUND CONTACT TIME PERCENT
//break;
case 40: // GROUND CONTACT TIME PERCENT
native_num = -1;
break;
case 41: // GROUND CONTACT TIME
if (!native_profile && field.deve_idx>-1)
@@ -1635,10 +1655,9 @@ struct FitFileReaderState
rcontact = value / 10.0f;
break;
//case 42: // ACTIVITY_TYPE
// // TODO We should know/test value for run
// run = true;
// break;
case 42: // ACTIVITY_TYPE
native_num = -1;
break;
case 43: // LEFT_TORQUE_EFFECTIVENESS
leftTorqueEff = value / 2.0;
@@ -1703,9 +1722,15 @@ struct FitFileReaderState
rightTopPeakPowerPhase = round(valueList.at(0) * 360.0/256);
rightBottomPeakPowerPhase = round(valueList.at(1) * 360.0/256);
break;
case 83: // VERTICAL_RATIO
native_num = -1;
break;
case 84: // Left right balance
lrbalance = value/100.0;
break;
case 85: // STEP_LENGTH
native_num = -1;
break;
case 87: // ???
break;

View File

@@ -43,7 +43,7 @@ class LeftRightBalance : public RideMetric {
setImperialUnits(tr("%"));
setType(RideMetric::Average);
setPrecision(1);
setDescription(tr("Left/Right Balance shows the proportion of power coming from each pedal."));
setDescription(tr("Left/Right Balance shows the proportion of power coming from each pedal for rides and the proportion of Ground Contact Time from each leg for runs."));
}
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &) {
@@ -60,7 +60,7 @@ class LeftRightBalance : public RideMetric {
RideFileIterator it(item->ride(), spec);
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
if (point->cad && point->lrbalance > 0.0f) {
if ((point->cad || point->rcad) && point->lrbalance > 0.0f) {
total += point->lrbalance;
++count;
}

View File

@@ -157,7 +157,8 @@
// 147 06 May 2018 Ale Martinez Added PeakHr metrics and HrZone
// 148 27 Jul 2018 Ale Martinez Changed Hrv Measures to retrun 0 when no record for the date
// 149 04 Jan 2019 Mark Liversedge PowerIndex metric to score performance tests/intervals/rides vs typical athlete PD curve
int DBSchemaVersion = 149;
// 150 28 Mar 2019 Ale Martinez Additional Running Dynamics metrics
int DBSchemaVersion = 150;
RideMetricFactory *RideMetricFactory::_instance;
QVector<QString> RideMetricFactory::noDeps;

View File

@@ -21,6 +21,7 @@
#include "Context.h"
#include "Settings.h"
#include "RideItem.h"
#include "RideFile.h"
#include "LTMOutliers.h"
#include "Units.h"
#include "Zones.h"
@@ -236,7 +237,7 @@ struct AvgRunVerticalOscillation : public RideMetric {
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
if (point->rcontact > 0) {
if (point->rvert > 0) {
total += point->rvert;
++count;
}
@@ -441,3 +442,183 @@ static bool avgStrideLengthAdded =
RideMetricFactory::instance().addMetric(AvgStrideLength());
//////////////////////////////////////////////////////////////////////////////
struct AvgRunVerticalRatio : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(AvgRunVerticalRatio)
double total, count;
public:
AvgRunVerticalRatio()
{
setSymbol("average_run_vert_ratio");
setInternalName("Average Vertical Ratio");
}
void initialize() {
setName(tr("Average Vertical Ratio"));
setMetricUnits(tr("%"));
setImperialUnits(tr("%"));
setPrecision(1);
setType(RideMetric::Average);
setDescription(tr("Average Vertical Ratio (%): Vertical Oscillation / Step Length"));
}
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &) {
// no ride or no samples
if (spec.isEmpty(item->ride())) {
setValue(RideFile::NIL);
setCount(0);
return;
}
total = count = 0;
int idx = 0;
RideFileIterator it(item->ride(), spec);
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
double vert_ratio = item->ride()->xdataValue(point, idx, "EXTRA", "VERTICALRATIO", RideFile::SPARSE);
if (point->rcad > 0 && vert_ratio > 0) {
total += vert_ratio;
++count;
}
}
setValue(count > 0 ? total / count : count);
setCount(count);
}
bool isRelevantForRide(const RideItem *ride) const { return (ride->present.contains("R") && ride->isRun); }
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new AvgRunVerticalRatio(*this); }
};
static bool avgRunVerticalRatio =
RideMetricFactory::instance().addMetric(AvgRunVerticalRatio());
//////////////////////////////////////////////////////////////////////////////
struct AvgRunStanceTimePercent : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(AvgRunStanceTimePercent)
double total, count;
public:
AvgRunStanceTimePercent()
{
setSymbol("average_run_stance_time_percent");
setInternalName("Average Stance Time Percent");
}
void initialize() {
setName(tr("Average Stance Time Percent"));
setMetricUnits(tr("%"));
setImperialUnits(tr("%"));
setPrecision(1);
setType(RideMetric::Average);
setDescription(tr("Average Stance Time Percent (%): Ground Contact Time / Step Time"));
}
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &) {
// no ride or no samples
if (spec.isEmpty(item->ride())) {
setValue(RideFile::NIL);
setCount(0);
return;
}
total = count = 0;
int idx = 0;
RideFileIterator it(item->ride(), spec);
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
double stance_time_pct = item->ride()->xdataValue(point, idx, "EXTRA", "STANCETIMEPERCENT", RideFile::SPARSE);
if (point->rcad > 0 && stance_time_pct > 0) {
total += stance_time_pct;
++count;
}
}
setValue(count > 0 ? total / count : count);
setCount(count);
}
bool isRelevantForRide(const RideItem *ride) const { return (ride->present.contains("R") && ride->isRun); }
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new AvgRunStanceTimePercent(*this); }
};
static bool avgRunStanceTimePercent =
RideMetricFactory::instance().addMetric(AvgRunStanceTimePercent());
//////////////////////////////////////////////////////////////////////////////
struct AvgStepLength : public RideMetric {
Q_DECLARE_TR_FUNCTIONS(AvgStepLength)
double total, count;
public:
AvgStepLength()
{
setSymbol("average_step_length");
setInternalName("Average Step Length");
}
void initialize() {
setName(tr("Average Step Length"));
setMetricUnits(tr("mm"));
setImperialUnits(tr("mm"));
setPrecision(0);
setType(RideMetric::Average);
setDescription(tr("Average Step Length: average single step (L to R / R to L) length in mm"));
}
void compute(RideItem *item, Specification spec, const QHash<QString,RideMetric*> &) {
// no ride or no samples
if (spec.isEmpty(item->ride())) {
setValue(RideFile::NIL);
setCount(0);
return;
}
total = count = 0;
int idx = 0;
RideFileIterator it(item->ride(), spec);
while (it.hasNext()) {
struct RideFilePoint *point = it.next();
double step_length = item->ride()->xdataValue(point, idx, "EXTRA", "STEPLENGTH", RideFile::SPARSE);
if (point->rcad > 0 && step_length > 0) {
total += step_length;
++count;
}
}
setValue(count > 0 ? total / count : count);
setCount(count);
}
bool isRelevantForRide(const RideItem *ride) const { return (ride->present.contains("S") && ride->present.contains("C") && ride->isRun); }
MetricClass classification() const { return Undefined; }
MetricValidity validity() const { return Unknown; }
RideMetric *clone() const { return new AvgStepLength(*this); }
};
static bool avgStepLengthAdded =
RideMetricFactory::instance().addMetric(AvgStepLength());
//////////////////////////////////////////////////////////////////////////////