from Justin: zones editor, switch from FTP to CP

This commit is contained in:
Sean C. Rhea
2008-05-14 00:17:10 +00:00
parent 4eeb656016
commit e6c85a12f4
7 changed files with 368 additions and 157 deletions

View File

@@ -95,7 +95,7 @@ class RelativeIntensity : public RideMetric {
assert(deps.contains("skiba_xpower"));
XPower *xp = dynamic_cast<XPower*>(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); }

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -3,6 +3,11 @@
#include <QWidget>
#include <QComboBox>
#include <QCalendarWidget>
#include <QPushButton>
#include <QList>
#include "Zones.h"
#include <QLabel>
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;
};
/*

View File

@@ -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;
}

View File

@@ -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