diff --git a/src/FixDerivePower.cpp b/src/FixDerivePower.cpp new file mode 100644 index 000000000..377ca160b --- /dev/null +++ b/src/FixDerivePower.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 "LTMOutliers.h" +#include "Settings.h" +#include "Units.h" +#include +#include + +// Config widget used by the Preferences/Options config panes +class FixDerivePower; +class FixDerivePowerConfig : public DataProcessorConfig +{ + Q_DECLARE_TR_FUNCTIONS(FixDerivePowerConfig) + + friend class ::FixDerivePower; + protected: + //QHBoxLayout *layout; + //QLabel *taLabel; + //QLineEdit *ta; + + public: + FixDerivePowerConfig(QWidget *parent) : DataProcessorConfig(parent) { + + //layout = new QHBoxLayout(this); + + //layout->setContentsMargins(0,0,0,0); + //setContentsMargins(0,0,0,0); + + //taLabel = new QLabel(tr("Torque Adjust")); + + //ta = new QLineEdit(); + + //layout->addWidget(taLabel); + //layout->addWidget(ta); + //layout->addStretch(); + } + + //~FixDerivePowerConfig() {} // deliberately not declared since Qt will delete + // the widget and its children when the config pane is deleted + + QString explain() { + return(QString(tr("Derive estimated power data based on speed/elevation/weight etc"))); + } + + void readConfig() { + //ta->setText(appsettings->value(NULL, GC_DPTA, "0 nm").toString()); + } + + void saveConfig() { + //appsettings->setValue(GC_DPTA, ta->text()); + } +}; + + +// RideFile Dataprocessor -- used to handle gaps in recording +// by inserting interpolated/zero samples +// to ensure dataPoints are contiguous in time +// +class FixDerivePower : public DataProcessor { + Q_DECLARE_TR_FUNCTIONS(FixDerivePower) + + public: + FixDerivePower() {} + ~FixDerivePower() {} + + // the processor + bool postProcess(RideFile *, DataProcessorConfig* config); + + // the config widget + DataProcessorConfig* processorConfig(QWidget *parent) { + return new FixDerivePowerConfig(parent); + } + + // Localized Name + QString name() { + return (tr("Estimate Power Values")); + } +}; + +static bool FixDerivePowerAdded = DataProcessorFactory::instance().registerProcessor(QString("Estimate Power Values"), new FixDerivePower()); + +bool +FixDerivePower::postProcess(RideFile *ride, DataProcessorConfig *config=0) +{ + + // Power Estimation Constants + double hRider = 1.7 ; //Height in m + double M = ride->getWeight(); //Weight kg + double MBik = 9.5; //Bike weight kg + double T = 15; //Temp degC in not in ride data + double W = 0; //Assume no wind + double cCad=.002; + double afCd = 0.62; + double afSin = 0.89; + double afCm = 1.025; + double afCdBike = 1.2; + double afCATireV = 1.1; + double afCATireH = 0.9; + double afAFrame = 0.048; + double CrV = 0.0031; + double ATire = 0.031; + double CrEff = CrV; + double adipos = sqrt(M/(hRider*750)); + double CwaBike = afCdBike * (afCATireV * ATire + afCATireH * ATire + afAFrame); + qDebug()<<"CwaBike="<areDataPresent()->slope && ride->areDataPresent()->alt && ride->areDataPresent()->km) { + for (int i=0; idataPoints().count(); i++) { + RideFilePoint *p = ride->dataPoints()[i]; + // Slope + // If there is no slope data then it can be derived + // from distanct and altitude + if (lastP) { + double deltaDistance = (p->km - lastP->km) * 1000; + double deltaAltitude = p->alt - lastP->alt; + if (deltaDistance>0) { + ride->command->setPointValue(i, RideFile::slope, (deltaAltitude / deltaDistance) * 100); + } else { + ride->command->setPointValue(i, RideFile::slope, 0); + } + if (p->slope > 20 || p->slope < -20) { + ride->command->setPointValue(i, RideFile::slope, lastP->slope); + } + } + // last point + lastP = p; + } + + // Smooth the slope if it has been derived + int smoothPoints = 10; + // initialise rolling average + double rtot = 0; + for (int i=smoothPoints; i>0 && ride->dataPoints().count()-i >=0; i--) { + rtot += ride->dataPoints()[ride->dataPoints().count()-i]->slope; + } + + // now run backwards setting the rolling average + for (int i=ride->dataPoints().count()-1; i>=smoothPoints; i--) { + double here = ride->dataPoints()[i]->slope; + ride->dataPoints()[i]->slope = rtot / smoothPoints; + rtot -= here; + rtot += ride->dataPoints()[i-smoothPoints]->slope; + } + ride->setDataPresent(ride->slope, true); + } + + if (!ride->areDataPresent()->cad) { + for (int i=0; idataPoints().count(); i++) { + RideFilePoint *p = ride->dataPoints()[i]; + ride->command->setPointValue(i, RideFile::cad, 85); + } + ride->setDataPresent(ride->cad, true); + } + + if (ride->areDataPresent()->slope && ride->areDataPresent()->alt + && ride->areDataPresent()->km && ride->areDataPresent()->cad) { + for (int i=0; idataPoints().count(); i++) { + RideFilePoint *p = ride->dataPoints()[i]; + // Estimate Power if not in data + if (p->cad > 0) { + if (ride->areDataPresent()->temp) T = p->temp; + double Slope = atan(p->slope * .01); + double V = p->kph * 0.27777777777778; //Speed m/s + double CrDyn = 0.1 * cos(Slope); + + double CwaRider, Ka; + double Frg = 9.81 * (MBik + M) * (CrEff * cos(Slope) + sin(Slope)); + + double vw=V+W; + CwaRider = (1 + p->cad * cCad) * afCd * adipos * (((hRider - adipos) * afSin) + adipos); + Ka = 176.5 * exp(-p->alt * .0001253) * (CwaRider + CwaBike) / (273 + T); + ride->command->setPointValue(i, RideFile::watts, afCm * V * (Ka * (vw * vw) + Frg + V * CrDyn)); + qDebug()<<"w="<watts<<", Ka="<