mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Add speed and cadence to the plot and automatically adjust y-axis based on
which lines are displayed.
This commit is contained in:
@@ -31,13 +31,14 @@ static const inline double
|
||||
max(double a, double b) { if (a > b) return a; else return b; }
|
||||
|
||||
AllPlot::AllPlot() :
|
||||
hrArray(NULL), wattsArray(NULL), timeArray(NULL), smooth(30)
|
||||
hrArray(NULL), wattsArray(NULL),
|
||||
speedArray(NULL), cadArray(NULL),
|
||||
timeArray(NULL), smooth(30)
|
||||
{
|
||||
insertLegend(new QwtLegend(), QwtPlot::BottomLegend);
|
||||
setCanvasBackground(Qt::white);
|
||||
|
||||
setAxisTitle(xBottom, "Time (minutes)");
|
||||
setAxisTitle(yLeft, "Power / HR");
|
||||
|
||||
wattsCurve = new QwtPlotCurve("Power");
|
||||
wattsCurve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
||||
@@ -49,6 +50,16 @@ AllPlot::AllPlot() :
|
||||
hrCurve->setPen(QPen(Qt::blue));
|
||||
hrCurve->attach(this);
|
||||
|
||||
speedCurve = new QwtPlotCurve("Speed");
|
||||
speedCurve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
||||
speedCurve->setPen(QPen(Qt::green));
|
||||
speedCurve->attach(this);
|
||||
|
||||
cadCurve = new QwtPlotCurve("Cadence");
|
||||
cadCurve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
||||
cadCurve->setPen(QPen(Qt::cyan));
|
||||
cadCurve->attach(this);
|
||||
|
||||
grid = new QwtPlotGrid();
|
||||
grid->enableX(false);
|
||||
QPen gridPen;
|
||||
@@ -58,33 +69,42 @@ AllPlot::AllPlot() :
|
||||
}
|
||||
|
||||
struct DataPoint {
|
||||
double time, hr, watts;
|
||||
DataPoint(double t, double h, double w) : time(t), hr(h), watts(w) {}
|
||||
double time, hr, watts, speed, cad;
|
||||
DataPoint(double t, double h, double w, double s, double c) :
|
||||
time(t), hr(h), watts(w), speed(s), cad(c) {}
|
||||
};
|
||||
|
||||
void
|
||||
AllPlot::recalc()
|
||||
{
|
||||
int rideTimeSecs = (int) ceil(timeArray[arrayLength - 1]);
|
||||
double ymax = 0.0;
|
||||
double totalWatts = 0.0;
|
||||
double totalHr = 0.0;
|
||||
double totalSpeed = 0.0;
|
||||
double totalCad = 0.0;
|
||||
QList<DataPoint*> list;
|
||||
int i = 0;
|
||||
double *smoothWatts = new double[rideTimeSecs + 1];
|
||||
double *smoothHr = new double[rideTimeSecs + 1];
|
||||
double *smoothSpeed = new double[rideTimeSecs + 1];
|
||||
double *smoothCad = new double[rideTimeSecs + 1];
|
||||
double *smoothTime = new double[rideTimeSecs + 1];
|
||||
for (int secs = 0; secs < smooth; ++secs) {
|
||||
smoothWatts[secs] = 0.0;
|
||||
smoothHr[secs] = 0.0;
|
||||
smoothSpeed[secs] = 0.0;
|
||||
smoothCad[secs] = 0.0;
|
||||
smoothTime[secs] = secs / 60.0;
|
||||
}
|
||||
for (int secs = smooth; secs <= rideTimeSecs; ++secs) {
|
||||
while ((i < arrayLength) && (timeArray[i] <= secs)) {
|
||||
DataPoint *dp =
|
||||
new DataPoint(timeArray[i], hrArray[i], wattsArray[i]);
|
||||
new DataPoint(timeArray[i], hrArray[i], wattsArray[i],
|
||||
speedArray[i], cadArray[i]);
|
||||
totalWatts += wattsArray[i];
|
||||
totalHr += hrArray[i];
|
||||
totalSpeed += speedArray[i];
|
||||
totalCad += cadArray[i];
|
||||
list.append(dp);
|
||||
++i;
|
||||
}
|
||||
@@ -93,6 +113,8 @@ AllPlot::recalc()
|
||||
list.removeFirst();
|
||||
totalWatts -= dp->watts;
|
||||
totalHr -= dp->hr;
|
||||
totalSpeed -= dp->speed;
|
||||
totalCad -= dp->cad;
|
||||
delete dp;
|
||||
}
|
||||
// TODO: this is wrong. We should do a weighted average over the
|
||||
@@ -100,44 +122,80 @@ AllPlot::recalc()
|
||||
if (list.empty()) {
|
||||
smoothWatts[secs] = 0.0;
|
||||
smoothHr[secs] = 0.0;
|
||||
smoothSpeed[secs] = 0.0;
|
||||
smoothCad[secs] = 0.0;
|
||||
}
|
||||
else {
|
||||
smoothWatts[secs] = totalWatts / list.size();
|
||||
if (smoothWatts[secs] > ymax)
|
||||
ymax = smoothWatts[secs];
|
||||
smoothHr[secs] = totalHr / list.size();
|
||||
if (smoothHr[secs] > ymax)
|
||||
ymax = smoothHr[secs];
|
||||
smoothSpeed[secs] = totalSpeed / list.size();
|
||||
smoothCad[secs] = totalCad / list.size();
|
||||
}
|
||||
smoothTime[secs] = secs / 60.0;
|
||||
}
|
||||
wattsCurve->setData(smoothTime, smoothWatts, rideTimeSecs + 1);
|
||||
hrCurve->setData(smoothTime, smoothHr, rideTimeSecs + 1);
|
||||
speedCurve->setData(smoothTime, smoothSpeed, rideTimeSecs + 1);
|
||||
cadCurve->setData(smoothTime, smoothCad, rideTimeSecs + 1);
|
||||
setAxisScale(xBottom, 0.0, smoothTime[rideTimeSecs]);
|
||||
setAxisScale(yLeft, 0.0, ymax + 30);
|
||||
setYMax();
|
||||
|
||||
replot();
|
||||
delete [] smoothWatts;
|
||||
delete [] smoothHr;
|
||||
delete [] smoothSpeed;
|
||||
delete [] smoothCad;
|
||||
delete [] smoothTime;
|
||||
}
|
||||
|
||||
void
|
||||
AllPlot::setYMax()
|
||||
{
|
||||
double ymax = 0;
|
||||
QString ylabel = "";
|
||||
if (wattsCurve->isVisible()) {
|
||||
ymax = max(ymax, wattsCurve->maxYValue());
|
||||
ylabel += QString((ylabel == "") ? "" : " / ") + "Watts";
|
||||
}
|
||||
if (hrCurve->isVisible()) {
|
||||
ymax = max(ymax, hrCurve->maxYValue());
|
||||
ylabel += QString((ylabel == "") ? "" : " / ") + "BPM";
|
||||
}
|
||||
if (speedCurve->isVisible()) {
|
||||
ymax = max(ymax, speedCurve->maxYValue());
|
||||
ylabel += QString((ylabel == "") ? "" : " / ") + "MPH";
|
||||
}
|
||||
if (cadCurve->isVisible()) {
|
||||
ymax = max(ymax, cadCurve->maxYValue());
|
||||
ylabel += QString((ylabel == "") ? "" : " / ") + "RPM";
|
||||
}
|
||||
setAxisScale(yLeft, 0.0, ymax * 1.1);
|
||||
setAxisTitle(yLeft, ylabel);
|
||||
}
|
||||
|
||||
void
|
||||
AllPlot::setData(RawFile *raw)
|
||||
{
|
||||
delete [] hrArray;
|
||||
delete [] wattsArray;
|
||||
delete [] hrArray;
|
||||
delete [] speedArray;
|
||||
delete [] cadArray;
|
||||
delete [] timeArray;
|
||||
setTitle(raw->startTime.toString(GC_DATETIME_FORMAT));
|
||||
hrArray = new double[raw->points.size()];
|
||||
wattsArray = new double[raw->points.size()];
|
||||
hrArray = new double[raw->points.size()];
|
||||
speedArray = new double[raw->points.size()];
|
||||
cadArray = new double[raw->points.size()];
|
||||
timeArray = new double[raw->points.size()];
|
||||
arrayLength = 0;
|
||||
QListIterator<RawFilePoint*> i(raw->points);
|
||||
while (i.hasNext()) {
|
||||
RawFilePoint *point = i.next();
|
||||
timeArray[arrayLength] = point->secs;
|
||||
hrArray[arrayLength] = max(0, point->hr);
|
||||
wattsArray[arrayLength] = max(0, point->watts);
|
||||
hrArray[arrayLength] = max(0, point->hr);
|
||||
speedArray[arrayLength] = max(0, point->mph);
|
||||
cadArray[arrayLength] = max(0, point->cad);
|
||||
++arrayLength;
|
||||
}
|
||||
recalc();
|
||||
@@ -148,6 +206,7 @@ AllPlot::showPower(int state)
|
||||
{
|
||||
assert(state != Qt::PartiallyChecked);
|
||||
wattsCurve->setVisible(state == Qt::Checked);
|
||||
setYMax();
|
||||
replot();
|
||||
}
|
||||
|
||||
@@ -156,6 +215,25 @@ AllPlot::showHr(int state)
|
||||
{
|
||||
assert(state != Qt::PartiallyChecked);
|
||||
hrCurve->setVisible(state == Qt::Checked);
|
||||
setYMax();
|
||||
replot();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlot::showSpeed(int state)
|
||||
{
|
||||
assert(state != Qt::PartiallyChecked);
|
||||
speedCurve->setVisible(state == Qt::Checked);
|
||||
setYMax();
|
||||
replot();
|
||||
}
|
||||
|
||||
void
|
||||
AllPlot::showCad(int state)
|
||||
{
|
||||
assert(state != Qt::PartiallyChecked);
|
||||
cadCurve->setVisible(state == Qt::Checked);
|
||||
setYMax();
|
||||
replot();
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ class AllPlot : public QwtPlot
|
||||
|
||||
QwtPlotCurve *wattsCurve;
|
||||
QwtPlotCurve *hrCurve;
|
||||
QwtPlotCurve *speedCurve;
|
||||
QwtPlotCurve *cadCurve;
|
||||
|
||||
AllPlot();
|
||||
|
||||
@@ -46,6 +48,8 @@ class AllPlot : public QwtPlot
|
||||
|
||||
void showPower(int state);
|
||||
void showHr(int state);
|
||||
void showSpeed(int state);
|
||||
void showCad(int state);
|
||||
void showGrid(int state);
|
||||
void setSmoothing(int value);
|
||||
|
||||
@@ -55,12 +59,15 @@ class AllPlot : public QwtPlot
|
||||
|
||||
double *hrArray;
|
||||
double *wattsArray;
|
||||
double *speedArray;
|
||||
double *cadArray;
|
||||
double *timeArray;
|
||||
int arrayLength;
|
||||
|
||||
int smooth;
|
||||
|
||||
void recalc();
|
||||
void setYMax();
|
||||
};
|
||||
|
||||
#endif // _GC_AllPlot_h
|
||||
|
||||
@@ -102,6 +102,14 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
showHr->setCheckState(Qt::Checked);
|
||||
showLayout->addWidget(showHr);
|
||||
|
||||
QCheckBox *showSpeed = new QCheckBox("Speed", window);
|
||||
showSpeed->setCheckState(Qt::Checked);
|
||||
showLayout->addWidget(showSpeed);
|
||||
|
||||
QCheckBox *showCad = new QCheckBox("Cadence", window);
|
||||
showCad->setCheckState(Qt::Checked);
|
||||
showLayout->addWidget(showCad);
|
||||
|
||||
QHBoxLayout *smoothLayout = new QHBoxLayout;
|
||||
QLabel *smoothLabel = new QLabel(tr("Smoothing (secs)"), window);
|
||||
smoothLineEdit = new QLineEdit(window);
|
||||
@@ -148,6 +156,10 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
allPlot, SLOT(showPower(int)));
|
||||
connect(showHr, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showHr(int)));
|
||||
connect(showSpeed, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showSpeed(int)));
|
||||
connect(showCad, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showCad(int)));
|
||||
connect(showGrid, SIGNAL(stateChanged(int)),
|
||||
allPlot, SLOT(showGrid(int)));
|
||||
connect(smoothSlider, SIGNAL(valueChanged(int)),
|
||||
|
||||
Reference in New Issue
Block a user