diff --git a/src/RealtimePlot.cpp b/src/RealtimePlot.cpp index fea7b474c..d87f4ceb7 100644 --- a/src/RealtimePlot.cpp +++ b/src/RealtimePlot.cpp @@ -78,7 +78,15 @@ void RealtimeHrData::addData(double v) { hrData[hrCur++] = v; if (hrCur==MAXSAMP //void RealtimeLodData::addData(double v) { lodData[lodCur++] = v; if (lodCur==50) lodCur=0; } -RealtimePlot::RealtimePlot() : pwrCurve(NULL) +RealtimePlot::RealtimePlot() : + pwrCurve(NULL), + showPowerState(Qt::Checked), + showPow30sState(Qt::Checked), + showHrState(Qt::Checked), + showSpeedState(Qt::Checked), + showCadState(Qt::Checked), + showAltState(Qt::Checked), + smooth(0) { setInstanceName("Realtime Plot"); @@ -233,3 +241,63 @@ RealtimePlot::configChanged() spdpen.setWidth(width); spdCurve->setPen(spdpen); } + +void +RealtimePlot::showPower(int state) +{ + showPowerState = state; + pwrCurve->setVisible(state == Qt::Checked); + enableAxis(yLeft, showAltState == Qt::Checked || showPowerState == Qt::Checked || showPow30sState == Qt::Checked); + replot(); +} + +void +RealtimePlot::showPow30s(int state) +{ + showPow30sState = state; + pwr30Curve->setVisible(state == Qt::Checked); + enableAxis(yLeft, showAltState == Qt::Checked || showPowerState == Qt::Checked || showPow30sState == Qt::Checked); + replot(); +} + +void +RealtimePlot::showHr(int state) +{ + showHrState = state; + hrCurve->setVisible(state == Qt::Checked); + enableAxis(yRight, showCadState == Qt::Checked || showHrState == Qt::Checked); + replot(); +} + +void +RealtimePlot::showSpeed(int state) +{ + showSpeedState = state; + spdCurve->setVisible(state == Qt::Checked); + enableAxis(yRight2, state == Qt::Checked); + replot(); +} + +void +RealtimePlot::showCad(int state) +{ + showCadState = state; + cadCurve->setVisible(state == Qt::Checked); + enableAxis(yRight, showCadState == Qt::Checked || showHrState == Qt::Checked); + replot(); +} + +void +RealtimePlot::showAlt(int state) +{ + showAltState = state; + altPwrCurve->setVisible(state == Qt::Checked); + enableAxis(yLeft, showAltState == Qt::Checked || showPowerState == Qt::Checked || showPow30sState == Qt::Checked); + replot(); +} + +void +RealtimePlot::setSmoothing(int value) +{ + smooth = value; +} diff --git a/src/RealtimePlot.h b/src/RealtimePlot.h index 8e4bcefde..81d79dd30 100644 --- a/src/RealtimePlot.h +++ b/src/RealtimePlot.h @@ -167,6 +167,13 @@ class RealtimePlot : public QwtPlot QwtPlotCurve *hrCurve; //QwtPlotCurve *lodCurve; + int showPowerState; + int showPow30sState; + int showHrState; + int showSpeedState; + int showCadState; + int showAltState; + #if 0 // power stores last 30 seconds for 30 second rolling avg, all else // just the last 30 seconds @@ -183,9 +190,17 @@ class RealtimePlot : public QwtPlot //RealtimeLodData lodData; RealtimePlot(); + int smooth; public slots: void configChanged(); + void showPower(int state); + void showPow30s(int state); + void showHr(int state); + void showSpeed(int state); + void showCad(int state); + void showAlt(int state); + void setSmoothing(int value); }; diff --git a/src/RealtimePlotWindow.cpp b/src/RealtimePlotWindow.cpp index 8db042597..c6f2531b8 100644 --- a/src/RealtimePlotWindow.cpp +++ b/src/RealtimePlotWindow.cpp @@ -20,20 +20,82 @@ #include "RealtimePlotWindow.h" RealtimePlotWindow::RealtimePlotWindow(MainWindow *mainWindow) : - GcWindow(mainWindow), mainWindow(mainWindow) + GcWindow(mainWindow), mainWindow(mainWindow), active(false) { setContentsMargins(0,0,0,0); setInstanceName("RT Plot"); - setControls(NULL); setProperty("color", GColor(CRIDEPLOTBACKGROUND)); + QWidget *c = new QWidget; + QVBoxLayout *cl = new QVBoxLayout(c); + setControls(c); + + // setup the controls + QLabel *showLabel = new QLabel(tr("Show"), c); + cl->addWidget(showLabel); + + showHr = new QCheckBox(tr("Heart Rate"), this); + showHr->setCheckState(Qt::Checked); + cl->addWidget(showHr); + + showSpeed = new QCheckBox(tr("Speed"), this); + showSpeed->setCheckState(Qt::Checked); + cl->addWidget(showSpeed); + + showCad = new QCheckBox(tr("Cadence"), this); + showCad->setCheckState(Qt::Checked); + cl->addWidget(showCad); + + showPower = new QCheckBox(tr("Power"), this); + showPower->setCheckState(Qt::Checked); + cl->addWidget(showPower); + + showAlt = new QCheckBox(tr("Alternate Power"), this); + showAlt->setCheckState(Qt::Checked); + cl->addWidget(showAlt); + + showPow30s = new QCheckBox(tr("30s Power"), this); + showPow30s->setCheckState(Qt::Checked); + cl->addWidget(showPow30s); + + QLabel *smoothLabel = new QLabel(tr("Smoothing (5Hz Samples)"), this); + smoothLineEdit = new QLineEdit(this); + smoothLineEdit->setFixedWidth(40); + + cl->addWidget(smoothLabel); + cl->addWidget(smoothLineEdit); + smoothSlider = new QSlider(Qt::Horizontal); + smoothSlider->setTickPosition(QSlider::TicksBelow); + smoothSlider->setTickInterval(10); + smoothSlider->setMinimum(1); + smoothSlider->setMaximum(150); + smoothLineEdit->setValidator(new QIntValidator(smoothSlider->minimum(), + smoothSlider->maximum(), + smoothLineEdit)); + cl->addWidget(smoothSlider); + cl->addStretch(); + QVBoxLayout *layout = new QVBoxLayout(this); rtPlot = new RealtimePlot(); layout->addWidget(rtPlot); + // common controls + connect(showPower, SIGNAL(stateChanged(int)), this, SLOT(setShowPower(int))); + connect(showPow30s, SIGNAL(stateChanged(int)), this, SLOT(setShowPow30s(int))); + connect(showHr, SIGNAL(stateChanged(int)), this, SLOT(setShowHr(int))); + connect(showSpeed, SIGNAL(stateChanged(int)), this, SLOT(setShowSpeed(int))); + connect(showCad, SIGNAL(stateChanged(int)), this, SLOT(setShowCad(int))); + connect(showAlt, SIGNAL(stateChanged(int)), this, SLOT(setShowAlt(int))); + connect(smoothSlider, SIGNAL(valueChanged(int)), this, SLOT(setSmoothingFromSlider())); + connect(smoothLineEdit, SIGNAL(editingFinished()), this, SLOT(setSmoothingFromLineEdit())); + // get updates.. connect(mainWindow, SIGNAL(telemetryUpdate(RealtimeData)), this, SLOT(telemetryUpdate(RealtimeData))); + // lets initialise all the smoothing variables + hrtot = hrindex = cadtot = cadindex = spdtot = spdindex = alttot = altindex = powtot = powindex = 0; + for(int i=0; i<150; i++) powHist[i] = altHist[i] = spdHist[i] = cadHist[i] = hrHist[i] = 0; + // set to zero telemetryUpdate(RealtimeData()); } @@ -41,13 +103,17 @@ RealtimePlotWindow::RealtimePlotWindow(MainWindow *mainWindow) : void RealtimePlotWindow::start() { - //resetValues(); + // lets initialise all the smoothing variables + hrtot = hrindex = cadtot = cadindex = spdtot = spdindex = alttot = altindex = powtot = powindex = 0; + for(int i=0; i<150; i++) powHist[i] = altHist[i] = spdHist[i] = cadHist[i] = hrHist[i] = 0; } void RealtimePlotWindow::stop() { - //resetValues(); + // lets initialise all the smoothing variables + hrtot = hrindex = cadtot = cadindex = spdtot = spdindex = alttot = altindex = powtot = powindex = 0; + for(int i=0; i<150; i++) powHist[i] = altHist[i] = spdHist[i] = cadHist[i] = hrHist[i] = 0; } void @@ -58,11 +124,146 @@ RealtimePlotWindow::pause() void RealtimePlotWindow::telemetryUpdate(RealtimeData rtData) { - rtPlot->pwrData.addData(rtData.value(RealtimeData::Watts)); - rtPlot->altPwrData.addData(rtData.value(RealtimeData::AltWatts)); - rtPlot->pwr30Data.addData(rtData.value(RealtimeData::Watts)); - rtPlot->cadData.addData(rtData.value(RealtimeData::Cadence)); - rtPlot->spdData.addData(rtData.value(RealtimeData::Speed)); - rtPlot->hrData.addData(rtData.value(RealtimeData::HeartRate)); + // lets apply smoothing if we have to + if (rtPlot->smooth) { + + // Heartrate + double hr = rtData.value(RealtimeData::HeartRate); + hrtot += hr; + hrtot -= hrHist[hrindex]; + hrHist[hrindex] = hr; + hrindex++; + if (hrindex >= rtPlot->smooth) hrindex = 0; + hr = hrtot / rtPlot->smooth; + rtPlot->hrData.addData(hr); + + // Speed + double spd= rtData.value(RealtimeData::Speed); + spdtot += spd; spdtot -= spdHist[spdindex]; spdHist[spdindex] = spd; + spdindex++; if (spdindex >= rtPlot->smooth) spdindex = 0; + spd = spdtot / rtPlot->smooth; + rtPlot->spdData.addData(spd); + + // Power + double pow = rtData.value(RealtimeData::Watts); + powtot += pow; powtot -= powHist[powindex]; powHist[powindex] = pow; + powindex++; if (powindex >= rtPlot->smooth) powindex = 0; + pow = powtot / rtPlot->smooth; + rtPlot->pwrData.addData(pow); + + // Alternate Power + double alt = rtData.value(RealtimeData::AltWatts); + alttot += alt; alttot -= altHist[altindex]; altHist[altindex] = alt; + altindex++; if (altindex >= rtPlot->smooth) altindex = 0; + alt = alttot / rtPlot->smooth; + rtPlot->altPwrData.addData(alt); + + // Cadence + double cad = rtData.value(RealtimeData::Cadence); + cadtot += cad; cadtot -= cadHist[cadindex]; cadHist[cadindex] = cad; + cadindex++; if (cadindex >= rtPlot->smooth) cadindex = 0; + cad = cadtot / rtPlot->smooth; + rtPlot->cadData.addData(cad); + + // its smoothed to 30s anyway + rtPlot->pwr30Data.addData(rtData.value(RealtimeData::Watts)); + + } else { + rtPlot->pwrData.addData(rtData.value(RealtimeData::Watts)); + rtPlot->altPwrData.addData(rtData.value(RealtimeData::AltWatts)); + rtPlot->pwr30Data.addData(rtData.value(RealtimeData::Watts)); + rtPlot->cadData.addData(rtData.value(RealtimeData::Cadence)); + rtPlot->spdData.addData(rtData.value(RealtimeData::Speed)); + rtPlot->hrData.addData(rtData.value(RealtimeData::HeartRate)); + } rtPlot->replot(); // redraw } + +void +RealtimePlotWindow::setSmoothingFromSlider() +{ + // active tells us we have been triggered by + // the setSmoothingFromLineEdit which will also + // recalculates smoothing, lets not double up... + if (active) return; + else active = true; + + if (rtPlot->smooth != smoothSlider->value()) { + setSmoothing(smoothSlider->value()); + smoothLineEdit->setText(QString("%1").arg(rtPlot->smooth)); + } + active = false; +} + +void +RealtimePlotWindow::setSmoothingFromLineEdit() +{ + // active tells us we have been triggered by + // the setSmoothingFromSlider which will also + // recalculates smoothing, lets not double up... + if (active) return; + else active = true; + + int value = smoothLineEdit->text().toInt(); + if (value != rtPlot->smooth) { + smoothSlider->setValue(value); + setSmoothing(value); + } + active = false; +} + +void +RealtimePlotWindow::setShowPow30s(int value) +{ + showPow30s->setChecked(value); + rtPlot->showPow30s(value); + rtPlot->replot(); +} + +void +RealtimePlotWindow::setShowPower(int value) +{ + showPower->setChecked(value); + rtPlot->showPower(value); + rtPlot->replot(); +} + +void +RealtimePlotWindow::setShowHr(int value) +{ + showHr->setChecked(value); + rtPlot->showHr(value); + rtPlot->replot(); +} + +void +RealtimePlotWindow::setShowSpeed(int value) +{ + showSpeed->setChecked(value); + rtPlot->showSpeed(value); + rtPlot->replot(); +} + +void +RealtimePlotWindow::setShowCad(int value) +{ + showCad->setChecked(value); + rtPlot->showCad(value); + rtPlot->replot(); +} + +void +RealtimePlotWindow::setShowAlt(int value) +{ + showAlt->setChecked(value); + rtPlot->showAlt(value); + rtPlot->replot(); +} + +void +RealtimePlotWindow::setSmoothing(int value) +{ + hrtot = hrindex = cadtot = cadindex = spdtot = spdindex = alttot = altindex = powtot = powindex = 0; + smoothSlider->setValue(value); + rtPlot->setSmoothing(value); +} diff --git a/src/RealtimePlotWindow.h b/src/RealtimePlotWindow.h index e02d7f9f6..d49bf7610 100644 --- a/src/RealtimePlotWindow.h +++ b/src/RealtimePlotWindow.h @@ -37,10 +37,27 @@ class RealtimePlotWindow : public GcWindow Q_OBJECT G_OBJECT + Q_PROPERTY(int showHr READ isShowHr WRITE setShowHr USER true) + Q_PROPERTY(int showSpeed READ isShowSpeed WRITE setShowSpeed USER true) + Q_PROPERTY(int showCad READ isShowCad WRITE setShowCad USER true) + Q_PROPERTY(int showAlt READ isShowAlt WRITE setShowAlt USER true) + Q_PROPERTY(int showPower READ isShowPower WRITE setShowPower USER true) + Q_PROPERTY(int showPow30s READ isShowPow30s WRITE setShowPow30s USER true) + Q_PROPERTY(int smoothing READ smoothing WRITE setSmoothing USER true) + public: RealtimePlotWindow(MainWindow *mainWindow); + // get properties - the setters are below + int isShowHr() const { return showHr->checkState(); } + int isShowSpeed() const { return showSpeed->checkState(); } + int isShowCad() const { return showCad->checkState(); } + int isShowAlt() const { return showAlt->checkState(); } + int isShowPower() const { return showPower->checkState(); } + int isShowPow30s() const { return showPow30s->checkState(); } + int smoothing() const { return smoothSlider->value(); } + public slots: // trap signals @@ -49,10 +66,50 @@ class RealtimePlotWindow : public GcWindow void stop(); void pause(); + // set properties + void setSmoothingFromSlider(); + void setSmoothingFromLineEdit(); + void setShowPower(int state); + void setShowPow30s(int state); + void setShowHr(int state); + void setShowSpeed(int state); + void setShowCad(int state); + void setShowAlt(int state); + void setSmoothing(int value); + private: MainWindow *mainWindow; RealtimePlot *rtPlot; + bool active; + + // Common controls + QGridLayout *controlsLayout; + QCheckBox *showHr; + QCheckBox *showSpeed; + QCheckBox *showCad; + QCheckBox *showAlt; + QCheckBox *showPower; + QCheckBox *showPow30s; + QSlider *smoothSlider; + QLineEdit *smoothLineEdit; + + // for smoothing charts + double powHist[150]; + double powtot; + int powindex; + double altHist[150]; + double alttot; + int altindex; + double spdHist[150]; + double spdtot; + int spdindex; + double cadHist[150]; + double cadtot; + int cadindex; + double hrHist[150]; + double hrtot; + int hrindex; }; #endif // _GC_RealtimePlotWindow_h