More configurable realtime plot

Allow data smoothing, which is particularly useful
during bike fit sessions to watch trends rather than
micro changes.

Also allow the user to select which data series to
plot. This allows you to open multiple plots for
each series.
This commit is contained in:
Mark Liversedge
2012-01-03 14:49:41 +00:00
parent cee35abb1e
commit 17705c1eae
4 changed files with 352 additions and 11 deletions

View File

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

View File

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

View File

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

View File

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