mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 08:08:42 +00:00
add performance monitor
This commit is contained in:
@@ -140,6 +140,19 @@ void ConfigDialog::save_Clicked()
|
||||
settings->setValue(GC_CRANKLENGTH, configPage->crankLengthCombo->currentText());
|
||||
settings->setValue(GC_BIKESCOREDAYS, configPage->BSdaysEdit->text());
|
||||
settings->setValue(GC_BIKESCOREMODE, configPage->bsModeCombo->currentText());
|
||||
settings->setValue(GC_INITIAL_STS, cyclistPage->perfManStart->text());
|
||||
settings->setValue(GC_INITIAL_LTS, cyclistPage->perfManStart->text());
|
||||
settings->setValue(GC_STS_DAYS, cyclistPage->perfManSTSavg->text());
|
||||
settings->setValue(GC_LTS_DAYS, cyclistPage->perfManLTSavg->text());
|
||||
|
||||
// set default stress names if not set:
|
||||
settings->setValue(GC_STS_NAME, settings->value(GC_STS_NAME,tr("Short Term Stress")));
|
||||
settings->setValue(GC_STS_ACRONYM, settings->value(GC_STS_ACRONYM,tr("STS")));
|
||||
settings->setValue(GC_LTS_NAME, settings->value(GC_LTS_NAME,tr("Long Term Stress")));
|
||||
settings->setValue(GC_LTS_ACRONYM, settings->value(GC_LTS_ACRONYM,tr("LTS")));
|
||||
settings->setValue(GC_SB_NAME, settings->value(GC_SB_NAME,tr("Stress Balance")));
|
||||
settings->setValue(GC_SB_ACRONYM, settings->value(GC_SB_ACRONYM,tr("SB")));
|
||||
|
||||
|
||||
// if the CP text entry reads invalid, there's nothing we can do
|
||||
int cp = cyclistPage->getCP();
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "ToolsDialog.h"
|
||||
#include "MetricAggregator.h"
|
||||
#include "SplitRideDialog.h"
|
||||
#include "PerformanceManagerWindow.h"
|
||||
|
||||
#ifndef GC_VERSION
|
||||
#define GC_VERSION "(developer build)"
|
||||
@@ -218,6 +219,12 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
weeklySummaryWindow = new WeeklySummaryWindow(useMetricUnits, this);
|
||||
tabWidget->addTab(weeklySummaryWindow, tr("Weekly Summary"));
|
||||
|
||||
//////////////////////// Performance Manager ////////////////////////
|
||||
|
||||
performanceManagerWindow = new PerformanceManagerWindow();
|
||||
tabWidget->addTab(performanceManagerWindow, "Performance Manager");
|
||||
|
||||
|
||||
////////////////////////////// Signals //////////////////////////////
|
||||
|
||||
connect(calendar, SIGNAL(clicked(const QDate &)),
|
||||
@@ -870,6 +877,10 @@ MainWindow::tabChanged(int index)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (index == 7) {
|
||||
// Performance Manager
|
||||
performanceManagerWindow->replot(home,allRides);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -37,6 +37,7 @@ class RideFile;
|
||||
class WeeklySummaryWindow;
|
||||
class Zones;
|
||||
class RideCalendar;
|
||||
class PerformanceManagerWindow;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
@@ -104,6 +105,10 @@ class MainWindow : public QMainWindow
|
||||
QTreeWidgetItem *allRides;
|
||||
QSplitter *leftLayout;
|
||||
|
||||
QwtPlotCurve *weeklyBSCurve;
|
||||
QwtPlotCurve *weeklyRICurve;
|
||||
PerformanceManagerWindow *performanceManagerWindow;
|
||||
|
||||
Zones *zones;
|
||||
|
||||
// pedal force/pedal velocity scatter plot widgets
|
||||
|
||||
@@ -163,6 +163,10 @@ CyclistPage::~CyclistPage()
|
||||
delete lblCurRange;
|
||||
delete powerLayout;
|
||||
delete rangeLayout;
|
||||
delete perfManLTSavgLayout;
|
||||
delete perfManSTSavgLayout;
|
||||
delete perfManStartValLayout;
|
||||
delete perfManLayout;
|
||||
delete dateRangeLayout;
|
||||
delete zoneLayout;
|
||||
delete calendarLayout;
|
||||
@@ -173,6 +177,8 @@ CyclistPage::~CyclistPage()
|
||||
CyclistPage::CyclistPage(Zones **_zones):
|
||||
zones(_zones)
|
||||
{
|
||||
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
|
||||
|
||||
cyclistGroup = new QGroupBox(tr("Cyclist Options"));
|
||||
lblThreshold = new QLabel(tr("Critical Power:"));
|
||||
txtThreshold = new QLineEdit();
|
||||
@@ -207,6 +213,28 @@ CyclistPage::CyclistPage(Zones **_zones):
|
||||
lblCurRange->setFrameStyle(QFrame::Panel | QFrame::Sunken);
|
||||
lblCurRange->setText(QString("Current Zone Range: %1").arg(currentRange + 1));
|
||||
|
||||
perfManLabel = new QLabel(tr("Performance Manager"));
|
||||
perfManStartLabel = new QLabel(tr("Starting LTS"));
|
||||
perfManSTSLabel = new QLabel(tr("STS average (days)"));
|
||||
perfManLTSLabel = new QLabel(tr("LTS average (days)"));
|
||||
perfManStartValidator = new QIntValidator(0,200,this);
|
||||
perfManSTSavgValidator = new QIntValidator(1,21,this);
|
||||
perfManLTSavgValidator = new QIntValidator(7,56,this);
|
||||
QVariant perfManStartVal = settings->value(GC_INITIAL_STS);
|
||||
QVariant perfManSTSVal = settings->value(GC_STS_DAYS);
|
||||
if (perfManSTSVal.isNull() || perfManSTSVal.toInt() == 0)
|
||||
perfManSTSVal = 7;
|
||||
QVariant perfManLTSVal = settings->value(GC_LTS_DAYS);
|
||||
if (perfManLTSVal.isNull() || perfManLTSVal.toInt() == 0)
|
||||
perfManLTSVal = 42;
|
||||
perfManStart = new QLineEdit(perfManStartVal.toString(),this);
|
||||
perfManStart->setValidator(perfManStartValidator);
|
||||
perfManSTSavg = new QLineEdit(perfManSTSVal.toString(),this);
|
||||
perfManSTSavg->setValidator(perfManSTSavgValidator);
|
||||
perfManLTSavg = new QLineEdit(perfManLTSVal.toString(),this);
|
||||
perfManLTSavg->setValidator(perfManLTSavgValidator);
|
||||
|
||||
|
||||
QDate today = QDate::currentDate();
|
||||
calendar->setSelectedDate(today);
|
||||
|
||||
@@ -246,12 +274,30 @@ CyclistPage::CyclistPage(Zones **_zones):
|
||||
calendarLayout = new QHBoxLayout();
|
||||
calendarLayout->addWidget(calendar);
|
||||
|
||||
// performance manager
|
||||
perfManLayout = new QVBoxLayout(); // outer
|
||||
perfManStartValLayout = new QHBoxLayout();
|
||||
perfManSTSavgLayout = new QHBoxLayout();
|
||||
perfManLTSavgLayout = new QHBoxLayout();
|
||||
perfManStartValLayout->addWidget(perfManStartLabel);
|
||||
perfManStartValLayout->addWidget(perfManStart);
|
||||
perfManSTSavgLayout->addWidget(perfManSTSLabel);
|
||||
perfManSTSavgLayout->addWidget(perfManSTSavg);
|
||||
perfManLTSavgLayout->addWidget(perfManLTSLabel);
|
||||
perfManLTSavgLayout->addWidget(perfManLTSavg);
|
||||
perfManLayout->addLayout(perfManStartValLayout);
|
||||
perfManLayout->addLayout(perfManSTSavgLayout);
|
||||
perfManLayout->addLayout(perfManLTSavgLayout);
|
||||
|
||||
|
||||
|
||||
cyclistLayout = new QVBoxLayout;
|
||||
cyclistLayout->addLayout(powerLayout);
|
||||
cyclistLayout->addLayout(rangeLayout);
|
||||
cyclistLayout->addLayout(zoneLayout);
|
||||
cyclistLayout->addLayout(dateRangeLayout);
|
||||
cyclistLayout->addLayout(calendarLayout);
|
||||
cyclistLayout->addLayout(perfManLayout);
|
||||
|
||||
cyclistGroup->setLayout(cyclistLayout);
|
||||
|
||||
|
||||
14
src/Pages.h
14
src/Pages.h
@@ -64,6 +64,13 @@ class CyclistPage : public QWidget
|
||||
QLabel *txtEndDate;
|
||||
QLabel *lblStartDate;
|
||||
QLabel *lblEndDate;
|
||||
QLabel *perfManLabel;
|
||||
QLabel *perfManStartLabel;
|
||||
QLabel *perfManSTSLabel;
|
||||
QLabel *perfManLTSLabel;
|
||||
QLineEdit *perfManStart;
|
||||
QLineEdit *perfManSTSavg;
|
||||
QLineEdit *perfManLTSavg;
|
||||
|
||||
int getCurrentRange();
|
||||
bool isNewMode();
|
||||
@@ -83,6 +90,10 @@ class CyclistPage : public QWidget
|
||||
QLabel *lblThreshold;
|
||||
QLineEdit *txtThreshold;
|
||||
QIntValidator *txtThresholdValidator;
|
||||
QVBoxLayout *perfManLayout;
|
||||
QHBoxLayout *perfManStartValLayout;
|
||||
QHBoxLayout *perfManSTSavgLayout;
|
||||
QHBoxLayout *perfManLTSavgLayout;
|
||||
QHBoxLayout *powerLayout;
|
||||
QHBoxLayout *rangeLayout;
|
||||
QHBoxLayout *dateRangeLayout;
|
||||
@@ -90,5 +101,8 @@ class CyclistPage : public QWidget
|
||||
QHBoxLayout *calendarLayout;
|
||||
QVBoxLayout *cyclistLayout;
|
||||
QVBoxLayout *mainLayout;
|
||||
QIntValidator *perfManStartValidator;
|
||||
QIntValidator *perfManSTSavgValidator;
|
||||
QIntValidator *perfManLTSavgValidator;
|
||||
};
|
||||
#endif
|
||||
|
||||
139
src/PerfPlot.cpp
Normal file
139
src/PerfPlot.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Eric Murray (ericm@lne.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <QDebug>
|
||||
#include <qwt_data.h>
|
||||
#include <qwt_legend.h>
|
||||
#include <qwt_plot_curve.h>
|
||||
#include <qwt_plot_grid.h>
|
||||
#include "RideItem.h"
|
||||
#include "RideFile.h"
|
||||
#include "PerfPlot.h"
|
||||
#include "StressCalculator.h"
|
||||
|
||||
PerfPlot::PerfPlot() : STScurve(NULL), LTScurve(NULL), SBcurve(NULL)
|
||||
{
|
||||
|
||||
|
||||
insertLegend(new QwtLegend(), QwtPlot::BottomLegend);
|
||||
setCanvasBackground(Qt::white);
|
||||
setAxisTitle(yLeft, "Stress (BS/Day)");
|
||||
setAxisTitle(xBottom, "Time (days)");
|
||||
|
||||
grid = new QwtPlotGrid();
|
||||
grid->enableX(false);
|
||||
QPen gridPen;
|
||||
gridPen.setStyle(Qt::DotLine);
|
||||
grid->setPen(gridPen);
|
||||
grid->attach(this);
|
||||
}
|
||||
|
||||
void PerfPlot::setStressCalculator(StressCalculator *sc) {
|
||||
_sc = sc;
|
||||
days = _sc->n();
|
||||
startDate = _sc->getStartDate();
|
||||
xmin = 0;
|
||||
xmax = _sc->n();
|
||||
}
|
||||
|
||||
|
||||
void PerfPlot::plot() {
|
||||
|
||||
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
|
||||
|
||||
int num, tics;
|
||||
tics = 27;
|
||||
|
||||
setAxisScale(yLeft, _sc->min(), _sc->max());
|
||||
num = xmax - xmin;
|
||||
|
||||
/*
|
||||
fprintf(stderr,"PerfPlot::plot: xmin = %d xmax = %d num = %d\n",
|
||||
xmin, xmax, num);
|
||||
*/
|
||||
|
||||
// set axis scale
|
||||
if (num < 15) {
|
||||
tics = 1;
|
||||
} else if (num < 71) {
|
||||
tics = 7;
|
||||
} else if (num < 141) {
|
||||
tics = 14;
|
||||
}
|
||||
setAxisScale(xBottom, xmin, xmax,tics);
|
||||
|
||||
setAxisScaleDraw(QwtPlot::xBottom, new TimeScaleDraw(startDate));
|
||||
|
||||
if (STScurve) {
|
||||
STScurve->detach();
|
||||
delete STScurve;
|
||||
}
|
||||
STScurve = new QwtPlotCurve(settings->value(GC_STS_NAME,tr("Short Term Stress")).toString());
|
||||
STScurve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
||||
QPen stspen = QPen(Qt::blue);
|
||||
stspen.setWidth(2.0);
|
||||
STScurve->setPen(stspen);
|
||||
STScurve->setData(_sc->getDays()+xmin,_sc->getSTSvalues()+xmin,num);
|
||||
STScurve->attach(this);
|
||||
|
||||
|
||||
if (LTScurve) {
|
||||
LTScurve->detach();
|
||||
delete LTScurve;
|
||||
}
|
||||
LTScurve = new QwtPlotCurve(settings->value(GC_LTS_NAME,tr("Long Term Stress")).toString());
|
||||
LTScurve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
||||
QPen ltspen = QPen(Qt::green);
|
||||
ltspen.setWidth(2.0);
|
||||
LTScurve->setPen(ltspen);
|
||||
LTScurve->setData(_sc->getDays()+xmin,_sc->getLTSvalues()+xmin,num);
|
||||
LTScurve->attach(this);
|
||||
|
||||
|
||||
if (SBcurve) {
|
||||
SBcurve->detach();
|
||||
delete SBcurve;
|
||||
}
|
||||
SBcurve = new QwtPlotCurve(settings->value(GC_SB_NAME,tr("Stress Balance")).toString());
|
||||
SBcurve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
||||
QPen sbpen = QPen(Qt::black);
|
||||
sbpen.setWidth(2.0);
|
||||
SBcurve->setPen(sbpen);
|
||||
SBcurve->setData(_sc->getDays()+xmin,_sc->getSBvalues()+xmin,num);
|
||||
SBcurve->attach(this);
|
||||
|
||||
replot();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void PerfPlot::resize(int newmin, int newmax)
|
||||
{
|
||||
if (newmin >= 0 && newmin < _sc->n() && newmin < xmax)
|
||||
xmin = newmin;
|
||||
if (newmax >= 0 && newmax < _sc->n() && newmax > xmin)
|
||||
xmax = newmax;
|
||||
|
||||
plot();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
90
src/PerfPlot.h
Normal file
90
src/PerfPlot.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Eric Murray (ericm@lne.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _GC_PerfPlot_h
|
||||
#define _GC_PerfPlot_h 1
|
||||
|
||||
#include "StressCalculator.h"
|
||||
#include <QtGui>
|
||||
#include <qwt_plot.h>
|
||||
#include <qwt_plot_marker.h>
|
||||
#include <qwt_plot_curve.h>
|
||||
#include <qwt_plot_grid.h>
|
||||
#include <qwt_scale_draw.h>
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
|
||||
|
||||
// handle x-axis names
|
||||
class TimeScaleDraw: public QwtScaleDraw
|
||||
{
|
||||
public:
|
||||
TimeScaleDraw(const QDateTime &base):
|
||||
baseTime(base) {
|
||||
format = "MMM d";
|
||||
}
|
||||
virtual QwtText label(double v) const
|
||||
{
|
||||
QDateTime upTime = baseTime.addDays((int)v);
|
||||
return upTime.toString(format);
|
||||
}
|
||||
private:
|
||||
QDateTime baseTime;
|
||||
QString format;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class PerfPlot : public QwtPlot
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QwtPlotGrid *grid;
|
||||
QwtPlotCurve *STScurve, *LTScurve, *SBcurve;
|
||||
int days;
|
||||
QDateTime startDate;
|
||||
StressCalculator *_sc;
|
||||
int xmin, xmax;
|
||||
|
||||
public:
|
||||
|
||||
double getSTS(int i) { return STScurve->y(i - xmin); }
|
||||
double getLTS(int i) { return LTScurve->y(i - xmin); }
|
||||
double getSB(int i) { return SBcurve->y(i - xmin); }
|
||||
int n(void) { return days; }
|
||||
int max(void) { return xmax; }
|
||||
int min(void) { return xmin; }
|
||||
|
||||
QDateTime getStartDate(void) { return startDate; }
|
||||
QDateTime getEndDate(void) { return startDate.addDays(days); }
|
||||
|
||||
|
||||
PerfPlot();
|
||||
|
||||
void setStressCalculator(StressCalculator *sc);
|
||||
|
||||
void plot();
|
||||
void resize(int newmin, int newmax);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _GC_PerfPlot_h
|
||||
|
||||
222
src/PerformanceManagerWindow.cpp
Normal file
222
src/PerformanceManagerWindow.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
|
||||
#include "PerformanceManagerWindow.h"
|
||||
#include "PerfPlot.h"
|
||||
#include "StressCalculator.h"
|
||||
#include "RideItem.h"
|
||||
|
||||
|
||||
|
||||
PerformanceManagerWindow::PerformanceManagerWindow()
|
||||
|
||||
{
|
||||
days = count = 0;
|
||||
sc = NULL;
|
||||
|
||||
settings = GetApplicationSettings();
|
||||
|
||||
QVBoxLayout *vlayout = new QVBoxLayout;
|
||||
|
||||
QHBoxLayout *PMPickerLayout = new QHBoxLayout;
|
||||
|
||||
QLabel *PMSTSLabel = new QLabel(settings->value(GC_STS_ACRONYM,"STS").toString() + ":", this);
|
||||
PMSTSValue = new QLineEdit("0");
|
||||
PMSTSValue->setReadOnly(true);
|
||||
PMPickerLayout->addWidget(PMSTSLabel);
|
||||
PMPickerLayout->addWidget(PMSTSValue);
|
||||
|
||||
QLabel *PMLTSLabel = new QLabel(settings->value(GC_LTS_ACRONYM,"LTS").toString() + ":", this);
|
||||
|
||||
PMLTSValue = new QLineEdit("0");
|
||||
PMLTSValue->setReadOnly(true);
|
||||
PMPickerLayout->addWidget(PMLTSLabel);
|
||||
PMPickerLayout->addWidget(PMLTSValue);
|
||||
|
||||
QLabel *PMSBLabel = new QLabel(settings->value(GC_SB_ACRONYM,"SB").toString() + ":", this);
|
||||
|
||||
PMSBValue = new QLineEdit("0");
|
||||
PMSBValue->setReadOnly(true);
|
||||
PMPickerLayout->addWidget(PMSBLabel);
|
||||
PMPickerLayout->addWidget(PMSBValue);
|
||||
|
||||
QLabel *PMDayLabel = new QLabel(tr("Day:"), this);
|
||||
PMDayValue = new QLineEdit(tr("no data"));
|
||||
PMPickerLayout->addWidget(PMDayLabel);
|
||||
PMPickerLayout->addWidget(PMDayValue);
|
||||
|
||||
// date range
|
||||
QHBoxLayout *dateRangeLayout = new QHBoxLayout;
|
||||
PMdateRangefrom = new QLineEdit("0");
|
||||
dateRangeLayout->addWidget(PMdateRangefrom);
|
||||
|
||||
PMleftSlider = new QSlider(Qt::Horizontal);
|
||||
PMleftSlider->setTickPosition(QSlider::TicksBelow);
|
||||
PMleftSlider->setTickInterval(1);
|
||||
PMleftSlider->setMinimum(0);
|
||||
dateRangeLayout->addWidget(PMleftSlider);
|
||||
|
||||
QLabel *dateRangeLabel = new QLabel(tr("to"),this);
|
||||
dateRangeLayout->addWidget(dateRangeLabel);
|
||||
|
||||
PMrightSlider = new QSlider(Qt::Horizontal);
|
||||
PMrightSlider->setTickPosition(QSlider::TicksBelow);
|
||||
PMrightSlider->setTickInterval(1);
|
||||
PMrightSlider->setMinimum(0);
|
||||
dateRangeLayout->addWidget(PMrightSlider);
|
||||
|
||||
PMdateRangeto = new QLineEdit("0");
|
||||
dateRangeLayout->addWidget(PMdateRangeto);
|
||||
|
||||
|
||||
perfplot = new PerfPlot();
|
||||
vlayout->addWidget(perfplot);
|
||||
vlayout->addLayout(dateRangeLayout);
|
||||
vlayout->addLayout(PMPickerLayout);
|
||||
setLayout(vlayout);
|
||||
|
||||
PMpicker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
|
||||
QwtPicker::PointSelection,
|
||||
QwtPicker::VLineRubberBand,
|
||||
QwtPicker::AlwaysOff, perfplot->canvas());
|
||||
PMpicker->setRubberBandPen(QColor(Qt::blue));
|
||||
|
||||
connect(PMpicker, SIGNAL(moved(const QPoint &)),
|
||||
SLOT(PMpickerMoved(const QPoint &)));
|
||||
|
||||
}
|
||||
|
||||
PerformanceManagerWindow::~PerformanceManagerWindow()
|
||||
{
|
||||
if (sc)
|
||||
delete sc;
|
||||
}
|
||||
|
||||
void PerformanceManagerWindow::replot(QDir home,QTreeWidgetItem *allRides)
|
||||
{
|
||||
|
||||
int newdays, endIndex;
|
||||
RideItem *firstRideItem;
|
||||
QDateTime now;
|
||||
|
||||
|
||||
// calculate the number of days to look at... for now
|
||||
// use first ride in allRides to today. When Season stuff is hooked
|
||||
// up, maybe use that, or will allRides reflect only current season?
|
||||
firstRideItem = (RideItem*) allRides->child(0);
|
||||
|
||||
if (firstRideItem) {
|
||||
|
||||
newdays = firstRideItem->dateTime.daysTo(now.currentDateTime());
|
||||
|
||||
if (newdays != days || allRides->childCount() != count ) {
|
||||
// days in allRides changed, so recalculate stress
|
||||
//
|
||||
bool firstrun = false;
|
||||
|
||||
/*
|
||||
fprintf(stderr,
|
||||
"PerformanceManagerWindow::replot: %d days from %s to %s\n",
|
||||
newdays,firstRideItem->dateTime.toString().toAscii().data(),
|
||||
now.currentDateTime().toString().toAscii().data());
|
||||
*/
|
||||
|
||||
if (days == 0)
|
||||
firstrun = true;
|
||||
|
||||
if (sc) delete sc;
|
||||
|
||||
sc = new StressCalculator(
|
||||
firstRideItem->dateTime,
|
||||
now.currentDateTime(),
|
||||
(settings->value(GC_INITIAL_STS)).toInt(),
|
||||
(settings->value(GC_INITIAL_LTS)).toInt(),
|
||||
(settings->value(GC_STS_DAYS,7)).toInt(),
|
||||
(settings->value(GC_LTS_DAYS,42)).toInt());
|
||||
|
||||
sc->calculateStress(this,home.absolutePath(),allRides);
|
||||
|
||||
perfplot->setStressCalculator(sc);
|
||||
|
||||
endIndex = sc->n();
|
||||
|
||||
|
||||
PMleftSlider->setMaximum(endIndex);
|
||||
|
||||
PMrightSlider->setMaximum(endIndex);
|
||||
|
||||
PMdateRangefrom->setValidator(new QIntValidator(PMleftSlider->minimum(),
|
||||
PMleftSlider->maximum(),PMleftSlider));
|
||||
PMdateRangeto->setValidator(new QIntValidator(PMrightSlider->minimum(),
|
||||
PMrightSlider->maximum(),PMrightSlider));
|
||||
|
||||
|
||||
setPMSliderDates();
|
||||
|
||||
|
||||
if (firstrun) {
|
||||
// connect slider change handlers only once
|
||||
connect(PMleftSlider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(setPMSizeFromSlider()));
|
||||
connect(PMrightSlider, SIGNAL(valueChanged(int)),
|
||||
this, SLOT(setPMSizeFromSlider()));
|
||||
|
||||
// set slider values only on the first time
|
||||
// set left slider to last 6 months
|
||||
if (newdays > 182)
|
||||
PMleftSlider->setValue(newdays - 182);
|
||||
else
|
||||
PMleftSlider->setValue(0);
|
||||
|
||||
PMrightSlider->setValue(endIndex);
|
||||
}
|
||||
days = newdays;
|
||||
count = allRides->childCount();
|
||||
}
|
||||
perfplot->plot();
|
||||
}
|
||||
}
|
||||
|
||||
// performance manager picker callback
|
||||
void
|
||||
PerformanceManagerWindow::PMpickerMoved(const QPoint &pos)
|
||||
{
|
||||
double day = perfplot->invTransform(QwtPlot::xBottom, pos.x());
|
||||
QDateTime date;
|
||||
double sts, lts, sb;
|
||||
|
||||
if (day >= perfplot->min() && day < perfplot->max()) {
|
||||
// set the date string
|
||||
PMDayValue->setText(perfplot->getStartDate().addDays(day).toString());
|
||||
|
||||
|
||||
sts = perfplot->getSTS(day);
|
||||
QString STSlabel = QString("%1").arg(sts);
|
||||
PMSTSValue->setText(STSlabel);
|
||||
|
||||
lts = perfplot->getLTS(day);
|
||||
QString LTSlabel = QString("%1").arg(lts);
|
||||
PMLTSValue->setText(LTSlabel);
|
||||
|
||||
sb = perfplot->getSB(day);
|
||||
QString SBlabel = QString("%1").arg(sb);
|
||||
PMSBValue->setText(SBlabel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PerformanceManagerWindow::setPMSizeFromSlider()
|
||||
{
|
||||
perfplot->resize(PMleftSlider->value(),PMrightSlider->value());
|
||||
setPMSliderDates();
|
||||
}
|
||||
|
||||
void
|
||||
PerformanceManagerWindow::setPMSliderDates()
|
||||
{
|
||||
PMdateRangefrom->setText(
|
||||
perfplot->getStartDate().addDays(PMleftSlider->value()).toString("MMM d yyyy"));
|
||||
PMdateRangeto->setText(
|
||||
perfplot->getEndDate().addDays(PMrightSlider->value() - perfplot->n()).toString("MMM d yyyy"));
|
||||
}
|
||||
|
||||
73
src/PerformanceManagerWindow.h
Normal file
73
src/PerformanceManagerWindow.h
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2009 Eric Murray (ericm@lne.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _GC_PerformanceManagerWindow_h
|
||||
#define _GC_PerformanceManagerWindow_h 1
|
||||
|
||||
#include <QtGui>
|
||||
#include <QSlider>
|
||||
|
||||
#include <qwt_plot_picker.h>
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
class AllPlot;
|
||||
class QwtPlotPanner;
|
||||
class QwtPlotZoomer;
|
||||
class QSlider;
|
||||
class RideItem;
|
||||
class PerfPlot;
|
||||
class StressCalculator;
|
||||
|
||||
|
||||
class PerformanceManagerWindow : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
PerformanceManagerWindow (void);
|
||||
~PerformanceManagerWindow (void);
|
||||
void replot(QDir home, QTreeWidgetItem *allRides);
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
void PMpickerMoved(const QPoint &pos);
|
||||
void setPMSizeFromSlider();
|
||||
|
||||
protected:
|
||||
int days, count;
|
||||
StressCalculator *sc;
|
||||
|
||||
PerfPlot *perfplot;
|
||||
QLineEdit *PMSTSValue;
|
||||
QLineEdit *PMLTSValue;
|
||||
QLineEdit *PMSBValue;
|
||||
QLineEdit *PMDayValue;
|
||||
QwtPlotPicker *PMpicker;
|
||||
QLineEdit *PMdateRangefrom, *PMdateRangeto;
|
||||
QSlider *PMleftSlider, *PMrightSlider;
|
||||
boost::shared_ptr<QSettings> settings;
|
||||
|
||||
void setPMSliderDates();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -36,6 +36,16 @@
|
||||
#define GC_CRANKLENGTH "crankLength"
|
||||
#define GC_BIKESCOREDAYS "bikeScoreDays"
|
||||
#define GC_BIKESCOREMODE "bikeScoreMode"
|
||||
#define GC_INITIAL_LTS "initialLTS"
|
||||
#define GC_INITIAL_STS "initialSTS"
|
||||
#define GC_LTS_DAYS "LTSdays"
|
||||
#define GC_STS_DAYS "STSdays"
|
||||
#define GC_STS_NAME "STSname"
|
||||
#define GC_STS_ACRONYM "STS"
|
||||
#define GC_LTS_NAME "LTSname"
|
||||
#define GC_LTS_ACRONYM "LTS"
|
||||
#define GC_SB_NAME "SBname"
|
||||
#define GC_SB_ACRONYM "SB"
|
||||
|
||||
#include <QSettings>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
248
src/StressCalculator.cpp
Normal file
248
src/StressCalculator.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
|
||||
#include <StressCalculator.h>
|
||||
#include "RideMetric.h"
|
||||
#include "RideItem.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QProgressDialog>
|
||||
|
||||
StressCalculator::StressCalculator (
|
||||
QDateTime startDate,
|
||||
QDateTime endDate,
|
||||
double initialSTS = 0,
|
||||
double initialLTS = 0,
|
||||
int shortTermDays = 7,
|
||||
int longTermDays = 42) :
|
||||
startDate(startDate), endDate(endDate), shortTermDays(shortTermDays),
|
||||
longTermDays(longTermDays),
|
||||
initialSTS(initialSTS), initialLTS(initialLTS), lastDaysIndex(-1)
|
||||
{
|
||||
days = startDate.daysTo(endDate);
|
||||
// make vectors 1 larger in case there is a ride for today.
|
||||
// see calculateStress()
|
||||
stsvalues.resize(days+1);
|
||||
ltsvalues.resize(days+1);
|
||||
sbvalues.resize(days+1);
|
||||
xdays.resize(days+1);
|
||||
list.resize(days+1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
double StressCalculator::max(void) {
|
||||
double max = 0.0;
|
||||
for(int i = 0; i < days; i++) {
|
||||
if (stsvalues[i] > max) max = stsvalues[i];
|
||||
if (ltsvalues[i] > max) max = ltsvalues[i]; // unlikely..
|
||||
if (sbvalues[i] > max) max = sbvalues[i]; // really unlikely.
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
|
||||
double StressCalculator::min(void) {
|
||||
double min = 100.0;
|
||||
for(int i = 0; i < days; i++) {
|
||||
if (sbvalues[i] < min) min = sbvalues[i];
|
||||
if (ltsvalues[i] < min) min = ltsvalues[i]; // unlikely..
|
||||
if (stsvalues[i] < min) min = stsvalues[i]; // really unlikely
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void StressCalculator::calculateStress(QWidget *mw,
|
||||
QString homePath, QTreeWidgetItem * rides)
|
||||
{
|
||||
double bs;
|
||||
QSharedPointer<QProgressDialog> progress;
|
||||
int endingOffset = 0;
|
||||
bool aborted = false;
|
||||
bool showProgress = false;
|
||||
RideItem *item;
|
||||
|
||||
|
||||
// set up cache file
|
||||
QString cachePath = homePath + "/" + "stress.cache";
|
||||
QFile cacheFile(cachePath);
|
||||
QMap<QString, float> cache;
|
||||
|
||||
if (cacheFile.exists() && cacheFile.size() > 0) {
|
||||
if (cacheFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
fprintf(stderr,"reading stress cache file\n");
|
||||
QTextStream in(&cacheFile);
|
||||
QString line, date, bs;
|
||||
while(! in.atEnd()) {
|
||||
line = in.readLine();
|
||||
date = line.section(',',0,0);
|
||||
bs = line.section(',',1,1);
|
||||
cache[date] = bs.toFloat();
|
||||
}
|
||||
cacheFile.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// set up progress bar only if no cache file
|
||||
progress = QSharedPointer<QProgressDialog>(new
|
||||
QProgressDialog(QString(tr("Computing stress.\n")),
|
||||
tr("Abort"),0,days,mw));
|
||||
endingOffset = progress->labelText().size();
|
||||
showProgress = true;
|
||||
}
|
||||
|
||||
QString ridedatestring;
|
||||
item = (RideItem*) rides->child(0);
|
||||
for (int i = 0; i < rides->childCount(); ++i) {
|
||||
item = (RideItem*) rides->child(i);
|
||||
|
||||
// calculate using rides within date range
|
||||
if (item->dateTime.daysTo(startDate) <= 0 &&
|
||||
item->dateTime.daysTo(endDate) >= 0) { // inclusive of end date
|
||||
|
||||
|
||||
RideMetric *m;
|
||||
ridedatestring = item->dateTime.toString();
|
||||
|
||||
bs = 0.0;
|
||||
|
||||
if (showProgress) {
|
||||
QString existing = progress->labelText();
|
||||
existing.chop(progress->labelText().size() - endingOffset);
|
||||
progress->setLabelText( existing +
|
||||
QString(tr("Processing %1...")).arg(item->fileName));
|
||||
}
|
||||
|
||||
// get new value if not in cache
|
||||
if (cache.contains(ridedatestring)) {
|
||||
bs = cache[ridedatestring];
|
||||
}
|
||||
else {
|
||||
item->htmlSummary(); // compute metrics
|
||||
|
||||
if ((m = item->metrics.value("skiba_bike_score")) &&
|
||||
m->value(true)) {
|
||||
bs = m->value(true);
|
||||
}
|
||||
cache[ridedatestring] = bs;
|
||||
}
|
||||
|
||||
addRideData(bs,item->dateTime);
|
||||
|
||||
|
||||
// check progress
|
||||
if (showProgress) {
|
||||
QCoreApplication::processEvents();
|
||||
if (progress->wasCanceled()) {
|
||||
aborted = true;
|
||||
goto done;
|
||||
}
|
||||
// set progress from 0 to days
|
||||
progress->setValue(startDate.daysTo(item->dateTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
// fill in any days from last ride up to YESTERDAY but not today.
|
||||
// we want to show todays ride if there is a ride but don't fill in
|
||||
// a zero for it if there is no ride
|
||||
if (item->dateTime.daysTo(endDate) > 0)
|
||||
{
|
||||
/*
|
||||
fprintf(stderr,"filling in up to date = %s\n",
|
||||
endDate.toString().toAscii().data());
|
||||
*/
|
||||
|
||||
addRideData(0.0,endDate.addDays(-1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// there was a ride for today, increment the count so
|
||||
// we will show it:
|
||||
days++;
|
||||
}
|
||||
|
||||
done:
|
||||
if (!aborted) {
|
||||
// write cache file
|
||||
if (cacheFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
cacheFile.resize(0); // truncate
|
||||
QTextStream out(&cacheFile);
|
||||
QMap<QString, float>::const_iterator i = cache.constBegin();
|
||||
while (i != cache.constEnd()) {
|
||||
out << i.key() << "," << i.value() << endl;
|
||||
++i;
|
||||
}
|
||||
cacheFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate each day's STS and LTS. The daily BS values are in
|
||||
* the list. if there aren't enough days in the list yet, we fake
|
||||
* the missing days using the supplied initial value for each day.
|
||||
* STS and LTS are calculated up to but not including todays' ride.
|
||||
* if there are two rides per day the second one is added to the first
|
||||
* so the BS/day is correct.
|
||||
*/
|
||||
void StressCalculator::addRideData(double BS, QDateTime rideDate) {
|
||||
int daysIndex = startDate.daysTo(rideDate);
|
||||
|
||||
/*
|
||||
fprintf(stderr,"addRideData date = %s\n",
|
||||
rideDate.toString().toAscii().data());
|
||||
*/
|
||||
|
||||
|
||||
// fill in any missing days before today
|
||||
int d;
|
||||
for (d = lastDaysIndex + 1; d < daysIndex ; d++) {
|
||||
list[d] = 0.0; // no ride
|
||||
calculate(d);
|
||||
}
|
||||
// do this ride (may be more than one ride in a day)
|
||||
list[daysIndex] += BS;
|
||||
calculate(daysIndex);
|
||||
lastDaysIndex = daysIndex;
|
||||
|
||||
// fprintf(stderr,"addRideData (%.2f, %d)\n",BS,daysIndex);
|
||||
}
|
||||
|
||||
void StressCalculator::calculate(int daysIndex) {
|
||||
int i;
|
||||
double sum;
|
||||
// LTS
|
||||
sum = 0.0;
|
||||
if (daysIndex < longTermDays - 1) {
|
||||
// fake the first N days using the initial value
|
||||
sum = initialLTS * (longTermDays - (daysIndex + 1));
|
||||
i = 0;
|
||||
}
|
||||
else { i = daysIndex - (longTermDays - 1); }
|
||||
// sum the real values
|
||||
for (; i <= daysIndex; i++)
|
||||
sum += list[i];
|
||||
ltsvalues[daysIndex] = sum / longTermDays;
|
||||
|
||||
// STS
|
||||
sum = 0.0;
|
||||
if (daysIndex < shortTermDays - 1) {
|
||||
// fake the first N days using the initial value
|
||||
sum = initialSTS * (shortTermDays - (daysIndex + 1));
|
||||
i = 0;
|
||||
}
|
||||
else { i = daysIndex - (shortTermDays - 1); }
|
||||
// sum the real values
|
||||
for (; i <= daysIndex; i++)
|
||||
sum += list[i];
|
||||
stsvalues[daysIndex] = sum / shortTermDays;
|
||||
|
||||
// SB (stress balance) long term - short term
|
||||
sbvalues[daysIndex] = ltsvalues[daysIndex] - stsvalues[daysIndex] ;
|
||||
|
||||
// xdays
|
||||
xdays[daysIndex] = daysIndex+1;
|
||||
}
|
||||
74
src/StressCalculator.h
Normal file
74
src/StressCalculator.h
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
|
||||
#ifndef _GC_StressCalculator_h
|
||||
#define _GC_StressCalculator_h 1
|
||||
|
||||
|
||||
/* STS = Short Term Stress.. default 7 days
|
||||
* LTS = Long Term Stress.. default 42 days
|
||||
* SB = stress balance... negative is stressed
|
||||
*/
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
#include <QList>
|
||||
#include <QDateTime>
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
class StressCalculator:public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
int days; // number of days to calculate stress for
|
||||
QDateTime startDate, endDate; // start date
|
||||
int shortTermDays;
|
||||
int longTermDays;
|
||||
double initialSTS;
|
||||
double initialLTS;
|
||||
|
||||
int lastDaysIndex;
|
||||
|
||||
// graph axis arrays
|
||||
QVector<double> stsvalues;
|
||||
QVector<double> ltsvalues;
|
||||
QVector<double> sbvalues;
|
||||
QVector<double> xdays;
|
||||
// averaging array
|
||||
QVector<double> list;
|
||||
|
||||
void calculate(int daysIndex);
|
||||
void addRideData(double BS, QDateTime rideDate);
|
||||
|
||||
public:
|
||||
|
||||
StressCalculator(QDateTime startDate, QDateTime endDate,
|
||||
double initialSTS, double initialLTS,
|
||||
int shortTermDays, int longTermDays);
|
||||
|
||||
void calculateStress(QWidget *mw, QString homePath,
|
||||
QTreeWidgetItem * rides);
|
||||
|
||||
// x axes:
|
||||
double *getSTSvalues() { return stsvalues.data(); }
|
||||
double *getLTSvalues() { return ltsvalues.data(); }
|
||||
double *getSBvalues() { return sbvalues.data(); }
|
||||
// y axis
|
||||
double *getDays() { return xdays.data(); }
|
||||
int n() { return days; }
|
||||
|
||||
// for scaling
|
||||
double min(void);
|
||||
double max(void);
|
||||
|
||||
// for scale
|
||||
QDateTime getStartDate(void) { return startDate; }
|
||||
QDateTime getEndDate(void) { return startDate.addDays(days); }
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // _GC_StressCalculator_h
|
||||
10
src/src.pro
10
src/src.pro
@@ -92,7 +92,10 @@ HEADERS += \
|
||||
WeeklySummaryWindow.h \
|
||||
ManualRideFile.h \
|
||||
ManualRideDialog.h \
|
||||
RideCalendar.h
|
||||
RideCalendar.h \
|
||||
PerfPlot.h \
|
||||
StressCalculator.h \
|
||||
PerformanceManagerWindow.h
|
||||
|
||||
SOURCES += \
|
||||
AllPlot.cpp \
|
||||
@@ -145,7 +148,10 @@ SOURCES += \
|
||||
WeeklySummaryWindow.cpp \
|
||||
ManualRideFile.cpp \
|
||||
ManualRideDialog.cpp \
|
||||
RideCalendar.cpp
|
||||
RideCalendar.cpp \
|
||||
PerfPlot.cpp \
|
||||
StressCalculator.cpp \
|
||||
PerformanceManagerWindow.cpp
|
||||
|
||||
RESOURCES = application.qrc
|
||||
|
||||
|
||||
Reference in New Issue
Block a user