diff --git a/src/FixSpeed.cpp b/src/FixSpeed.cpp new file mode 100644 index 000000000..729007c87 --- /dev/null +++ b/src/FixSpeed.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2015 Alejandro Martinez (amtriathlon@gmail.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "DataProcessor.h" +#include "Settings.h" +#include "Units.h" +#include "HelpWhatsThis.h" +#include +#include + +// Config widget used by the Preferences/Options config panes +class FixSpeed; +class FixSpeedConfig : public DataProcessorConfig +{ + Q_DECLARE_TR_FUNCTIONS(FixSpeedConfig) + + friend class ::FixSpeed; + protected: + QHBoxLayout *layout; + QLabel *maLabel; + QSpinBox *ma; + + public: + FixSpeedConfig(QWidget *parent) : DataProcessorConfig(parent) { + + HelpWhatsThis *help = new HelpWhatsThis(parent); + parent->setWhatsThis(help->getWhatsThisText(HelpWhatsThis::MenuBar_Edit_FixSpeed)); + + layout = new QHBoxLayout(this); + + layout->setContentsMargins(0,0,0,0); + setContentsMargins(0,0,0,0); + + maLabel = new QLabel(tr("Moving Average Seconds")); + + ma = new QSpinBox(); + ma->setMaximum(60); + ma->setMinimum(0); + ma->setSingleStep(1); + layout->addWidget(maLabel); + layout->addWidget(ma); + layout->addStretch(); + } + + //~FixSpeedConfig() {} // deliberately not declared since Qt will + // delete the widget and its children when + // the config pane is deleted + + QString explain() { + return tr("Some devices report sample speed not matching the " "change of distance, for example when using a footpod " + "for speed and GPS for distance.\n" + "This tool replaces the existing speed channel, or " "creates a new one if not present, based on travelled " + "distance\n\n" + "Moving Average Seconds parameter allows to set the " + "seconds of the MA filter to smooth speed spikes"); + } + + void readConfig() { + ma->setValue(appsettings->value(NULL, GC_DPFV_MA, "1").toInt()); + } + + void saveConfig() { + appsettings->setValue(GC_DPFV_MA, ma->value()); + } +}; + + +// RideFile Dataprocessor -- used to compute Speed from Distance +// travelled during each sample +// to ensure consistency. +// +class FixSpeed : public DataProcessor { + Q_DECLARE_TR_FUNCTIONS(FixSpeed) + + public: + FixSpeed() {} + ~FixSpeed() {} + + // the processor + bool postProcess(RideFile *, DataProcessorConfig* config); + + // the config widget + DataProcessorConfig* processorConfig(QWidget *parent) { + return new FixSpeedConfig(parent); + } + + // Localized Name + QString name() { + return (tr("Fix Speed from Distance")); + } +}; + +static bool FixSpeedAdded = DataProcessorFactory::instance().registerProcessor(QString("Fix Speed"), new FixSpeed()); + +bool +FixSpeed::postProcess(RideFile *ride, DataProcessorConfig *config=0) +{ + // get settings + int ma; + if (config == NULL) { // being called automatically + ma = appsettings->value(NULL, GC_DPFV_MA, "1").toInt(); + } else { // being called manually + ma = ((FixSpeedConfig*)(config))->ma->value(); + } + + // no dice if we don't have Distance + if (!ride->areDataPresent()->km) return false; + + if(ride->recIntSecs() == 0) return false; + + int rollingwindowsize = ma / ride->recIntSecs(); + if (rollingwindowsize < 1) rollingwindowsize = 1; + QVector rolling(rollingwindowsize); + int index = 0; + double sum = 0; + + // apply the change + bool changed=false; + + double secs = 0.0; + double km = 0.0; + for (int i=0; i< ride->dataPoints().count(); i++) { + + RideFilePoint *p = ride->dataPoints()[i]; + + // Estimate Speed from travelled distance + double kph = p->kph; + if (p->secs - secs > 0) kph = 3600 * (p->km - km) / (p->secs - secs); + else if (p->secs == 0) kph = 0; + + // compute rolling average for rollingwindowsize seconds + sum += kph; + sum -= rolling[index]; + rolling[index] = kph; + kph = sum/std::min(i+1, rollingwindowsize); + // move index on/round + index = (index >= rollingwindowsize-1) ? 0 : index+1; + + // If different enough, update + if (abs(kph - p->kph) > 10e-6) { + // ok, lets start a luw if not already changed + if (!changed) { + changed = true; + ride->command->startLUW("Fix Speed"); + } + ride->command->setPointValue(i, RideFile::kph, kph); + } + + // update accumulated time and distance + secs = p->secs; + km = p->km; + } + + if (changed) { + ride->setDataPresent(ride->kph, true); + ride->command->endLUW(); + return true; + } + + return false; +} diff --git a/src/HelpWhatsThis.cpp b/src/HelpWhatsThis.cpp index b0e57f8ce..99afece1c 100644 --- a/src/HelpWhatsThis.cpp +++ b/src/HelpWhatsThis.cpp @@ -149,6 +149,8 @@ HelpWhatsThis::getText(GCHelp chapter) { return text.arg("Menu%20Bar_Tools#tool-fix-hr-spikes").arg(tr("Fix HR Spikes")); case MenuBar_Edit_FixPowerSpikes: return text.arg("Menu%20Bar_Toolss#tool-fix-power-spikes").arg(tr("Fix Power Spikes")); + case MenuBar_Edit_FixSpeed: + return text.arg("Menu%20Bar_Toolss#tool-fix-speed").arg(tr("Fix Speed")); case MenuBar_View: return text.arg("Menu%20Bar_View").arg(tr("Options to show/hide views (e.g. Sidebar) as well as adding charts and resetting chart layouts to factory settings")); diff --git a/src/HelpWhatsThis.h b/src/HelpWhatsThis.h index c74246597..2301fbf29 100644 --- a/src/HelpWhatsThis.h +++ b/src/HelpWhatsThis.h @@ -76,6 +76,7 @@ Q_OBJECT MenuBar_Edit_FixGPSErrors, MenuBar_Edit_FixHRSpikes, MenuBar_Edit_FixPowerSpikes, + MenuBar_Edit_FixSpeed, MenuBar_View, MenuBar_Help, diff --git a/src/Settings.h b/src/Settings.h index 87605465b..3ce5cc061 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -161,6 +161,7 @@ #define GC_DPFHRS_MAX "dataprocess/fixhrspikes/max" #define GC_DPDP_BIKEWEIGHT "dataprocess/fixderivepower/bikewheight" #define GC_DPDP_CRR "dataprocess/fixderivepower/crr" +#define GC_DPFV_MA "dataprocess/fixspeed/ma" // device Configurations NAME/SPEC/TYPE/DEFI/DEFR all get a number appended // to them to specify which configured device i.e. devices1 ... devicesn where diff --git a/src/src.pro b/src/src.pro index a642d3647..d1284df7b 100644 --- a/src/src.pro +++ b/src/src.pro @@ -613,6 +613,7 @@ SOURCES += \ FixGPS.cpp \ FixMoxy.cpp \ FixPower.cpp \ + FixSpeed.cpp \ FixSpikes.cpp \ FixTorque.cpp \ FixHRSpikes.cpp \