From 0e6d7c1f6b5fbcbc196699ae3b2d79036f3c8f1e Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Date: Thu, 22 Oct 2015 15:02:24 -0300 Subject: [PATCH] Added Equivalent Performance for Target Race to VDOT Calculator Also fixed constant term error in VO2 formula --- src/VDOTCalculator.cpp | 62 +++++++++++++++++++++++++++++++++++++++--- src/VDOTCalculator.h | 4 +++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/VDOTCalculator.cpp b/src/VDOTCalculator.cpp index 75e5148a6..54466b57b 100644 --- a/src/VDOTCalculator.cpp +++ b/src/VDOTCalculator.cpp @@ -31,7 +31,7 @@ double VDOTCalculator::vdot(double mins, double vel) { // estimated VO2 cost of running at vel speed in m/min - double VO2 = -4.3 + 0.182258*vel + 0.000104*pow(vel, 2); + double VO2 = -4.6 + 0.182258*vel + 0.000104*pow(vel, 2); // fractional utilization of VO2max for mins duration double FVO2 = 0.8 + 0.1894393*exp(-0.012778*mins) + 0.2989558*exp(-0.1932605*mins); @@ -47,6 +47,24 @@ VDOTCalculator::vVdot(double VDOT) return 29.54 + 5.000663*VDOT - 0.007546*pow(VDOT, 2); } +double +VDOTCalculator::eqvTime(double VDOT, double dist) +{ + // equivalent time for VDOT at dist, estimated by Newton-Raphson method + double t = dist/vVdot(VDOT)/0.9; // initial guess at TPace + int iter = 100; // max iterations + double f_t, fprime_t; + + do { + f_t = (0.000104*pow(dist, 2)*pow(t, -2) + 0.182258*dist*pow(t, -1) -4.6)/(0.2989558*exp(-0.1932605*t) + 0.1894393*exp(-0.012778*t) + 0.8) - VDOT; + fprime_t = ((0.2989558*exp(-0.1932605*t) + 0.1894393*exp(-0.012778*t) + 0.8)*(-0.000208*pow(dist, 2)*pow(t,-3) - 0.182258*dist*pow(t, -2)) - ((0.000104*pow(dist, 2)*pow(t, -2) + 0.182258*dist*pow(t, -1) -4.6) * (-0.1932605*0.2989558*exp( -0.1932605*t) + -0.012778*0.1894393*exp(-0.012778*t)))) / pow(0.2989558*exp(-0.1932605*t) + 0.1894393*exp(-0.012778*t) + 0.8, 2); + t -= f_t/fprime_t; + iter--; + } while (abs(f_t/fprime_t) > 1e-3 && iter > 0); + + return t; +} + VDOTCalculator::VDOTCalculator(QWidget *parent) : QDialog(parent) { bool metricRnPace = appsettings->value(this, GC_PACE, true).toBool(); @@ -57,13 +75,13 @@ VDOTCalculator::VDOTCalculator(QWidget *parent) : QDialog(parent) setAttribute(Qt::WA_DeleteOnClose); - setFixedSize(300, 450); + setFixedSize(300, 480); QVBoxLayout *mainVBox = new QVBoxLayout(this); - mainVBox->addWidget(new QLabel(tr("Your race (1500 to Marathon):"))); - QHBoxLayout *distHBox = new QHBoxLayout; + distHBox->addWidget(new QLabel(tr("Your Test Race:"))); + distHBox->addStretch(); distSpinBox = new QDoubleSpinBox(this); distSpinBox->setDecimals(3); if (metricRnPace) { @@ -149,11 +167,45 @@ VDOTCalculator::VDOTCalculator(QWidget *parent) : QDialog(parent) } } tableWidgetTPACE->selectRow(2); // Highlight T-Pace + tableWidgetTPACE->resizeRowsToContents(); tableLayout->addWidget(tableWidgetTPACE); mainVBox->addLayout(tableLayout); mainVBox->addStretch(); + QHBoxLayout *targetHBox = new QHBoxLayout; + targetHBox->addWidget(new QLabel(tr("Your Target Race:"))); + targetHBox->addStretch(); + targetSpinBox = new QDoubleSpinBox(this); + targetSpinBox->setDecimals(3); + if (metricRnPace) { + targetSpinBox->setRange(1.5, 42.195); + targetSpinBox->setSuffix(tr(" km")); + targetSpinBox->setValue(21.0975); + } else { + targetSpinBox->setRange(1.5/KM_PER_MILE, 42.195/KM_PER_MILE); + targetSpinBox->setSuffix(tr(" mi")); + targetSpinBox->setValue(21.0975/KM_PER_MILE); + } + targetSpinBox->setSingleStep(1.0); + targetSpinBox->setWrapping(false); + targetSpinBox->setAlignment(Qt::AlignRight); + targetHBox->addWidget(targetSpinBox); + targetHBox->addStretch(); + mainVBox->addLayout(targetHBox); + QHBoxLayout *eqvHBox = new QHBoxLayout; + eqvHBox->addStretch(); + labelEQV = new QLabel(tr("Equivalent Time:")); + eqvHBox->addWidget(labelEQV); + txtEQV = new QLineEdit(this); + txtEQV->setAlignment(Qt::AlignRight); + txtEQV->setReadOnly(true); + eqvHBox->addWidget(txtEQV, Qt::AlignLeft); + eqvHBox->addStretch(); + mainVBox->addLayout(eqvHBox); + + mainVBox->addStretch(); + QHBoxLayout *buttonHBox = new QHBoxLayout; btnCalculate = new QPushButton(this); btnCalculate->setText(tr("Calculate")); @@ -206,4 +258,6 @@ void VDOTCalculator::on_btnCalculate_clicked() tableWidgetTPACE->item(i, 2)->setData(Qt::EditRole, QString("%1") .arg(i < 4 ? QTime(0,0,0).addSecs(pace).toString("mm:ss") : "-----")); } + double targetDist = paceFactor*1000*targetSpinBox->value(); + txtEQV->setText(QTime(0,0,0).addSecs(60*eqvTime(VDOT, targetDist)).toString("hh:mm:ss")); } diff --git a/src/VDOTCalculator.h b/src/VDOTCalculator.h index 3c359cc90..ceb5da52c 100644 --- a/src/VDOTCalculator.h +++ b/src/VDOTCalculator.h @@ -34,6 +34,7 @@ class VDOTCalculator : public QDialog VDOTCalculator(QWidget *parent = 0); static double vdot(double mins, double vel); static double vVdot(double VDOT); + static double eqvTime(double VDOT, double dist); private: QPushButton *btnCalculate; @@ -46,6 +47,9 @@ class VDOTCalculator : public QDialog QDoubleSpinBox *hoursSpinBox; QDoubleSpinBox *minsSpinBox; QDoubleSpinBox *secsSpinBox; + QDoubleSpinBox *targetSpinBox; + QLabel *labelEQV; + QLineEdit *txtEQV; private slots: void on_btnOK_clicked();