Find Ascents

.. another method for find intervals to find the ascents
   in a ride. Uses the hysteresis as defined in options
   which means a climb ends if there is a descent of that
   or more (by default only 3m).
This commit is contained in:
Mark Liversedge
2013-11-12 18:09:24 +00:00
parent bef02d6783
commit 5054a9bc90
2 changed files with 114 additions and 3 deletions

View File

@@ -17,6 +17,7 @@
*/
#include "AddIntervalDialog.h"
#include "Settings.h"
#include "Athlete.h"
#include "Context.h"
#include "IntervalItem.h"
@@ -53,6 +54,11 @@ AddIntervalDialog::AddIntervalDialog(Context *context) :
methodButtonGroup->addButton(methodBestPower);
methodRadios->addWidget(methodBestPower);
methodClimb = new QRadioButton(tr("Ascent (elevation)"));
methodClimb->setChecked(false);
methodButtonGroup->addButton(methodClimb);
methodRadios->addWidget(methodClimb);
methodWPrime = new QRadioButton(tr("W' (Energy)"));
methodWPrime->setChecked(false);
methodButtonGroup->addButton(methodWPrime);
@@ -189,6 +195,24 @@ AddIntervalDialog::AddIntervalDialog(Context *context) :
intervalWPrimeWidget->hide();
mainLayout->addWidget(intervalWPrimeWidget);
intervalClimbWidget = new QWidget();
QHBoxLayout *intervalClimbLayout = new QHBoxLayout;
QLabel *intervalClimbLabel = new QLabel(tr("Minimum Ascent: "), this);
intervalClimbLayout->addStretch();
intervalClimbLayout->addWidget(intervalClimbLabel);
altSpinBox = new QDoubleSpinBox(this);
altSpinBox->setDecimals(1);
altSpinBox->setRange(10, 5000);
altSpinBox->setValue(100);
altSpinBox->setSuffix(" metres");
altSpinBox->setSingleStep(10);
altSpinBox->setAlignment(Qt::AlignRight);
intervalClimbLayout->addWidget(altSpinBox);
intervalClimbLayout->addStretch();
intervalClimbWidget->setLayout(intervalClimbLayout);
intervalClimbWidget->hide();
mainLayout->addWidget(intervalClimbWidget);
QHBoxLayout *findbuttonLayout = new QHBoxLayout;
findbuttonLayout->addStretch();
createButton = new QPushButton(tr("&Find"), this);
@@ -221,6 +245,7 @@ AddIntervalDialog::AddIntervalDialog(Context *context) :
connect(methodFirst, SIGNAL(clicked()), this, SLOT(methodFirstClicked()));
connect(methodBestPower, SIGNAL(clicked()), this, SLOT(methodBestPowerClicked()));
connect(methodWPrime, SIGNAL(clicked()), this, SLOT(methodWPrimeClicked()));
connect(methodClimb, SIGNAL(clicked()), this, SLOT(methodClimbClicked()));
connect(peakPowerStandard, SIGNAL(clicked()), this, SLOT(peakPowerStandardClicked()));
connect(peakPowerCustom, SIGNAL(clicked()), this, SLOT(peakPowerCustomClicked()));
connect(typeTime, SIGNAL(clicked()), this, SLOT(typeTimeClicked()));
@@ -239,6 +264,7 @@ AddIntervalDialog::methodFirstClicked()
clearResultsTable(resultsTable);
intervalPeakPowerWidget->hide();
intervalClimbWidget->hide();
intervalWPrimeWidget->hide();
intervalTypeWidget->show();
if (typeDistance->isChecked())
@@ -256,6 +282,7 @@ AddIntervalDialog::methodBestPowerClicked()
intervalWPrimeWidget->hide();
intervalPeakPowerWidget->show();
intervalClimbWidget->hide();
if (peakPowerCustom->isChecked())
peakPowerCustomClicked();
else
@@ -266,6 +293,7 @@ void
AddIntervalDialog::methodWPrimeClicked()
{
intervalPeakPowerWidget->hide();
intervalClimbWidget->hide();
intervalTypeWidget->hide();
intervalDistanceWidget->hide();
intervalTimeWidget->hide();
@@ -273,6 +301,18 @@ AddIntervalDialog::methodWPrimeClicked()
intervalWPrimeWidget->show();
}
void
AddIntervalDialog::methodClimbClicked()
{
intervalClimbWidget->show();
intervalPeakPowerWidget->hide();
intervalTypeWidget->hide();
intervalDistanceWidget->hide();
intervalTimeWidget->hide();
intervalCountWidget->hide();
intervalWPrimeWidget->hide();
}
void
AddIntervalDialog::peakPowerStandardClicked()
{
@@ -416,6 +456,73 @@ AddIntervalDialog::createClicked()
findFirsts(byTime, ride, (byTime?windowSizeSecs:windowSizeMeters), maxIntervals, results);
}
// FIND ASCENTS
if (methodClimb->isChecked()) {
// we need altitude and more than 3 data points
if (ride->areDataPresent()->alt == false || ride->dataPoints().count() < 3) return;
double hysteresis = appsettings->value(NULL, GC_ELEVATION_HYSTERESIS).toDouble();
if (hysteresis <= 0.1) hysteresis = 3.00;
// first apply hysteresis
QVector<QPoint> points;
int index=0;
int runningAlt = ride->dataPoints().first()->alt;
foreach(RideFilePoint *p, ride->dataPoints()) {
// up
if (p->alt > (runningAlt + hysteresis)) {
runningAlt = p->alt;
points << QPoint(index, runningAlt);
}
// down
if (p->alt < (runningAlt - hysteresis)) {
runningAlt = p->alt;
points << QPoint(index, runningAlt);
}
index++;
}
// now find peaks and troughs in the point data
// there will only be ups and downs, no flats
QVector<QPoint> peaks;
for(int i=1; i<(points.count()-1); i++) {
// peak
if (points[i].y() > points[i-1].y() &&
points[i].y() > points[i+1].y()) peaks << points[i];
// trough
if (points[i].y() < points[i-1].y() &&
points[i].y() < points[i+1].y()) peaks << points[i];
}
// now run through looking for diffs > requested
int counter=0;
for (int i=0; i<(peaks.count()-1); i++) {
int ascent = 0; // ascent found in meters
if ((ascent=peaks[i+1].y() - peaks[i].y()) >= altSpinBox->value()) {
// found one so increment from zero
counter++;
// we have a winner...
struct AddedInterval add;
add.start = ride->dataPoints()[peaks[i].x()]->secs;
add.stop = ride->dataPoints()[peaks[i+1].x()]->secs;
add.name = QString("Climb #%1 (%2m)").arg(counter)
.arg(ascent);
results << add;
}
}
}
// FIND W' BAL DROPS
if (methodWPrime->isChecked()) {
WPrime wp;

View File

@@ -58,6 +58,7 @@ class AddIntervalDialog : public QDialog
void methodFirstClicked();
void methodBestPowerClicked();
void methodWPrimeClicked();
void methodClimbClicked();
void peakPowerStandardClicked();
void peakPowerCustomClicked();
void typeTimeClicked();
@@ -66,12 +67,15 @@ class AddIntervalDialog : public QDialog
private:
Context *context;
QWidget *intervalMethodWidget, *intervalPeakPowerWidget, *intervalTypeWidget, *intervalTimeWidget, *intervalDistanceWidget, *intervalCountWidget, *intervalWPrimeWidget;
QWidget *intervalMethodWidget, *intervalPeakPowerWidget, *intervalTypeWidget,
*intervalTimeWidget, *intervalDistanceWidget, *intervalClimbWidget,
*intervalCountWidget, *intervalWPrimeWidget;
QHBoxLayout *intervalPeakPowerTypeLayout;
QPushButton *createButton, *addButton;
QDoubleSpinBox *hrsSpinBox, *minsSpinBox, *secsSpinBox, *countSpinBox,*kmsSpinBox, *msSpinBox, *kjSpinBox;
QRadioButton *methodFirst, *methodBestPower, *methodWPrime;
QDoubleSpinBox *hrsSpinBox, *minsSpinBox, *secsSpinBox, *altSpinBox,
*countSpinBox,*kmsSpinBox, *msSpinBox, *kjSpinBox;
QRadioButton *methodFirst, *methodBestPower, *methodWPrime, *methodClimb;
QRadioButton *typeDistance, *typeTime, *peakPowerStandard, *peakPowerCustom;
QTableWidget *resultsTable;
};