diff --git a/.gitignore b/.gitignore index 1a7bd482a..56391d48a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ bin/ plugins/ resources/ src/debug/ +src/release/ qwt/src/debug/ qwt/src/release/ @@ -26,4 +27,6 @@ qwt/src/object_script.libqwt.Release qwt/src/object_script.libqwtd.Debug qwt/src/Makefile.Release qwt/src/Makefile.Debug +qwt/textengines/mathml/debug/ +qwt/textengines/mathml/release/ build.pro.user \ No newline at end of file diff --git a/src/DBAccess.cpp b/src/DBAccess.cpp index 44123e635..0bb5ceaec 100644 --- a/src/DBAccess.cpp +++ b/src/DBAccess.cpp @@ -113,8 +113,9 @@ // 91 16 Nov 2014 Damien Grauser Do not include values if data not present in TimeInZone and HRTimeInZone // 92 21 Nov 2014 Mark Liversedge Added Watts:RPE ratio // 93 26 Nov 2014 Mark Liversedge Added Min, Max, Avg SmO2 +// 94 02 Dic 2014 Ale Martinez Added xPace -int DBSchemaVersion = 93; +int DBSchemaVersion = 94; DBAccess::DBAccess(Context* context) : context(context), db(NULL) { diff --git a/src/GOVSS.cpp b/src/GOVSS.cpp index 17564c87a..086ed1d19 100644 --- a/src/GOVSS.cpp +++ b/src/GOVSS.cpp @@ -19,6 +19,7 @@ #include "RideMetric.h" #include "PaceZones.h" +#include "Units.h" #include #include #include @@ -32,8 +33,8 @@ // Running Power based on speed and slope static inline double running_power( double weight, double height, - double speed, double slope, - double distance) { + double speed, double slope=0.0, + double distance=0.0) { // Aero contribution - probably needs refinement double cAero = 0.25*0.01*pow(speed, 2)*pow(height,-3); // Energy Cost of Running according to slope @@ -45,7 +46,7 @@ static inline double running_power( double weight, double height, return (cAero + cSlope*eff*(1 - 0.5*speed/8.33) + cKin)*weight*speed; } -// Lactate Normalized Power, used for GOVSS calculation +// Lactate Normalized Power, used for GOVSS and xPace calculation class LNP : public RideMetric { Q_DECLARE_TR_FUNCTIONS(LNP) double lnp; @@ -117,7 +118,7 @@ class LNP : public RideMetric { double slope120 = sumSlope/std::min(count+1, rollingwindowsize120); // slope rolling average // running power based on 120sec averages - double watts = running_power(weight, height, speed120, slope120, 0.0);// sumSpeed*ride->recIntSecs()); KE contribution disabled + double watts = running_power(weight, height, speed120, slope120); // sumSpeed*ride->recIntSecs()); KE contribution disabled sumPower += watts; sumPower -= rollingPower[index30]; @@ -144,6 +145,65 @@ class LNP : public RideMetric { RideMetric *clone() const { return new LNP(*this); } }; +// xPace: constant Pace which, on flat surface, gives same Lactate Normalized Power +class XPace : public RideMetric { + Q_DECLARE_TR_FUNCTIONS(XPace) + double xPace; + + public: + + XPace() : xPace(0.0) + { + setSymbol("xPace"); + setInternalName("xPace"); + } + void initialize() { + setName(tr("xPace")); + setType(RideMetric::Average); + setMetricUnits(tr("min/km")); + setImperialUnits(tr("min/mile")); + setPrecision(1); + setConversion(KM_PER_MILE); + } + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &deps, + const Context *) { + // xPace only makes sense for running + if (!ride->isRun()) return; + + // unconst naughty boy, get athlete's data + RideFile *uride = const_cast(ride); + double weight = uride->getWeight(); + double height = uride->getWeight(); + + assert(deps.contains("govss_lnp")); + LNP *lnp = dynamic_cast(deps.value("govss_lnp")); + assert(lnp); + double lnp_watts = lnp->value(true); + + // search for speed which gives flat power within 0.001watt of LNP + // up to around 10 iterations for speed within 0.01m/s or ~1sec/km + double low = 0.0, high = 10.0, speed; + if (lnp_watts <= 0.0) speed = low; + else if (lnp_watts >= running_power(weight, height, high)) speed = high; + else do { + speed = (low + high)/2.0; + double watts = running_power(weight, height, speed); + if (abs(watts - lnp_watts) < 0.001) break; + else if (watts < lnp_watts) low = speed; + else if (watts > lnp_watts) high = speed; + } while (high - low > 0.01); + // divide by zero or stupidly low pace + if (speed > 0.01) xPace = (1000.0/60.0) / speed; + else xPace = 0.0; + + setValue(xPace); + } + + RideMetric *clone() const { return new XPace(*this); } +}; + // Running Threshold Power based on CV, used for GOVSS calculation class RTP : public RideMetric { Q_DECLARE_TR_FUNCTIONS(RTP) @@ -186,7 +246,7 @@ class RTP : public RideMetric { cv = zones->getCV(zoneRange); // Running power at cv on flat surface - double watts = running_power(weight, height, cv/3.6, 0.0, 0.0); //120*cv/3.6); KE contribution disabled + double watts = running_power(weight, height, cv/3.6); //120*cv/3.6); KE contribution disabled setValue(watts); } @@ -285,6 +345,7 @@ static bool addAllGOVSS() { RideMetricFactory::instance().addMetric(RTP()); QVector deps; deps.append("govss_lnp"); + RideMetricFactory::instance().addMetric(XPace(), &deps); deps.append("govss_rtp"); RideMetricFactory::instance().addMetric(IWF(), &deps); deps.append("govss_iwf"); diff --git a/src/RideSummaryWindow.cpp b/src/RideSummaryWindow.cpp index c376ea6b6..a236d7383 100644 --- a/src/RideSummaryWindow.cpp +++ b/src/RideSummaryWindow.cpp @@ -558,7 +558,7 @@ RideSummaryWindow::htmlSummary() s = s.arg(ride->getTag("Temperature", "-")); - } else if (m->internalName() == "Pace") { // pace is mm:ss + } else if (m->internalName() == "Pace" || m->internalName() == "xPace") { // pace is mm:ss double pace; bool metricPace = appsettings->value(this, GC_PACE, true).toBool(); @@ -927,7 +927,7 @@ RideSummaryWindow::htmlSummary() RideMetricPtr m = metrics.value(symbol); if (!m) continue; summary += "" + m->name(); - if (m->internalName() == "Pace") { // pace is mm:ss + if (m->internalName() == "Pace" || m->internalName() == "xPace") { // pace is mm:ss summary += " (" + m->units(metricPace) + ")"; @@ -958,7 +958,7 @@ RideSummaryWindow::htmlSummary() QString s("%1"); if (m->units(useMetricUnits) == "seconds" || m->units(useMetricUnits) == tr("seconds")) summary += s.arg(time_to_string(m->value(useMetricUnits))); - else if (m->internalName() == "Pace") { // pace is mm:ss + else if (m->internalName() == "Pace" || m->internalName() == "xPace") { // pace is mm:ss double pace = m->value(metricPace); summary += s.arg(QTime(0,0,0,0).addSecs(pace*60).toString("mm:ss"));