Permalink
Browse files

Model CP and W' decay in Morton Model

.. add option to apply a decay factor to CP and W' when plotting
   the model curve in CP Plot.

.. since we always fit to observations <20mins the mostly submax
   points at longer durations in the general population do not
   impact the fit at all.

.. the decay factors for w' and cp have been fit to the results of:

   Effects of Two Hours of Heavy-Intensity Exercise on the Power-
Duration Relationship
   Clark IE, Vanhatalo A, Bailey SJ, Wylie LJ, Kirby BS, Wilkins BW,
Jones AM.
   https://europepmc.org/abstract/med/29521722
  • Loading branch information...
liversedge committed Nov 30, 2018
1 parent 1a50345 commit 0b4d46666d7dc30568c93f132e014f049b686856
@@ -59,7 +59,7 @@
CPPlot::CPPlot(CriticalPowerWindow *parent, Context *context, bool rangemode) : QwtPlot(parent), parent(parent),
// model
model(0), modelVariant(0), fit(0), fitdata(0),
model(0), modelVariant(0), fit(0), fitdata(0), modelDecay(false),
// state
context(context), bestsCache(NULL), dateCV(0.0), isRun(false), isSwim(false),
@@ -357,6 +357,7 @@ CPPlot::initModel()
break;
case 2 : // 3 param
pdModel = new CP3Model(context);
static_cast<CP3Model*>(pdModel)->modelDecay = modelDecay;
break;
case 3 : // extended model
pdModel = new ExtendedModel(context);
@@ -2337,7 +2338,7 @@ CPPlot::showXAxisLinear(bool x)
// model parameters!
void
CPPlot::setModel(int sanI1, int sanI2, int anI1, int anI2, int aeI1, int aeI2, int laeI1, int laeI2, int model, int variant, int fit, int fitdata)
CPPlot::setModel(int sanI1, int sanI2, int anI1, int anI2, int aeI1, int aeI2, int laeI1, int laeI2, int model, int variant, int fit, int fitdata, bool decay)
{
this->anI1 = double(anI1);
this->anI2 = double(anI2);
@@ -2353,6 +2354,7 @@ CPPlot::setModel(int sanI1, int sanI2, int anI1, int anI2, int aeI1, int aeI2, i
this->modelVariant = variant;
this->fit = fit;
this->fitdata = fitdata;
this->modelDecay = decay;
clearCurves();
}
@@ -84,7 +84,7 @@ class CPPlot : public QwtPlot
void setPlotType(int index);
void showXAxisLinear(bool x);
void setModel(int sanI1, int sanI2, int anI1, int anI2,
int aeI1, int aeI2, int laeI1, int laeI2, int model, int variant, int fit, int fitdata);
int aeI1, int aeI2, int laeI1, int laeI2, int model, int variant, int fit, int fitdata, bool modelDecay);
// getters
QVector<double> getBests();
@@ -152,6 +152,7 @@ class CPPlot : public QwtPlot
// Models and Extended Models
int model, modelVariant;
int fit, fitdata;
bool modelDecay;
double sanI1, sanI2, anI1, anI2, aeI1, aeI2, laeI1, laeI2;
// Data and State
@@ -296,6 +296,12 @@ CriticalPowerWindow::CriticalPowerWindow(Context *context, bool rangemode) :
mcl->addRow(new QLabel(tr("Data to fit")), fitdataCombo);
mcl->addRow(new QLabel(tr(" ")));
modelDecayLabel = new QLabel(tr("CP and W' Decay"));
modelDecayCheck = new QCheckBox(this);
mcl->addRow(modelDecayLabel, modelDecayCheck);
mcl->addRow(new QLabel(tr(" ")));
intervalLabel = new QLabel(tr("Search Interval"));
secondsLabel = new QLabel(tr("(seconds)"));
mcl->addRow(intervalLabel, secondsLabel);
@@ -527,6 +533,7 @@ CriticalPowerWindow::CriticalPowerWindow(Context *context, bool rangemode) :
connect(sanI2SpinBox, SIGNAL(valueChanged(double)), this, SLOT(modelParametersChanged()));
connect(laeI1SpinBox, SIGNAL(valueChanged(double)), this, SLOT(modelParametersChanged()));
connect(laeI2SpinBox, SIGNAL(valueChanged(double)), this, SLOT(modelParametersChanged()));
connect(modelDecayCheck, SIGNAL(toggled(bool)), this, SLOT(modelParametersChanged()));
connect(velo1, SIGNAL(toggled(bool)), this, SLOT(modelParametersChanged()));
connect(velo2, SIGNAL(toggled(bool)), this, SLOT(modelParametersChanged()));
connect(velo3, SIGNAL(toggled(bool)), this, SLOT(modelParametersChanged()));
@@ -764,6 +771,8 @@ CriticalPowerWindow::modelChanged()
laeLabel->hide();
laeI1SpinBox->hide();
laeI2SpinBox->hide();
modelDecayCheck->hide();
modelDecayLabel->hide();
// No default values !
break;
@@ -774,6 +783,8 @@ CriticalPowerWindow::modelChanged()
velo1->show();
velo2->show();
velo3->show();
modelDecayCheck->hide();
modelDecayLabel->hide();
// intentional fallthrough
// and drop through into case 1 below ...
@@ -795,6 +806,8 @@ CriticalPowerWindow::modelChanged()
laeLabel->hide();
laeI1SpinBox->hide();
laeI2SpinBox->hide();
modelDecayCheck->hide();
modelDecayLabel->hide();
// Default values: class 2-3mins 10-20 model
anI1SpinBox->setValue(120);
@@ -821,6 +834,8 @@ CriticalPowerWindow::modelChanged()
laeLabel->hide();
laeI1SpinBox->hide();
laeI2SpinBox->hide();
modelDecayLabel->show();
modelDecayCheck->show();
// Default values
anI1SpinBox->setValue(180);
@@ -846,6 +861,8 @@ CriticalPowerWindow::modelChanged()
laeLabel->show();
laeI1SpinBox->show();
laeI2SpinBox->show();
modelDecayCheck->hide();
modelDecayLabel->hide();
// Default values
sanI1SpinBox->setValue(20);
@@ -909,7 +926,8 @@ CriticalPowerWindow::modelParametersChanged()
modelCombo->currentIndex(),
variant(),
fitCombo->currentIndex(),
fitdataCombo->currentIndex());
fitdataCombo->currentIndex(),
modelDecay());
// and apply
if (amVisible() && myRideItem != NULL) {
@@ -62,6 +62,7 @@ class CriticalPowerWindow : public GcChartWindow
Q_PROPERTY(int fit READ fit WRITE setFit USER true)
Q_PROPERTY(int fitdata READ fitdata WRITE setFitdata USER true)
Q_PROPERTY(int variant READ variant WRITE setVariant USER true)
Q_PROPERTY(bool modelDecay READ modelDecay WRITE setModelDecay USER true)
Q_PROPERTY(int ani1 READ anI1 WRITE setAnI1 USER true)
Q_PROPERTY(int ani2 READ anI2 WRITE setAnI2 USER true)
Q_PROPERTY(int aei1 READ aeI1 WRITE setAeI1 USER true)
@@ -108,6 +109,9 @@ class CriticalPowerWindow : public GcChartWindow
int cpModel() const { return modelCombo->currentIndex(); }
void setCPModel(int x) { modelCombo->setCurrentIndex(x); }
bool modelDecay() const { return modelDecayCheck->isChecked(); }
void setModelDecay(bool x) { modelDecayCheck->setChecked(x); }
int fit() const { return fitCombo->currentIndex(); }
void setFit(int x) { fitCombo->setCurrentIndex(x); }
@@ -299,6 +303,7 @@ class CriticalPowerWindow : public GcChartWindow
QLabel *cpintTodayValue;
QLabel *cpintAllValue;
QLabel *cpintCPValue;
QLabel *modelDecayLabel;
QComboBox *seriesCombo;
QComboBox *modelCombo;
QComboBox *fitCombo;
@@ -320,6 +325,7 @@ class CriticalPowerWindow : public GcChartWindow
QCheckBox *showGridCheck;
QCheckBox *rPercent, *rHeat, *rDelta, *rDeltaPercent;
QCheckBox *showCSLinearCheck;
QCheckBox *modelDecayCheck;
QLabel *showCSLinearLabel;
QwtPlotPicker *picker;
QwtPlotGrid *grid;
@@ -544,6 +544,7 @@ CP3Model::CP3Model(Context *context) :
aeI2=1200;
model = 1;
modelDecay = false;
connect (this, SIGNAL(dataChanged()), this, SLOT(onDataChanged()));
connect (this, SIGNAL(intervalsChanged()), this, SLOT(onIntervalsChanged()));
@@ -559,8 +560,20 @@ CP3Model::y(double t) const
// adjust to seconds
if (minutes) t *= 60.00f;
// decay value (75% at 10 hrs)
double cpdecay=1.0;
double wdecay=1.0;
// just use a constant for now - it modifies CP/W' so should adjust to
// athlete without needing to be fitted (?)
if (modelDecay) {
cpdecay = 2.0-(1.0/exp(-0.000009*t));
wdecay = 2.0-(1.0/exp(-0.000025*t));
}
// typical values: CP=285.355547 tau=0.500000 t0=0.381158
// classic model - W' / t + CP
return cp * (double(1.00f)+tau /(((double(t)/double(60))+t0)));
return (cp*cpdecay) * (double(1.00f)+(tau*wdecay) /(((double(t)/double(60))+t0)));
}
// 2 parameter model can calculate these
@@ -251,6 +251,8 @@ class CP3Model : public PDModel
public:
CP3Model(Context *context);
bool modelDecay; // should we apply CP + W' decay constants?
// synthetic data for a curve
virtual double y(double t) const;

0 comments on commit 0b4d466

Please sign in to comment.