add performance monitor

This commit is contained in:
Eric Murray
2009-09-23 17:13:57 -07:00
committed by Sean Rhea
parent abf4b74cc3
commit c28591142b
13 changed files with 953 additions and 2 deletions

View File

@@ -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();

View File

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

View File

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

View File

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

View File

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

View 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"));
}

View 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

View File

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

View File

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