mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Merge pull request #3060 from amtriathlon/master
Additional Running Dynamics support
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Reference in New Issue
Block a user