From e5affbbc64ed77bd6d3552a0e8fdaea3f6860476 Mon Sep 17 00:00:00 2001 From: Sean Rhea Date: Thu, 17 Dec 2009 18:49:13 -0500 Subject: [PATCH] introduce "metric overrides" This commit allows every ride file to specify a set of "metric overrides": values to use in place of those for RideMetrics we would otherwise compute. The most gratifying immediate result of this change is that we can associate a "skiba_bike_score" metric override with each Manual CSV file, thereby eliminating the need for a bogus "bs" parameter in RideFilePoint. In the future, though, we can also save these overrides to a GcRideFile using a syntax something like this: (Note that average_speed needs to store time and distance in order to aggregate properly.) Then we can add a dialog that allows the user to override the computed value of a metric for any given ride. For example, if my HRM was on the fritz during a ride, I could estimate my average HR and override that metric. (We might want to show these overrided metrics in a different color, so that it was clear they weren't the computed values.) Finally, I think we could actually use this feature to eliminate the Manual CSV format altogether, and just use GcRideFiles without any samples or intervals, but with metric overrides for all the available metrics. --- src/BikeScore.cpp | 30 ++++++++++++++---------------- src/DanielsPoints.cpp | 9 ++++++++- src/ManualRideFile.cpp | 7 +++++-- src/RideFile.cpp | 8 ++------ src/RideFile.h | 11 ++++++----- src/RideItem.cpp | 5 ++++- src/RideMetric.h | 3 +++ 7 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/BikeScore.cpp b/src/BikeScore.cpp index a896a7690..70fda17a3 100644 --- a/src/BikeScore.cpp +++ b/src/BikeScore.cpp @@ -143,25 +143,23 @@ class BikeScore : public RideMetric { QString name() const { return "skiba_bike_score"; } QString units(bool) const { return ""; } double value(bool) const { return score; } - void compute(const RideFile *ride, const Zones *zones, int zoneRange, + void compute(const RideFile *, const Zones *zones, int zoneRange, const QHash &deps) { if (!zones || zoneRange < 0) return; - if (ride->deviceType() == QString("Manual CSV")) { - // manual entry, use BS from dataPoints - score = ride->dataPoints().first()->bs; - } - else { - assert(deps.contains("skiba_xpower")); - assert(deps.contains("skiba_relative_intensity")); - XPower *xp = dynamic_cast(deps.value("skiba_xpower")); - RideMetric *ri = deps.value("skiba_relative_intensity"); - assert(ri); - double normWork = xp->xpower * xp->secs; - double rawBikeScore = normWork * ri->value(true); - double workInAnHourAtCP = zones->getCP(zoneRange) * 3600; - score = rawBikeScore / workInAnHourAtCP * 100.0; - } + assert(deps.contains("skiba_xpower")); + assert(deps.contains("skiba_relative_intensity")); + XPower *xp = dynamic_cast(deps.value("skiba_xpower")); + RideMetric *ri = deps.value("skiba_relative_intensity"); + assert(ri); + double normWork = xp->xpower * xp->secs; + double rawBikeScore = normWork * ri->value(true); + double workInAnHourAtCP = zones->getCP(zoneRange) * 3600; + score = rawBikeScore / workInAnHourAtCP * 100.0; + } + void override(const QMap &map) { + if (map.contains("value")) + score = map.value("value").toDouble(); } RideMetric *clone() const { return new BikeScore(*this); } bool canAggregate() const { return true; } diff --git a/src/DanielsPoints.cpp b/src/DanielsPoints.cpp index d58f3233e..d82cbb546 100644 --- a/src/DanielsPoints.cpp +++ b/src/DanielsPoints.cpp @@ -52,7 +52,14 @@ class DanielsPoints : public RideMetric { // Manual entry: use BS from dataPoints with a scaling factor // that works about right for long, steady rides. double scaling_factor = 0.55; - score = ride->dataPoints().first()->bs * scaling_factor; + if (ride->metricOverrides.contains("skiba_bike_score")) { + const QMap bsm = + ride->metricOverrides.value("skiba_bike_score"); + if (bsm.contains("value")) { + double bs = bsm.value("value").toDouble(); + score = bs * scaling_factor; + } + } return; } diff --git a/src/ManualRideFile.cpp b/src/ManualRideFile.cpp index 54f78d080..04a439e6a 100644 --- a/src/ManualRideFile.cpp +++ b/src/ManualRideFile.cpp @@ -109,8 +109,11 @@ RideFile *ManualFileReader::openRideFile(QFile &file, QStringList &errors) const cad = nm = 0.0; interval = 0; - rideFile->appendPoint(minutes * 60.0, cad, hr, km, - kph, nm, watts, alt, 0.0, 0.0, interval, bs); + rideFile->appendPoint(minutes * 60.0, cad, hr, km, + kph, nm, watts, alt, 0.0, 0.0, interval); + QMap bsm; + bsm.insert("value", QString("%1").arg(bs)); + rideFile->metricOverrides.insert("skiba_bike_score", bsm); rideSec = minutes * 60.0; } diff --git a/src/RideFile.cpp b/src/RideFile.cpp index 1e4e1ac27..f2bde604a 100644 --- a/src/RideFile.cpp +++ b/src/RideFile.cpp @@ -123,10 +123,6 @@ void RideFile::writeAsCsv(QFile &file, bool bIsMetric) const out << point->interval; out << ","; out << point->alt; - if (point->bs > 0.0) { - out << ","; - out << point->bs; - } out << "\n"; } @@ -200,10 +196,10 @@ QStringList RideFileFactory::listRideFiles(const QDir &dir) const void RideFile::appendPoint(double secs, double cad, double hr, double km, double kph, double nm, double watts, double alt, - double lon, double lat, int interval, double bs) + double lon, double lat, int interval) { dataPoints_.append(new RideFilePoint(secs, cad, hr, km, kph, - nm, watts, alt, lon, lat, interval,bs)); + nm, watts, alt, lon, lat, interval)); dataPresent.secs |= (secs != 0); dataPresent.cad |= (cad != 0); dataPresent.hr |= (hr != 0); diff --git a/src/RideFile.h b/src/RideFile.h index e1242a585..14294acfd 100644 --- a/src/RideFile.h +++ b/src/RideFile.h @@ -45,13 +45,12 @@ struct RideFilePoint { double secs, cad, hr, km, kph, nm, watts, alt, lon, lat;; int interval; - double bs; // to init in order RideFilePoint() : secs(0.0), cad(0.0), hr(0.0), km(0.0), kph(0.0), - nm(0.0), watts(0.0), alt(0.0), lon(0.0), lat(0.0), interval(0), bs(0.0) {} + nm(0.0), watts(0.0), alt(0.0), lon(0.0), lat(0.0), interval(0) {} RideFilePoint(double secs, double cad, double hr, double km, double kph, - double nm, double watts, double alt, double lon, double lat, int interval, double bs) : + double nm, double watts, double alt, double lon, double lat, int interval) : secs(secs), cad(cad), hr(hr), km(km), kph(kph), nm(nm), - watts(watts), alt(alt), lon(lon), lat(lat), interval(interval), bs(bs) {} + watts(watts), alt(alt), lon(lon), lat(lat), interval(interval) {} }; struct RideFileDataPresent @@ -107,7 +106,7 @@ class RideFile void appendPoint(double secs, double cad, double hr, double km, double kph, double nm, double watts, double alt, - double lon, double lat, int interval, double bs=0.0); + double lon, double lat, int interval); const QList &intervals() const { return intervals_; } void addInterval(double start, double stop, const QString &name) { @@ -122,6 +121,8 @@ class RideFile void resetDataPresent(); double timeToDistance(double) const; // get distance in km at time in secs + + QMap > metricOverrides; }; struct RideFileReader { diff --git a/src/RideItem.cpp b/src/RideItem.cpp index c1d8f867c..3f9a17f85 100644 --- a/src/RideItem.cpp +++ b/src/RideItem.cpp @@ -171,7 +171,10 @@ RideItem::computeMetrics() if (!metrics.contains(deps[j])) goto later; RideMetric *metric = factory.newMetric(name); - metric->compute(ride(), zones, zone_range, metrics); + if (ride()->metricOverrides.contains(name)) + metric->override(ride()->metricOverrides.value(name)); + else + metric->compute(ride(), zones, zone_range, metrics); metrics.insert(name, metric); i.remove(); } diff --git a/src/RideMetric.h b/src/RideMetric.h index 335f0f184..a597be595 100644 --- a/src/RideMetric.h +++ b/src/RideMetric.h @@ -37,6 +37,9 @@ struct RideMetric { const Zones *zones, int zoneRange, const QHash &deps) = 0; + virtual void override(const QMap &) { + assert(false); + } virtual bool canAggregate() const { return false; } virtual void aggregateWith(RideMetric *other) { (void) other;