From 5054a9bc904b0d6c19cbd2706bd4fcb766f1dc55 Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Tue, 12 Nov 2013 18:09:24 +0000 Subject: [PATCH] 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). --- src/AddIntervalDialog.cpp | 107 ++++++++++++++++++++++++++++++++++++++ src/AddIntervalDialog.h | 10 ++-- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/src/AddIntervalDialog.cpp b/src/AddIntervalDialog.cpp index 63a9fef76..d8b74b899 100644 --- a/src/AddIntervalDialog.cpp +++ b/src/AddIntervalDialog.cpp @@ -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 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 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; diff --git a/src/AddIntervalDialog.h b/src/AddIntervalDialog.h index b8aaae61c..eb3febf86 100644 --- a/src/AddIntervalDialog.h +++ b/src/AddIntervalDialog.h @@ -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; };