From 4eff6b6eb9db78b348feec2c2971ddb3c2f756e2 Mon Sep 17 00:00:00 2001 From: "Sean C. Rhea" Date: Thu, 7 Sep 2006 18:50:19 +0000 Subject: [PATCH] Add speed and cadence to the plot and automatically adjust y-axis based on which lines are displayed. --- src/gui/AllPlot.cpp | 106 +++++++++++++++++++++++++++++++++++------ src/gui/AllPlot.h | 7 +++ src/gui/MainWindow.cpp | 12 +++++ 3 files changed, 111 insertions(+), 14 deletions(-) diff --git a/src/gui/AllPlot.cpp b/src/gui/AllPlot.cpp index ddb3ce539..cc8df18bd 100644 --- a/src/gui/AllPlot.cpp +++ b/src/gui/AllPlot.cpp @@ -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 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 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(); } diff --git a/src/gui/AllPlot.h b/src/gui/AllPlot.h index 831eb08e3..77a30e717 100644 --- a/src/gui/AllPlot.h +++ b/src/gui/AllPlot.h @@ -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 diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 0ffcfa959..40826b94a 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -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)),