diff --git a/src/BikeScore.cpp b/src/BikeScore.cpp index b85db6d5d..b53186910 100644 --- a/src/BikeScore.cpp +++ b/src/BikeScore.cpp @@ -95,7 +95,7 @@ class RelativeIntensity : public RideMetric { assert(deps.contains("skiba_xpower")); XPower *xp = dynamic_cast(deps.value("skiba_xpower")); assert(xp); - reli = xp->xpower / zones->getFTP(zoneRange); + reli = xp->xpower / zones->getCP(zoneRange); secs = xp->secs; } } @@ -122,8 +122,7 @@ class BikeScore : public RideMetric { assert(ri); double normWork = xp->xpower * xp->secs; double rawBikeScore = normWork * ri->value(true); - // TODO: use CP, not FTP here - double workInAnHourAtCP = zones->getFTP(zoneRange) * 3600; + double workInAnHourAtCP = zones->getCP(zoneRange) * 3600; score = rawBikeScore / workInAnHourAtCP * 100.0; } RideMetric *clone() const { return new BikeScore(*this); } diff --git a/src/ConfigDialog.cpp b/src/ConfigDialog.cpp index 573e1dc85..0e35fdc1b 100644 --- a/src/ConfigDialog.cpp +++ b/src/ConfigDialog.cpp @@ -23,10 +23,22 @@ ConfigDialog::ConfigDialog(QDir _home) configPage = new ConfigurationPage(); - //cyclistPage = new CyclistPage(); + + QFile zonesFile(_home.absolutePath() + "/power.zones"); + if (zonesFile.exists()) + { + zones = new Zones(); + if (!zones->read(zonesFile)) + { + QMessageBox::warning(this, tr("Zones File Error"), zones->errorString()); + zones = NULL; + } + } + + cyclistPage = new CyclistPage(this, zones); pagesWidget = new QStackedWidget; pagesWidget->addWidget(configPage); - //pagesWidget->addWidget(cyclistPage); + pagesWidget->addWidget(cyclistPage); //pagesWidget->addWidget(new QueryPage); QPushButton *closeButton = new QPushButton(tr("Close")); @@ -37,6 +49,9 @@ ConfigDialog::ConfigDialog(QDir _home) connect(closeButton, SIGNAL(clicked()), this, SLOT(reject())); connect(saveButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(cyclistPage->btnBack, SIGNAL(clicked()), this, SLOT(back_Clicked())); + connect(cyclistPage->btnForward, SIGNAL(clicked()), this, SLOT(forward_Clicked())); + connect(cyclistPage->btnNew, SIGNAL(clicked()), this, SLOT(new_Clicked())); QHBoxLayout *horizontalLayout = new QHBoxLayout; horizontalLayout->addWidget(contentsWidget); @@ -65,28 +80,14 @@ void ConfigDialog::createIcons() configButton->setTextAlignment(Qt::AlignHCenter); configButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - /* + QListWidgetItem *cyclistButton = new QListWidgetItem(contentsWidget); cyclistButton->setIcon(QIcon(":images/cyclist.png")); cyclistButton->setText(tr("Cyclist Info")); cyclistButton->setTextAlignment(Qt::AlignHCenter); cyclistButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - */ - - /* - QListWidgetItem *powerButton = new QListWidgetItem(contentsWidget); - powerButton->setIcon(QIcon("images/power.png")); - powerButton->setText(tr("Power Levels")); - powerButton->setTextAlignment(Qt::AlignHCenter); - powerButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - QListWidgetItem *queryButton = new QListWidgetItem(contentsWidget); - queryButton->setIcon(QIcon(":/images/query.png")); - queryButton->setText(tr("Query")); - queryButton->setTextAlignment(Qt::AlignHCenter); - queryButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - */ + connect(contentsWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(changePage(QListWidgetItem *, QListWidgetItem*))); @@ -106,19 +107,138 @@ void ConfigDialog::save_Clicked() { QSettings settings(GC_SETTINGS_CO, GC_SETTINGS_APP); settings.setValue(GC_UNIT, configPage->unitCombo->currentText()); - -/* - QString strLT = cyclistPage->txtThreshold->text(); - qDebug() << "LT is: " << strLT; - double LT = strLT.toDouble(); - - Zones *zones = new Zones(); - zones->write(LT, QDir(home)); - - delete zones; -*/ + //If the user never switched pages, then make sure we have up to date data. + if (cyclistPage->getCurrentRange() == zones->ranges.size() - 1) + { + // Record the End Date.. + zones->setStartDate(zones->ranges.size() - 1, cyclistPage->calendar->selectedDate()); + //Swap the end date for the previous zone.. + zones->setEndDate(zones->ranges.size() - 2, cyclistPage->calendar->selectedDate()); + + //Store the CP for the new Zone.. + zones->setCP(cyclistPage->getCurrentRange(), cyclistPage->txtThreshold->text().toInt()); + } + + zones->write(home); + accept(); } +void ConfigDialog::back_Clicked() +{ + + if ((cyclistPage->txtThreshold->isModified() == true) || cyclistPage->btnNew->isEnabled() == true) + { + if (cyclistPage->txtThreshold->text().length() == 0) + { + QMessageBox::warning(this, tr("Missing Field"), "CP cannot be empty."); + cyclistPage->txtThreshold->setFocus(); + return; + } + + //Store the current CP before changing zones. + zones->setCP(cyclistPage->getCurrentRange(), cyclistPage->txtThreshold->text().toInt()); + } + cyclistPage->setCurrentRange(cyclistPage->getCurrentRange() - 1); + cyclistPage->btnForward->setEnabled(true); + int ftp = zones->getCP(cyclistPage->getCurrentRange()); + QString strCP; + cyclistPage->txtThreshold->setText(strCP.setNum(ftp)); + + QDate _date = zones->getEndDate(cyclistPage->getCurrentRange()); + if (cyclistPage->btnNew->isEnabled() == true) + cyclistPage->calendar->setEnabled(false); + cyclistPage->calendar->setMinimumDate(zones->getStartDate(0)); + cyclistPage->calendar->setSelectedDate(_date); + + if (cyclistPage->getCurrentRange() == 0) + cyclistPage->btnBack->setEnabled(false); + cyclistPage->lblCurRange->setText(QString("Current Zone Range: %1").arg(cyclistPage->getCurrentRange() + 1)); + +} + +void ConfigDialog::forward_Clicked() +{ + + if ((cyclistPage->txtThreshold->isModified() == true) || cyclistPage->btnNew->isEnabled() == true) + { + if (cyclistPage->txtThreshold->text().length() == 0) + { + QMessageBox::warning(this, tr("Missing Field"), "CP cannot be empty"); + cyclistPage->txtThreshold->setFocus(); + return; + } + + //Store the current CP before changing zones. + zones->setCP(cyclistPage->getCurrentRange(), cyclistPage->txtThreshold->text().toInt()); + } + if (cyclistPage->btnNew->isEnabled() == true) + cyclistPage->calendar->setEnabled(false); + //Now switch zones + QDate date; + cyclistPage->setCurrentRange(cyclistPage->getCurrentRange() + 1); + + int ftp = zones->getCP(cyclistPage->getCurrentRange()); + QString strCP; + cyclistPage->txtThreshold->setText(strCP.setNum(ftp)); + + if (cyclistPage->getCurrentRange() + 1 == zones->ranges.size()) + { + cyclistPage->btnForward->setEnabled(false); + date = zones->getStartDate(cyclistPage->getCurrentRange()); + } + else + date = zones->getEndDate(cyclistPage->getCurrentRange()); + + cyclistPage->calendar->setSelectedDate(date); + cyclistPage->calendar->setMinimumDate(zones->getStartDate(cyclistPage->getCurrentRange())); + cyclistPage->btnBack->setEnabled(true); + cyclistPage->lblCurRange->setText(QString("Current Zone Range: %1").arg(cyclistPage->getCurrentRange() + 1)); +} + +void ConfigDialog::new_Clicked() +{ + + if ((cyclistPage->txtThreshold->isModified() == true) || cyclistPage->btnNew->isEnabled() == true) + { + if (cyclistPage->txtThreshold->text().length() == 0) + { + QMessageBox::warning(this, tr("Missing Field"), "CP cannot be empty"); + cyclistPage->txtThreshold->setFocus(); + return; + } + + //Store the current CP before changing zones. + zones->setCP(cyclistPage->getCurrentRange(), cyclistPage->txtThreshold->text().toInt()); + } + + + cyclistPage->setChoseNewZone(true); + cyclistPage->txtThreshold->setText(""); + cyclistPage->btnNew->setEnabled(false); + cyclistPage->calendar->setEnabled(true); + + //Modify the current zone.. + zones->setEndDate(cyclistPage->getCurrentRange(), cyclistPage->calendar->selectedDate()); + + + //Create the Zone + cyclistPage->calendar->setMinimumDate(zones->getStartDate(zones->ranges.size() - 1)); + zones->addZoneRange(zones->getStartDate(cyclistPage->getCurrentRange()), cyclistPage->calendar->selectedDate(), 0); + cyclistPage->setCurrentRange(zones->ranges.size() - 1); + cyclistPage->lblCurRange->setText(QString("Current Zone Range: %1").arg(cyclistPage->getCurrentRange() + 1)); + + + + + + +} + +ConfigDialog::~ConfigDialog() +{ + delete zones; +} + diff --git a/src/ConfigDialog.h b/src/ConfigDialog.h index 1371db85f..0e6ed97d7 100644 --- a/src/ConfigDialog.h +++ b/src/ConfigDialog.h @@ -15,10 +15,14 @@ class ConfigDialog : public QDialog Q_OBJECT public: ConfigDialog(QDir home); + ~ConfigDialog(); public slots: void changePage(QListWidgetItem *current, QListWidgetItem *previous); void save_Clicked(); - + void back_Clicked(); + void forward_Clicked(); + void new_Clicked(); + private: void createIcons(); void calculateZones(); @@ -31,6 +35,8 @@ class ConfigDialog : public QDialog CyclistPage *cyclistPage; QPushButton *saveButton; QDir home; + Zones *zones; + }; #endif diff --git a/src/Pages.cpp b/src/Pages.cpp index 5bf88b536..c4b6f62cf 100644 --- a/src/Pages.cpp +++ b/src/Pages.cpp @@ -41,34 +41,72 @@ ConfigurationPage::ConfigurationPage(QWidget *parent) setLayout(mainLayout); } -/* -CyclistPage::CyclistPage(QWidget *parent) -: QWidget(parent) + +CyclistPage::CyclistPage(QWidget *parent, Zones *_zones) + : QWidget(parent) { QGroupBox *cyclistGroup = new QGroupBox(tr("Cyclist Options")); - QLabel *lblThreshold = new QLabel(tr("Threshold Power:")); + zones = _zones; + + setChoseNewZone(false); + + QLabel *lblThreshold = new QLabel(tr("Critical Power:")); txtThreshold = new QLineEdit(); + txtThreshold->setInputMask("999"); - //QLabel *lblWeight = new QLabel(tr("Weight:")); - //QLineEdit *txtWeight = new QLineEdit(this); + calendar = new QCalendarWidget(this); + setCurrentRange(zones->ranges.size() - 1); + QDate date = zones->getStartDate(getCurrentRange()); + calendar->setSelectedDate(date); + calendar->setMinimumDate(date); + calendar->setEnabled(false); + + QString strCP; + + setCurrentRange((zones->ranges.size() - 1)); + int ftp = zones->getCP(zones->ranges.size() - 1); + txtThreshold->setText(strCP.setNum(ftp)); + + lblCurRange = new QLabel(this); + lblCurRange->setFrameStyle(QFrame::Panel | QFrame::Sunken); + lblCurRange->setText(QString("Current Zone Range: %1").arg(getCurrentRange() + 1)); + + btnBack = new QPushButton(this); + btnBack->setText(tr("Back")); + btnForward = new QPushButton(this); + btnForward->setText(tr("Forward")); + btnNew = new QPushButton(this); + btnNew->setText(tr("New Zone Range")); + + btnForward->setEnabled(false); //Layout - QHBoxLayout *powerLayout = new QHBoxLayout; + QHBoxLayout *powerLayout = new QHBoxLayout(); powerLayout->addWidget(lblThreshold); powerLayout->addWidget(txtThreshold); - - //QHBoxLayout *thresholdLayout = new QHBoxLayout; - //thresholdLayout->addWidget(lblWeight); - //thresholdLayout->addWidget(txtWeight); + QHBoxLayout *rangeLayout = new QHBoxLayout(); + rangeLayout->addWidget(lblCurRange); + + QHBoxLayout *zoneLayout = new QHBoxLayout(); + zoneLayout->addWidget(btnBack); + zoneLayout->addWidget(btnForward); + zoneLayout->addWidget(btnNew); + + QHBoxLayout *calendarLayout = new QHBoxLayout(); + calendarLayout->addWidget(calendar); QVBoxLayout *cyclistLayout = new QVBoxLayout; cyclistLayout->addLayout(powerLayout); - //cyclistLayout->addLayout(thresholdLayout); + cyclistLayout->addLayout(rangeLayout); + cyclistLayout->addLayout(zoneLayout); + cyclistLayout->addLayout(calendarLayout); + cyclistGroup->setLayout(cyclistLayout); - + + QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(cyclistGroup); mainLayout->addStretch(1); @@ -77,50 +115,27 @@ CyclistPage::CyclistPage(QWidget *parent) QString CyclistPage::getText() { - qDebug() << txtThreshold->text(); - return txtThreshold->text(); + return txtThreshold->text(); } -*/ -/* -PowerPage::PowerPage(QWidget *parent) -: QWidget(parent) +void CyclistPage::setCurrentRange(int _range) { - QGroupBox *powerGroup = new QGroupBox(tr("Power Levels")); - - QLabel *lblThreshold = new QLabel(tr("Threshold Power:")); - QLineEdit *txtThreshold = new QLineEdit(this); - - QLabel *lblWeight = new QLabel(tr("Weight:")); - QLineEdit *txtWeight = new QLineEdit(this); - - - //Layout - QHBoxLayout *powerLayout = new QHBoxLayout; - powerLayout->addWidget(lblThreshold); - powerLayout->addWidget(txtThreshold); - - - QHBoxLayout *thresholdLayout = new QHBoxLayout; - thresholdLayout->addWidget(lblWeight); - thresholdLayout->addWidget(txtWeight); - - QVBoxLayout *cyclistLayout = new QVBoxLayout; - cyclistLayout->addLayout(powerLayout); - cyclistLayout->addLayout(thresholdLayout); - powerGroup->setLayout(cyclistLayout); - - QVBoxLayout *mainLayout = new QVBoxLayout; - mainLayout->addWidget(powerGroup); - mainLayout->addStretch(1); - setLayout(mainLayout); + currentRange = _range; } -*/ -//Active recovery: <55% -//Endurance: 56-75% -//Tempo: 76-90% -//Threshold: 91-105% -//VO2 max: 106-120% -//Anaerobic Capacity: 121-150% -//Neuromuscular power: N/A but could be anything >151% +int CyclistPage::getCurrentRange() +{ + return currentRange; +} + +bool CyclistPage::isNewZone() +{ + return newZone; +} + +void CyclistPage::setChoseNewZone(bool _newZone) +{ + newZone = _newZone; +} + + diff --git a/src/Pages.h b/src/Pages.h index 998c0229b..25feb9b7e 100644 --- a/src/Pages.h +++ b/src/Pages.h @@ -3,6 +3,11 @@ #include #include +#include +#include +#include +#include "Zones.h" +#include class ConfigurationPage : public QWidget { @@ -14,11 +19,26 @@ class ConfigurationPage : public QWidget class CyclistPage : public QWidget { public: - CyclistPage(QWidget *parent = 0); - int thresholdPower; - QLineEdit *txtThreshold; - QString getText(); - //int weight; + CyclistPage(QWidget *parent = 0, Zones *_zones = 0); + int thresholdPower; + QLineEdit *txtThreshold; + QString getText(); + QCalendarWidget *calendar; + void setCurrentRange(int range); + QPushButton *btnBack; + QPushButton *btnForward; + QPushButton *btnNew; + QLabel *lblCurRange; + + int getCurrentRange(); + void setChoseNewZone(bool _newZone); + bool isNewZone(); + + private: + Zones *zones; + int currentRange; + bool newZone; + }; /* diff --git a/src/Zones.cpp b/src/Zones.cpp index 7aa6b234f..bef6203c1 100644 --- a/src/Zones.cpp +++ b/src/Zones.cpp @@ -176,12 +176,18 @@ void Zones::zoneInfo(int rnum, int znum, high = zone->hi; } -int Zones::getFTP(int rnum) const +int Zones::getCP(int rnum) const { assert(rnum < ranges.size()); return ranges[rnum]->ftp; } +void Zones::setCP(int rnum, int ftp) +{ + ranges[rnum]->ftp = ftp; +} + + QString Zones::summarize(int rnum, double *time_in_zone, int num_zones) const { assert(rnum < ranges.size()); @@ -225,70 +231,100 @@ QString Zones::summarize(int rnum, double *time_in_zone, int num_zones) const return summary; } -/* -// Eventually for automatically generating the power.zones file -// Disabled for now until multiple date ranges are respected. -*/ -/* -void Zones::write(int LT, QDir home) +void Zones::write(QDir home) { - int active_recovery = 0; - int endurance_start = 0; - int endurance_end = 0; - int tempo_start = 0; - int tempo_end = 0; - int threshold_start = 0; - int threshold_end = 0; - int vo2max_start = 0; - int vo2max_end = 0; - int anaerobicCapacity_start = 0; - int anaerobicCapacity_end = 0; - int neuromuscular = 0; - - active_recovery = int(LT * .55); - endurance_start = int(LT * .56); - endurance_end = int(LT * .75); - tempo_start = int(LT * .76); - tempo_end = int(LT * .90); - threshold_start = int(LT * .91); - threshold_end = int(LT * 1.05); - vo2max_start = int(LT * 1.06); - vo2max_end = int(LT * 1.2); - anaerobicCapacity_start = int(LT * 1.21); - anaerobicCapacity_end = int(LT * 1.5); - neuromuscular = int(LT * 1.51); - + int active_recovery; + int endurance_start; + int endurance_end; + int tempo_start; + int tempo_end; + int threshold_start; + int threshold_end; + int vo2max_start; + int vo2max_end; + int anaerobicCapacity_start; + int anaerobicCapacity_end; + int neuromuscular; QString strzones; - strzones += QString("From BEGIN until END, FTP=%1:").arg(LT); - strzones += QString("\n"); - strzones += QString("1,Active Recovery, 1, %1").arg(active_recovery); - strzones += QString("\n"); - strzones += QString("2,Endurance, %1, %2").arg(endurance_start).arg(endurance_end); - strzones += QString("\n"); - strzones += QString("3,Tempo, %1, %2").arg(tempo_start).arg(tempo_end); - strzones += QString("\n"); - strzones += QString("4,Threshold, %1, %2").arg(threshold_start).arg(threshold_end); - strzones += QString("\n"); - strzones += QString("5,VO2Max, %1, %2").arg(vo2max_start).arg(vo2max_end); - strzones += QString("\n"); - strzones += QString("6,Anaerobic, %1, %2").arg(anaerobicCapacity_start).arg(anaerobicCapacity_end); - strzones += QString("\n"); - strzones += QString("7,Neuromuscular, %1,").arg(neuromuscular); - strzones += QString("MAX"); - strzones += QString("\n"); - qDebug() << "Zones are:" << strzones; - - QFile file( home.absolutePath() + "/power.zones" ); - if ( file.open( QFile::WriteOnly ) ) { - QTextStream stream( &file ); - stream << strzones; - file.close(); - } -*/ + for (int i = 0; i < ranges.size(); i++) + { + int LT = getCP(i); + active_recovery = 0; + endurance_start = 0; + endurance_end = 0; + tempo_start = 0; + tempo_end = 0; + threshold_start = 0; + threshold_end = 0; + vo2max_start = 0; + vo2max_end = 0; + anaerobicCapacity_start = 0; + anaerobicCapacity_end = 0; + neuromuscular = 0; + + + active_recovery = int (LT * .55); + endurance_start = int (LT * .56); + endurance_end = int (LT * .75); + tempo_start = int (LT * .76); + tempo_end = int (LT * .90); + threshold_start = int (LT * .91); + threshold_end = int (LT * 1.05); + vo2max_start = int (LT * 1.06); + vo2max_end = int (LT * 1.2); + anaerobicCapacity_start = int (LT * 1.21); + anaerobicCapacity_end = int (LT * 1.5); + neuromuscular = int (LT * 1.51); + + if (i == 0) + strzones += QString("FROM BEGIN UNTIL %1, CP=%2:").arg(getEndDate(i).toString("yyyy/MM/dd")).arg(LT); + else if (i == ranges.size() - 1) + strzones += QString("FROM %1 UNTIL END, CP=%2:").arg(getStartDate(i).toString("yyyy/MM/dd")).arg(LT); + else + strzones += QString("FROM %1 UNTIL %2, CP=%3:").arg(getStartDate(i).toString("yyyy/MM/yy")).arg(getEndDate(i).toString("yyyy/MM/dd")).arg(LT); + + + strzones += QString("\n"); + strzones += QString("1,Active Recovery, 1, %1").arg(active_recovery); + strzones += QString("\n"); + strzones += QString("2,Endurance, %1, %2").arg(endurance_start).arg(endurance_end); + strzones += QString("\n"); + strzones += QString("3,Tempo, %1, %2").arg(tempo_start).arg(tempo_end); + strzones += QString("\n"); + strzones += QString("4,Threshold, %1, %2").arg(threshold_start).arg(threshold_end); + strzones += QString("\n"); + strzones += QString("5,VO2Max, %1, %2").arg(vo2max_start).arg(vo2max_end); + strzones += QString("\n"); + strzones += QString("6,Anaerobic, %1, %2").arg(anaerobicCapacity_start).arg(anaerobicCapacity_end); + strzones += QString("\n"); + strzones += QString("7,Neuromuscular, %1,").arg(neuromuscular); + strzones += QString("MAX"); + strzones += QString("\n"); + strzones += QString("\n"); + + } + + + QFile file(home.absolutePath() + "/power.zones"); + if (file.open(QFile::WriteOnly)) + { + QTextStream stream(&file); + stream << strzones << endl; + file.close(); + } + +} + +void Zones::addZoneRange(QDate _start, QDate _end, int _ftp) +{ + ZoneRange *range = new ZoneRange(_start, _end); + range->ftp = _ftp; + ranges.append(range); +} /* -From 2008/01/01 until END,FTP=270: +From 2008/01/01 until END,CP=270: 1,Active Recovery, 1, 150 2,Endurance, 151, 204 3,Tempo, 205, 245 @@ -296,4 +332,12 @@ From 2008/01/01 until END,FTP=270: 5,VO2Max, 286, 326 6,Anaerobic, 327, MAX */ -//} + +void Zones::setEndDate(int rnum, QDate endDate) +{ + ranges[rnum]->end = endDate; +} +void Zones::setStartDate(int rnum, QDate startDate) +{ + ranges[rnum]->begin = startDate; +} diff --git a/src/Zones.h b/src/Zones.h index 65ff38ad0..de5ea19db 100644 --- a/src/Zones.h +++ b/src/Zones.h @@ -60,18 +60,25 @@ class Zones : public QObject delete i.next(); } + void addZoneRange(QDate _start, QDate _end, int _ftp); + bool read(QFile &file); - void write(int LT, QDir home); + void write(QDir home); const QString &errorString() const { return err; } int whichRange(const QDate &date) const; int numZones(int range) const; int whichZone(int range, double value) const; - void zoneInfo(int range, int zone, + void zoneInfo(int range, int zone, QString &name, QString &description, int &low, int &high) const; QString summarize(int rnum, double *time_in_zone, int num_zones) const; - int getFTP(int rnum) const; + int getCP(int rnum) const; + void setCP(int rnum, int ftp); + QDate getStartDate(int rnum); + QDate getEndDate(int rnum); + void setEndDate(int rnum, QDate date); + void setStartDate(int rnum, QDate date); }; #endif // _Zones_h