diff --git a/src/HrPwPlot.cpp b/src/HrPwPlot.cpp index 663726f38..e6dbfff67 100644 --- a/src/HrPwPlot.cpp +++ b/src/HrPwPlot.cpp @@ -42,21 +42,14 @@ HrPwPlot::HrPwPlot(MainWindow *mainWindow, HrPwWindow *hrPwWindow) : QwtPlot(hrPwWindow), hrPwWindow(hrPwWindow), mainWindow(mainWindow), - bg(NULL), smooth(240), hrMin(50), + bg(NULL), delay(-1), + minHr(50), minWatt(50), maxWatt(500), settings(GC_SETTINGS_CO, GC_SETTINGS_APP), unit(settings.value(GC_UNIT)) { setCanvasBackground(Qt::white); setXTitle(); // Power (Watts) - // Heart Rate Curve - - hrCurves.resize(36); - for (int i = 0; i < 36; ++i) { - hrCurves[i] = new QwtPlotCurve; - hrCurves[i]->attach(this); - } - // Linear Regression Curve regCurve = new QwtPlotCurve("reg"); regCurve->setPen(QPen(GColor(CPLOTMARKER))); @@ -83,6 +76,14 @@ HrPwPlot::HrPwPlot(MainWindow *mainWindow, HrPwWindow *hrPwWindow) : hrStepCurve->setBrush(QBrush(hrColor)); hrStepCurve->attach(this); + // Heart Rate Curve + + hrCurves.resize(36); + for (int i = 0; i < 36; ++i) { + hrCurves[i] = new QwtPlotCurve; + hrCurves[i]->attach(this); + } + // Grid grid = new QwtPlotGrid(); grid->enableX(false); @@ -123,6 +124,7 @@ HrPwPlot::setAxisTitle(int axis, QString label) QwtPlot::setAxisFont(axis, stGiles); QwtPlot::setAxisTitle(axis, title); } + void HrPwPlot::recalc() { @@ -134,9 +136,12 @@ HrPwPlot::recalc() return; } + + + // Find Hr Delay //int delayori = findDelay(wattsArray, hrArray, rideTimeSecs/5); - int delay = 0; + //int delay = 0; // ------ smoothing ----- @@ -150,20 +155,21 @@ HrPwPlot::recalc() QVector smoothTime(rideTimeSecs + 1); int decal=0; - for (int secs = 0; ((secs < smooth) && (secs < rideTimeSecs)); ++secs) { + /*for (int secs = 0; ((secs < smooth) && (secs < rideTimeSecs)); ++secs) { smoothWatts[secs] = 0.0; smoothHr[secs] = 0.0; - } + }*/ //int interval = 0; + int smooth = hrPwWindow->smooth; - for (int secs = smooth; secs <= rideTimeSecs-delay; ++secs) { + for (int secs = smooth; secs <= rideTimeSecs; ++secs) { while ((i < arrayLength) && (timeArray[i] <= secs)) { DataPoint *dp = - new DataPoint(timeArray[i], hrArray[i+delay], wattsArray[i], interArray[i]); + new DataPoint(timeArray[i], hrArray[i], wattsArray[i], interArray[i]); totalWatts += wattsArray[i]; - totalHr += hrArray[i+delay]; + totalHr += hrArray[i]; list.append(dp); ++i; @@ -188,103 +194,116 @@ HrPwPlot::recalc() smoothTime[secs] = secs / 60.0; } - rideTimeSecs = rideTimeSecs-delay-decal; + rideTimeSecs = rideTimeSecs-decal; + smoothWatts.resize(rideTimeSecs); + smoothHr.resize(rideTimeSecs); + + // Clip to max + QVector clipWatts(rideTimeSecs); + QVector clipHr(rideTimeSecs); + + /*for (int secs = 0; secs < rideTimeSecs; ++secs) { + clipWatts[secs] = 0.0; + clipHr[secs] = 0.0; + }*/ + + decal = 0; + for (int secs = 0; secs < rideTimeSecs; ++secs) { + if (smoothHr[secs]>= minHr && smoothWatts[secs]>= minWatt && smoothWatts[secs]findDelay(smoothWatts, smoothHr, rideTimeSecs); - //delayori = delay; + if (delay == -1) + delay = hrPwWindow->findDelay(clipWatts, clipHr, clipWatts.size()); - //double rpente = pente(smoothWatts, smoothHr, rideTimeSecs); - //double rordonnee = ordonnee(smoothWatts, smoothHr, rideTimeSecs); - - // Applique le delai - QVector clipWatts(rideTimeSecs-delay); - QVector clipHr(rideTimeSecs-delay); + // Apply delay + QVector delayWatts(rideTimeSecs-delay); + QVector delayHr(rideTimeSecs-delay); for (int secs = 0; secs < rideTimeSecs-delay; ++secs) { - clipWatts[secs] = 0.0; - clipHr[secs] = 0.0; - } + delayWatts[secs] = clipWatts[secs]; + delayHr[secs] = clipHr[secs+delay]; + } + rideTimeSecs = rideTimeSecs-delay; - decal = 0; - for (int secs = 0; secs < rideTimeSecs-delay; ++secs) { - if (smoothHr[secs+delay]>= 50 && smoothWatts[secs]>= 80 && smoothWatts[secs]<500) { - clipWatts[secs-decal] = smoothWatts[secs]; - clipHr[secs-decal] = smoothHr[secs+delay]; - } - else - decal ++; - } - rideTimeSecs = rideTimeSecs-delay-decal; + double rpente = hrPwWindow->pente(delayWatts, delayHr, delayWatts.size()); + double rordonnee = hrPwWindow->ordonnee(delayWatts, delayHr, delayWatts.size()); + double maxr = hrPwWindow->corr(delayWatts, delayHr, delayWatts.size()); - // ----- limit points --- + // ----- limit plotted points --- // ---------------------- - int intpoints = 10;//(int)floor(rideTimeSecs/1000); + int intpoints = 10; // could be ride length dependent int nbpoints = (int)floor(rideTimeSecs/intpoints); - QVector finalWatts(nbpoints); - QVector finalHr(nbpoints); - - decal = 0; + QVector plotedWatts(nbpoints); + QVector plotedHr(nbpoints); for (int secs = 0; secs < nbpoints; ++secs) { - finalWatts[secs-decal] = clipWatts[secs*intpoints]; - finalHr[secs-decal] = clipHr[secs*intpoints]; + plotedWatts[secs] = clipWatts[secs*intpoints]; + plotedHr[secs] = clipHr[secs*intpoints]; } - nbpoints = nbpoints-decal; - - int nbpoints2 = (int)floor(nbpoints/36)+2; - double *finalWattsArray[36]; - double *finalHrArray[36]; + double *plotedWattsArray[36]; + double *plotedHrArray[36]; for (int i = 0; i < 36; ++i) { - finalWattsArray[i]= new double[nbpoints2]; - finalHrArray[i]= new double[nbpoints2]; + plotedWattsArray[i]= new double[nbpoints2]; + plotedHrArray[i]= new double[nbpoints2]; } for (int secs = 0; secs < nbpoints; ++secs) { for (int i = 0; i < 36; ++i) { if (secs >= i*nbpoints2 && secs< (i+1)*nbpoints2) { - finalWattsArray[i][secs-i*nbpoints2] = finalWatts[secs-i]; - finalHrArray[i][secs-i*nbpoints2] = finalHr[secs-i]; + plotedWattsArray[i][secs-i*nbpoints2] = plotedWatts[secs-i]; + plotedHrArray[i][secs-i*nbpoints2] = plotedHr[secs-i]; } } } - // Calcul corr - double maxr = hrPwWindow->corr(smoothWatts, smoothHr, rideTimeSecs); - for (int i = 0; i < 36; ++i) { if (nbpoints-i*nbpoints2>0) { - hrCurves[i]->setData(finalWattsArray[i], finalHrArray[i], (nbpoints-i*nbpoints2setData(plotedWattsArray[i], plotedHrArray[i], (nbpoints-i*nbpoints2setVisible(true); } else hrCurves[i]->setVisible(false); } - setAxisScale(xBottom, 0.0, 500); + setAxisScale(xBottom, 0.0, maxWatt); setYMax(); refreshZoneLabels(); QString labelp; - double rpente = hrPwWindow->pente(finalWatts, finalHr, nbpoints); - labelp.setNum(rpente); + + labelp.setNum(rpente, 'f', 3); QString labelo; - double rordonnee = hrPwWindow->ordonnee(finalWatts, finalHr, nbpoints); - labelo.setNum(rordonnee); + labelo.setNum(rordonnee, 'f', 1); QString labelr; - labelr.setNum(maxr); + labelr.setNum(maxr, 'f', 3); QString labeldelay; labeldelay.setNum(delay); - QwtText textr = QwtText(labelp+"*x+"+labelo+" : R "+labelr+" ("+labeldelay+")"); + + int power150 = (int)floor((150-rordonnee)/rpente); + QString labelpower150; + labelpower150.setNum(power150); + + QwtText textr = QwtText(labelp+"*x+"+labelo+" : R "+labelr+" ("+labeldelay+") \n Power@150:"+labelpower150+"W"); textr.setFont(QFont("Helvetica", 10, QFont::Bold)); textr.setColor(Qt::black); @@ -292,7 +311,7 @@ HrPwPlot::recalc() r_mrk1->setLineStyle(QwtPlotMarker::VLine); r_mrk1->setLabelAlignment(Qt::AlignRight | Qt::AlignTop); r_mrk1->setLinePen(QPen(Qt::black, 0, Qt::DashDotLine)); - double moyennewatt = hrPwWindow->moyenne(smoothWatts, rideTimeSecs); + double moyennewatt = hrPwWindow->moyenne(clipWatts, clipWatts.size()); r_mrk1->setValue(moyennewatt, 0.0); r_mrk1->setLabel(textr); @@ -300,13 +319,13 @@ HrPwPlot::recalc() r_mrk2->setLineStyle(QwtPlotMarker::HLine); r_mrk2->setLabelAlignment(Qt::AlignRight | Qt::AlignTop); r_mrk2->setLinePen(QPen(Qt::black, 0, Qt::DashDotLine)); - double moyennehr = hrPwWindow->moyenne(smoothHr, rideTimeSecs); + double moyennehr = hrPwWindow->moyenne(clipHr, clipHr.size()); r_mrk2->setValue(0.0,moyennehr); - addWattStepCurve(finalWatts, nbpoints); - addHrStepCurve(finalHr, nbpoints); + addWattStepCurve(clipWatts, clipWatts.size()); + addHrStepCurve(clipHr, clipHr.size()); - addRegLinCurve(finalHr, finalWatts, nbpoints); + addRegLinCurve(rpente, rordonnee); setJoinLine(joinLine); replot(); @@ -323,7 +342,7 @@ HrPwPlot::setYMax() //ylabel += QString((ylabel == "") ? "" : " / ") + "BPM"; } } - setAxisScale(yLeft, hrMin, ymax * 1.2); + setAxisScale(yLeft, minHr, ymax * 1.2); setAxisTitle(yLeft, tr("Heart Rate(BPM)")); } @@ -360,7 +379,7 @@ HrPwPlot::addWattStepCurve(QVector &finalWatts, int nbpoints) int high = low + 10; smoothWattsStep[t] = low; - smoothTimeStep[t] = hrMin; + smoothTimeStep[t] = minHr; while (low < high) { smoothTimeStep[t] += array[low++]/ nbpoints * 300; } @@ -415,13 +434,12 @@ HrPwPlot::addHrStepCurve(QVector &finalHr, int nbpoints) } void -HrPwPlot::addRegLinCurve(QVector &finalHr, QVector &finalWatts, int nbpoints) +HrPwPlot::addRegLinCurve( double rpente, double rordonnee) { double regWatts[] = {0, 0}; double regHr[] = {0, 500}; - double rpente = hrPwWindow->pente(finalWatts, finalHr, nbpoints); - double rordonnee = hrPwWindow->ordonnee(finalWatts, finalHr, nbpoints); - regWatts[0] = regHr[0]*rpente+rordonnee; + + regWatts[0] = regHr[0]*rpente+rordonnee; regWatts[1] = regHr[1]*rpente+rordonnee; regCurve->setData(regHr, regWatts, 2); @@ -468,7 +486,7 @@ HrPwPlot::setDataFromRide(RideItem *_rideItem) ++arrayLength; } - + delay = -1; recalc(); } } diff --git a/src/HrPwPlot.h b/src/HrPwPlot.h index 21f106d94..748662e6a 100644 --- a/src/HrPwPlot.h +++ b/src/HrPwPlot.h @@ -53,8 +53,6 @@ class HrPwPlot : public QwtPlot void setShadeZones(int); int isShadeZones() const; void refreshZoneLabels(); - - int smoothing() const { return smooth; } void setDataFromRide(RideItem *ride); void setJoinLine(bool value); void setAxisTitle(int axis, QString label); @@ -91,8 +89,10 @@ class HrPwPlot : public QwtPlot double *array; int arrayLength2; - int smooth; - int hrMin; + int delay; + int minHr; + int minWatt; + int maxWatt; QList zoneLabels; bool shade_zones; // whether power should be shaded @@ -103,7 +103,7 @@ class HrPwPlot : public QwtPlot void addWattStepCurve(QVector &finalWatts, int nbpoints); void addHrStepCurve(QVector &finalHr, int nbpoints); - void addRegLinCurve(QVector &finalHr, QVector &finalWatts, int nbpoints); + void addRegLinCurve(double rpente, double rordonnee); private: QSettings settings; diff --git a/src/HrPwWindow.cpp b/src/HrPwWindow.cpp index a1ba970e6..1a02091b9 100644 --- a/src/HrPwWindow.cpp +++ b/src/HrPwWindow.cpp @@ -79,6 +79,7 @@ HrPwWindow::HrPwWindow(MainWindow *mainWindow) : QWidget *c = new QWidget(this); setControls(c); QFormLayout *cl = new QFormLayout(c); + QLabel *delayLabel = new QLabel(tr("HR delay"), this); delayEdit = new QLineEdit(this); delayEdit->setFixedWidth(30); @@ -87,12 +88,29 @@ HrPwWindow::HrPwWindow(MainWindow *mainWindow) : delaySlider->setTickPosition(QSlider::TicksBelow); delaySlider->setTickInterval(1); delaySlider->setMinimum(1); - delaySlider->setMaximum(100);; + delaySlider->setMaximum(100); delayEdit->setValidator(new QIntValidator(delaySlider->minimum(), delaySlider->maximum(), delayEdit)); cl->addRow(delaySlider); + smooth = 240; + QLabel *smoothLabel = new QLabel(tr("Smooth"), this); + smoothEdit = new QLineEdit(this); + smoothEdit->setFixedWidth(30); + smoothEdit->setText(QString("%1").arg(smooth)); + cl->addRow(smoothLabel, smoothEdit); + smoothSlider = new QSlider(Qt::Horizontal); + smoothSlider->setTickPosition(QSlider::TicksBelow); + smoothSlider->setTickInterval(10); + smoothSlider->setMinimum(0); + smoothSlider->setMaximum(500); + smoothSlider->setValue(smooth); + smoothEdit->setValidator(new QIntValidator(smoothSlider->minimum(), + smoothSlider->maximum(), + smoothEdit)); + cl->addRow(smoothSlider); + joinlineCheckBox = new QCheckBox(this);; joinlineCheckBox->setText(tr("Join points")); joinlineCheckBox->setCheckState(Qt::Unchecked); @@ -110,7 +128,10 @@ HrPwWindow::HrPwWindow(MainWindow *mainWindow) : hrPwPlot, SLOT(pointHover(QwtPlotCurve*, int))); connect(joinlineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setJoinLineFromCheckBox())); connect(shadeZones, SIGNAL(stateChanged(int)), this, SLOT(setShadeZones())); - connect(delayEdit, SIGNAL(editingFinished()), this, SLOT(setSmoothingFromLineEdit())); + connect(smoothEdit, SIGNAL(editingFinished()), this, SLOT(setSmoothingFromLineEdit())); + connect(smoothSlider, SIGNAL(valueChanged(int)), this, SLOT(setSmoothingFromSlider())); + connect(delayEdit, SIGNAL(editingFinished()), this, SLOT(setDelayFromLineEdit())); + connect(delaySlider, SIGNAL(valueChanged(int)), this, SLOT(setDelayFromSlider())); //connect(mainWindow, SIGNAL(configChanged()), this, SLOT(configChanged())); connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected())); } @@ -130,6 +151,7 @@ HrPwWindow::rideSelected() void HrPwWindow::setData(RideItem *ride) { + setSmooth(240); hrPwPlot->setDataFromRide(ride); smallPlot->setData(ride); } @@ -152,14 +174,47 @@ HrPwWindow::setShadeZones() } } +void +HrPwWindow::setSmooth(int _smooth) +{ + smooth = _smooth; + smoothSlider->setValue(_smooth); + smoothEdit->setText(QString("%1").arg(_smooth)); + hrPwPlot->recalc(); +} + void HrPwWindow::setSmoothingFromLineEdit() { - int value = delayEdit->text().toInt(); - //if (value != allPlot->smoothing()) { - //allPlot->setSmoothing(value); - delaySlider->setValue(value); - //} + int _smooth = smoothEdit->text().toInt(); + setSmooth(_smooth); +} + +void +HrPwWindow::setSmoothingFromSlider() +{ + setSmooth(smoothSlider->value()); +} + +void +HrPwWindow::setDelayFromLineEdit() +{ + int delay = delayEdit->text().toInt(); + if (hrPwPlot->delay != delay) { + delaySlider->setValue(delay); + hrPwPlot->delay = delaySlider->value(); + hrPwPlot->recalc(); + } +} + +void +HrPwWindow::setDelayFromSlider() +{ + if (hrPwPlot->delay != delaySlider->value()) { + delayEdit->setText(QString("%1").arg(delaySlider->value())); + hrPwPlot->delay = delaySlider->value(); + hrPwPlot->recalc(); + } } int diff --git a/src/HrPwWindow.h b/src/HrPwWindow.h index dbc2f03ca..77656f562 100644 --- a/src/HrPwWindow.h +++ b/src/HrPwWindow.h @@ -59,13 +59,19 @@ class HrPwWindow : public GcWindow double corr(QVector &Xi, QVector &Yi,int n); double moyenne(QVector &Xi,int n); + int smooth; + public slots: void rideSelected(); protected slots: void setJoinLineFromCheckBox(); + void setDelayFromLineEdit(); + void setDelayFromSlider(); void setSmoothingFromLineEdit(); + void setSmoothingFromSlider(); void setShadeZones(); + void setSmooth(int); protected: MainWindow *mainWindow; @@ -80,6 +86,9 @@ class HrPwWindow : public GcWindow QSlider *delaySlider; QLineEdit *delayEdit; + QSlider *smoothSlider; + QLineEdit *smoothEdit; + private: // Maths functions used by the plots