From 2803c8659bfade5cdb675fde8d473d63fe79833f Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Thu, 3 Dec 2015 14:58:38 +0000 Subject: [PATCH] NA value and Min Temperature metric .. allow NA to be used anywhere and skipped on the metric trend chart. .. add a min temperature metric .. add a chart for tracking temperatures --- src/AllPlot.cpp | 4 +-- src/BasicRideMetrics.cpp | 71 ++++++++++++++++++++++++++++++++----- src/Bin2RideFile.cpp | 4 +-- src/BinRideFile.cpp | 2 +- src/Computrainer3dpFile.cpp | 2 +- src/CsvRideFile.cpp | 2 +- src/FitRideFile.cpp | 6 ++-- src/FitlogParser.cpp | 2 +- src/FitlogRideFile.cpp | 2 +- src/GcRideFile.cpp | 2 +- src/GpxParser.cpp | 2 +- src/JsonRideFile.y | 2 +- src/LTMPlot.cpp | 18 ++++++++-- src/LapsEditor.cpp | 2 +- src/ManualRideFile.cpp | 2 +- src/PolarRideFile.cpp | 2 +- src/PowerHist.cpp | 4 +-- src/PwxRideFile.cpp | 2 +- src/QuarqParser.cpp | 4 +-- src/RawRideFile.cpp | 4 +-- src/RideCache.cpp | 2 +- src/RideFile.cpp | 12 +++---- src/RideFile.h | 2 +- src/RideMetric.cpp | 3 +- src/RideNavigatorProxy.h | 2 +- src/RideSummaryWindow.cpp | 2 +- src/ScatterPlot.cpp | 2 +- src/SlfParser.cpp | 2 +- src/SmlParser.cpp | 4 +-- src/SrdRideFile.cpp | 2 +- src/TacxCafRideFile.cpp | 4 +-- src/TcxParser.cpp | 8 ++--- src/TxtRideFile.cpp | 4 +-- src/WkoRideFile.cpp | 4 +-- test/charts/Temperature.xml | 3 ++ 35 files changed, 133 insertions(+), 62 deletions(-) create mode 100644 test/charts/Temperature.xml diff --git a/src/AllPlot.cpp b/src/AllPlot.cpp index 35dfe9f32..a80b45470 100644 --- a/src/AllPlot.cpp +++ b/src/AllPlot.cpp @@ -1981,7 +1981,7 @@ AllPlot::recalc(AllPlotObject *objects) if (!objects->windArray.empty()) totalWind += objects->windArray[i]; if (!objects->torqueArray.empty()) totalTorque += objects->torqueArray[i]; if (!objects->tempArray.empty() ) { - if (objects->tempArray[i] == RideFile::NoTemp) { + if (objects->tempArray[i] == RideFile::NA) { dp.temp = (i>0 && !list.empty()?list.back().temp:0.0); totalTemp += dp.temp; } @@ -2255,7 +2255,7 @@ AllPlot::recalc(AllPlotObject *objects) objects->smoothDistance.append(context->athlete->useMetricUnits ? dp->km : dp->km * MILES_PER_KM); objects->smoothAltitude.append(context->athlete->useMetricUnits ? dp->alt : dp->alt * FEET_PER_METER); objects->smoothSlope.append(dp->slope); - if (dp->temp == RideFile::NoTemp && !objects->smoothTemp.empty()) + if (dp->temp == RideFile::NA && !objects->smoothTemp.empty()) dp->temp = objects->smoothTemp.last(); objects->smoothTemp.append(context->athlete->useMetricUnits ? dp->temp : dp->temp * FAHRENHEIT_PER_CENTIGRADE + FAHRENHEIT_ADD_CENTIGRADE); objects->smoothWind.append(context->athlete->useMetricUnits ? dp->headwind : dp->headwind * MILES_PER_KM); diff --git a/src/BasicRideMetrics.cpp b/src/BasicRideMetrics.cpp index 16dc7c798..0cdcbd3d6 100644 --- a/src/BasicRideMetrics.cpp +++ b/src/BasicRideMetrics.cpp @@ -1542,9 +1542,9 @@ struct AvgTemp : public RideMetric { // we DO aggregate zero, its -255 we ignore ! bool aggregateZero() const { return true; } - // override to special case NoTemp + // override to special case NA QString toString(bool useMetricUnits) const { - if (value() == RideFile::NoTemp) return "-"; + if (value() == RideFile::NA) return "-"; return RideMetric::toString(useMetricUnits); } @@ -1565,7 +1565,7 @@ struct AvgTemp : public RideMetric { if (ride->areDataPresent()->temp) { total = count = 0; foreach (const RideFilePoint *point, ride->dataPoints()) { - if (point->temp != RideFile::NoTemp) { + if (point->temp != RideFile::NA) { total += point->temp; ++count; } @@ -1573,7 +1573,7 @@ struct AvgTemp : public RideMetric { setValue(count > 0 ? total / count : count); setCount(count); } else { - setValue(RideFile::NoTemp); + setValue(RideFile::NA); setCount(1); } } @@ -1977,9 +1977,9 @@ class MaxTemp : public RideMetric { setConversionSum(FAHRENHEIT_ADD_CENTIGRADE); } - // override to special case NoTemp + // override to special case NA QString toString(bool useMetricUnits) const { - if (value() == RideFile::NoTemp) return "-"; + if (value() == RideFile::NA) return "-"; return RideMetric::toString(useMetricUnits); } @@ -1991,11 +1991,11 @@ class MaxTemp : public RideMetric { if (ride->areDataPresent()->temp) { double max = 0.0; foreach (const RideFilePoint *point, ride->dataPoints()) - if (point->temp != RideFile::NoTemp && point->temp > max) max = point->temp; + if (point->temp != RideFile::NA && point->temp > max) max = point->temp; setValue(max); } else { - setValue(RideFile::NoTemp); + setValue(RideFile::NA); } } @@ -2013,6 +2013,61 @@ static bool maxTempAdded = ////////////////////////////////////////////////////////////////////////////// +class MinTemp : public RideMetric { + Q_DECLARE_TR_FUNCTIONS(MinTemp) + public: + + MinTemp() + { + setSymbol("min_temp"); + setInternalName("Min Temp"); + } + void initialize() { + setName(tr("Min Temp")); + setMetricUnits(tr("C")); + setImperialUnits(tr("F")); + setType(RideMetric::Peak); + setPrecision(1); + setConversion(FAHRENHEIT_PER_CENTIGRADE); + setConversionSum(FAHRENHEIT_ADD_CENTIGRADE); + } + + // override to special case NA + QString toString(bool useMetricUnits) const { + if (value() == RideFile::NA) return "-"; + return RideMetric::toString(useMetricUnits); + } + + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const Context *) { + + if (ride->areDataPresent()->temp) { + double min = 10000; + foreach (const RideFilePoint *point, ride->dataPoints()) + if (point->temp != RideFile::NA && point->temp < min) min = point->temp; + + setValue(min < 10000 ? min : RideFile::NA); + } else { + setValue(RideFile::NA); + } + } + + void aggregateWith(const RideMetric &other) { + assert(symbol() == other.symbol()); + const MinTemp &mc = dynamic_cast(other); + + setValue(mc.value(true) < value(true) ? mc.value(true) : value(true)); + } + RideMetric *clone() const { return new MinTemp(*this); } +}; + +static bool minTempAdded = + RideMetricFactory::instance().addMetric(MinTemp()); + +////////////////////////////////////////////////////////////////////////////// + class NinetyFivePercentHeartRate : public RideMetric { Q_DECLARE_TR_FUNCTIONS(NinetyFivePercentHeartRate) double hr; diff --git a/src/Bin2RideFile.cpp b/src/Bin2RideFile.cpp index 25b6c042e..e89d2b1b9 100644 --- a/src/Bin2RideFile.cpp +++ b/src/Bin2RideFile.cpp @@ -158,7 +158,7 @@ struct Bin2FileReaderState int nm = 0; double kph = 0.0; int alt = 0; - double temp = RideFile::NoTemp; + double temp = RideFile::NA; double lat = 0.0; double lng = 0.0; double km = 0.0; @@ -223,7 +223,7 @@ struct Bin2FileReaderState kph = kph/10.0; if (temp == 0x8000) //0x8000 = invalid - temp = RideFile::NoTemp; + temp = RideFile::NA; else if (temp > 0x7FFF) // Negative temp = (temp-0xFFFF)/10.0; else diff --git a/src/BinRideFile.cpp b/src/BinRideFile.cpp index f99c2a873..a04bf1abc 100644 --- a/src/BinRideFile.cpp +++ b/src/BinRideFile.cpp @@ -405,7 +405,7 @@ struct BinFileReaderState int i = 0; double secs = 0, alt = 0, cad = 0, km = 0, grade = 0, hr = 0; double nm = 0, kph = 0, watts = 0; - double temperature = RideFile::NoTemp; + double temperature = RideFile::NA; double lrbalance = 0.0; foreach(const BinField &field, def.fields) { diff --git a/src/Computrainer3dpFile.cpp b/src/Computrainer3dpFile.cpp index be065b59d..9b2f2199e 100644 --- a/src/Computrainer3dpFile.cpp +++ b/src/Computrainer3dpFile.cpp @@ -296,7 +296,7 @@ RideFile *Computrainer3dpFileReader::openRideFile(QFile & file, // so now we can add to the ride rideFile->appendPoint(sample.secs, sample.cad, sample.hr, sample.km, sample.kph, 0.0, sample.watts, sample.alt, 0.0, 0.0, - 0.0, 0.0, RideFile::NoTemp, 0.0, + 0.0, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0,0.0,0.0, 0.0,0.0,0.0,0.0,0.0,0.0, 0); diff --git a/src/CsvRideFile.cpp b/src/CsvRideFile.cpp index 9c8950abe..169cc7c89 100644 --- a/src/CsvRideFile.cpp +++ b/src/CsvRideFile.cpp @@ -410,7 +410,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors, QList unitsHeader) { double minutes=0,nm=0,kph=0,watts=0,km=0,cad=0,alt=0,hr=0,dfpm=0, seconds=0.0; - double temp=RideFile::NoTemp; + double temp=RideFile::NA; double slope=0.0; bool ok; double lat = 0.0, lon = 0.0; diff --git a/src/FitRideFile.cpp b/src/FitRideFile.cpp index 5e6f423fc..43ce3aa03 100644 --- a/src/FitRideFile.cpp +++ b/src/FitRideFile.cpp @@ -764,7 +764,7 @@ struct FitFileReaderState if (time_offset > 0) time = last_time + time_offset; double alt = 0, cad = 0, km = 0, hr = 0, lat = 0, lng = 0, badgps = 0, lrbalance = 0; - double kph = 0, temperature = RideFile::NoTemp, watts = 0, slope = 0; + double kph = 0, temperature = RideFile::NA, watts = 0, slope = 0; double leftTorqueEff = 0, rightTorqueEff = 0, leftPedalSmooth = 0, rightPedalSmooth = 0; double leftPedalCenterOffset = 0; @@ -1189,7 +1189,7 @@ struct FitFileReaderState last_time+i, 0.0, 0.0, last_distance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, RideFile::NoTemp, + 0.0, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, @@ -1215,7 +1215,7 @@ struct FitFileReaderState last_time + i, cad, 0.0, last_distance + (deltaDist * i/deltaSecs), kph, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, RideFile::NoTemp, + 0.0, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, diff --git a/src/FitlogParser.cpp b/src/FitlogParser.cpp index 6f6a10e39..c5c2e7b8e 100644 --- a/src/FitlogParser.cpp +++ b/src/FitlogParser.cpp @@ -100,7 +100,7 @@ FitlogParser::startElement( const QString&, const QString&, // now add rideFile->appendPoint(point.secs,point.cad,point.hr,point.km,point.kph,point.nm, point.watts,point.alt,point.lon,point.lat, point.headwind, - 0.0, RideFile::NoTemp, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, diff --git a/src/FitlogRideFile.cpp b/src/FitlogRideFile.cpp index 525d850d7..5319f4488 100644 --- a/src/FitlogRideFile.cpp +++ b/src/FitlogRideFile.cpp @@ -150,7 +150,7 @@ FitlogFileReader::writeRideFile(Context *context, const RideFile *ride, QFile &f break; f.appendPoint(p->secs, p->cad, p->hr, p->km, p->kph, p->nm, p->watts, p->alt, p->lon, p->lat, p->headwind, - 0.0, RideFile::NoTemp, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, diff --git a/src/GcRideFile.cpp b/src/GcRideFile.cpp index 55a25eb86..20f62ed01 100644 --- a/src/GcRideFile.cpp +++ b/src/GcRideFile.cpp @@ -147,7 +147,7 @@ GcFileReader::openRideFile(QFile &file, QStringList &errors, QList*) lat = sample.attribute("lat", "0.0").toDouble(); while ((interval < intervalStops.size()) && (secs >= intervalStops[interval])) ++interval; - rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind, 0.0, RideFile::NoTemp, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, interval); + rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind, 0.0, RideFile::NA, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, interval); if (!recIntSet) { rideFile->setRecIntSecs(sample.attribute("len").toDouble()); recIntSet = true; diff --git a/src/GpxParser.cpp b/src/GpxParser.cpp index b42585a21..cd262a2f7 100644 --- a/src/GpxParser.cpp +++ b/src/GpxParser.cpp @@ -46,7 +46,7 @@ GpxParser::GpxParser (RideFile* rideFile) lon = 0; lat = 0; hr = 0; - temp = RideFile::NoTemp; + temp = RideFile::NA; firstTime = true; metadata = false; diff --git a/src/JsonRideFile.y b/src/JsonRideFile.y index 573da3dcf..967e9878b 100644 --- a/src/JsonRideFile.y +++ b/src/JsonRideFile.y @@ -564,7 +564,7 @@ JsonFileReader::writeRideFile(Context *, const RideFile *ride, QFile &file) cons out << ", \"LON\":" << QString("%1").arg(p->lon, 0, 'g', 11); if (ride->areDataPresent()->headwind) out << ", \"HEADWIND\":" << QString("%1").arg(p->headwind); if (ride->areDataPresent()->slope) out << ", \"SLOPE\":" << QString("%1").arg(p->slope); - if (ride->areDataPresent()->temp && p->temp != RideFile::NoTemp) out << ", \"TEMP\":" << QString("%1").arg(p->temp); + if (ride->areDataPresent()->temp && p->temp != RideFile::NA) out << ", \"TEMP\":" << QString("%1").arg(p->temp); if (ride->areDataPresent()->lrbalance) out << ", \"LRBALANCE\":" << QString("%1").arg(p->lrbalance); if (ride->areDataPresent()->lte) out << ", \"LTE\":" << QString("%1").arg(p->lte); if (ride->areDataPresent()->rte) out << ", \"RTE\":" << QString("%1").arg(p->rte); diff --git a/src/LTMPlot.cpp b/src/LTMPlot.cpp index b85588a98..8a8e42d93 100644 --- a/src/LTMPlot.cpp +++ b/src/LTMPlot.cpp @@ -2459,6 +2459,9 @@ LTMPlot::createTODCurveData(Context *context, LTMSettings *settings, MetricDetai // check values are bounded to stop QWT going berserk if (std::isnan(value) || std::isinf(value)) value = 0; + // skip unavailable values + if (value == RideFile::NA) continue; + // Special computed metrics (LTS/STS) have a null metric pointer if (metricDetail.metric) { // convert from stored metric value to imperial @@ -2477,7 +2480,7 @@ LTMPlot::createTODCurveData(Context *context, LTMSettings *settings, MetricDetai bool aggZero = metricDetail.metric ? metricDetail.metric->aggregateZero() : false; // set aggZero to false and value to zero if is temperature and -255 - if (metricDetail.metric && metricDetail.metric->symbol() == "average_temp" && value == RideFile::NoTemp) { + if (metricDetail.metric && metricDetail.metric->symbol() == "average_temp" && value == RideFile::NA) { value = 0; aggZero = false; } @@ -2580,8 +2583,11 @@ LTMPlot::createMetricData(Context *context, LTMSettings *settings, MetricDetail // check values are bounded to stop QWT going berserk if (std::isnan(value) || std::isinf(value)) value = 0; + // skip unavailable values + if (value == RideFile::NA) continue; + // set aggZero to false and value to zero if is temperature and -255 - if (metricDetail.metric && metricDetail.metric->symbol() == "average_temp" && value == RideFile::NoTemp) { + if (metricDetail.metric && metricDetail.metric->symbol() == "average_temp" && value == RideFile::NA) { value = 0; aggZero = false; } @@ -2708,6 +2714,9 @@ LTMPlot::createFormulaData(Context *context, LTMSettings *settings, MetricDetail // check values are bounded to stop QWT going berserk if (std::isnan(value) || std::isinf(value)) value = 0; + // skip unavailable values + if (value == RideFile::NA) continue; + // convert seconds to hours if (metricDetail.uunits == tr("seconds")) value /= 3600; @@ -2829,8 +2838,11 @@ LTMPlot::createBestsData(Context *, LTMSettings *settings, MetricDetail metricDe // check values are bounded to stop QWT going berserk if (std::isnan(value) || std::isinf(value)) value = 0; + // skip unavailable values + if (value == RideFile::NA) continue; + // set aggZero to false and value to zero if is temperature and -255 - if (metricDetail.metric && metricDetail.metric->symbol() == "average_temp" && value == RideFile::NoTemp) { + if (metricDetail.metric && metricDetail.metric->symbol() == "average_temp" && value == RideFile::NA) { value = 0; aggZero = false; } diff --git a/src/LapsEditor.cpp b/src/LapsEditor.cpp index 60b7994dd..04c870348 100644 --- a/src/LapsEditor.cpp +++ b/src/LapsEditor.cpp @@ -150,7 +150,7 @@ LapsEditor::okClicked() km += kph/3600.0; dataPoints_.append(new RideFilePoint(secs, 0.0, 0.0, km, kph, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - RideFile::NoTemp, 0.0, + RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, diff --git a/src/ManualRideFile.cpp b/src/ManualRideFile.cpp index 02793bc37..04dc7b320 100644 --- a/src/ManualRideFile.cpp +++ b/src/ManualRideFile.cpp @@ -125,7 +125,7 @@ RideFile *ManualFileReader::openRideFile(QFile &file, QStringList &errors, QList rideFile->appendPoint(minutes * 60.0, cad, hr, km, kph, nm, watts, alt, 0.0, 0.0, 0.0, 0.0, - RideFile::NoTemp, 0.0, + RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // pedal platform offset 0.0, 0.0, 0.0, 0.0, //pedal power phase diff --git a/src/PolarRideFile.cpp b/src/PolarRideFile.cpp index 1dc4e3328..803987687 100644 --- a/src/PolarRideFile.cpp +++ b/src/PolarRideFile.cpp @@ -318,7 +318,7 @@ this differently igpx += 1; } - rideFile->appendPoint(seconds, cad, hr, km, kph, nm, watts, alt, lon, lat, 0.0, 0.0, RideFile::NoTemp, lrbalance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, interval); + rideFile->appendPoint(seconds, cad, hr, km, kph, nm, watts, alt, lon, lat, 0.0, 0.0, RideFile::NA, lrbalance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, interval); // fprintf(stderr, " %f, %f, %f, %f, %f, %f, %f, %d\n", seconds, cad, hr, km, kph, nm, watts, alt, interval); if (recInterval==238){ seconds += hrm / 1000.0; diff --git a/src/PowerHist.cpp b/src/PowerHist.cpp index 7345ea0f0..7ba9acf14 100644 --- a/src/PowerHist.cpp +++ b/src/PowerHist.cpp @@ -1706,7 +1706,7 @@ PowerHist::setData(Specification specification, QString totalMetric, QString dis double v = x->getForSymbol(distMetric, context->athlete->useMetricUnits); // ignore no temp files - if ((distMetric == "average_temp" || distMetric == "max_temp") && v == RideFile::NoTemp) continue; + if ((distMetric == "average_temp" || distMetric == "max_temp") && v == RideFile::NA) continue; // clean up dodgy values if (std::isnan(v) || std::isinf(v)) v = 0; @@ -1744,7 +1744,7 @@ PowerHist::setData(Specification specification, QString totalMetric, QString dis double v = x->getForSymbol(distMetric, context->athlete->useMetricUnits); // ignore no temp files - if ((distMetric == "average_temp" || distMetric == "max_temp") && v == RideFile::NoTemp) continue; + if ((distMetric == "average_temp" || distMetric == "max_temp") && v == RideFile::NA) continue; // clean up dodgy values if (std::isnan(v) || std::isinf(v)) v = 0; diff --git a/src/PwxRideFile.cpp b/src/PwxRideFile.cpp index 8b17fb7a6..155c0c1ac 100644 --- a/src/PwxRideFile.cpp +++ b/src/PwxRideFile.cpp @@ -248,7 +248,7 @@ PwxFileReader::PwxFromDomDoc(QDomDocument doc, QStringList&) const // temp QDomElement temp = node.firstChildElement("temp"); if (!temp.isNull()) add.temp = temp.text().toDouble(); - else add.temp = RideFile::NoTemp; + else add.temp = RideFile::NA; // if there are data points && a time difference > 1sec && smartRecording processing is requested at all if ((!rideFile->dataPoints().empty()) && (add.secs > rtime + 1) && (isGarminSmartRecording.toInt() != 0)) { diff --git a/src/QuarqParser.cpp b/src/QuarqParser.cpp index 281b3da00..b63d18c52 100644 --- a/src/QuarqParser.cpp +++ b/src/QuarqParser.cpp @@ -51,7 +51,7 @@ QuarqParser::incrementTime( const double new_time ) while (time_diff > seconds_from_start) { rideFile->appendPoint(seconds_from_start, cad, hr, km, - kph, nm, watts, 0, 0.0, 0.0, 0.0, 0.0, RideFile::NoTemp, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); + kph, nm, watts, 0, 0.0, 0.0, 0.0, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); seconds_from_start += SAMPLE_INTERVAL; } @@ -113,7 +113,7 @@ QuarqParser::endElement( const QString&, const QString&, const QString& qName) // flush one last data point if (qName == "Qollector") { rideFile->appendPoint(seconds_from_start, cad, hr, km, - kph, nm, watts, 0, 0.0, 0.0, 0.0, 0.0, RideFile::NoTemp, + kph, nm, watts, 0, 0.0, 0.0, 0.0, 0.0, RideFile::NA, 0.0,0.0,0.0,0.0, 0.0,0.0, 0.0,0.0,0.0,0.0, diff --git a/src/RawRideFile.cpp b/src/RawRideFile.cpp index afe32d6d9..42df1046c 100644 --- a/src/RawRideFile.cpp +++ b/src/RawRideFile.cpp @@ -71,7 +71,7 @@ time_cb(struct tm *, time_t since_epoch, void *context) state->rideFile->appendPoint(secs, 0.0, 0.0, state->last_miles * KM_PER_MILE, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - RideFile::NoTemp, 0.0, + RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, 0.0, 0.0, 0.0, 0.0, @@ -93,7 +93,7 @@ data_cb(double secs, double nm, double mph, double watts, double miles, double a ReadState *state = (ReadState*) context; state->rideFile->appendPoint(secs, cad, hr, miles * KM_PER_MILE, - mph * KM_PER_MILE, nm, watts, alt, 0.0, 0.0, 0.0, 0.0, RideFile::NoTemp, 0.0, + mph * KM_PER_MILE, nm, watts, alt, 0.0, 0.0, 0.0, 0.0, RideFile::NA, 0.0, 0.0,0.0,0.0,0.0, // pedal smooth/te 0.0,0.0, 0.0,0.0,0.0,0.0, diff --git a/src/RideCache.cpp b/src/RideCache.cpp index 49635b275..5d5e154af 100644 --- a/src/RideCache.cpp +++ b/src/RideCache.cpp @@ -454,7 +454,7 @@ RideCache::getAggregate(QString name, Specification spec, bool useMetricUnits, b bool aggZero = metric->aggregateZero(); // set aggZero to false and value to zero if is temperature and -255 - if (metric->symbol() == "average_temp" && value == RideFile::NoTemp) { + if (metric->symbol() == "average_temp" && value == RideFile::NA) { value = 0; aggZero = false; } diff --git a/src/RideFile.cpp b/src/RideFile.cpp index c332688dc..e6b27454f 100644 --- a/src/RideFile.cpp +++ b/src/RideFile.cpp @@ -747,7 +747,7 @@ void RideFile::updateMin(RideFilePoint* point) minPoint->headwind = point->headwind; if (point->slopeslope) minPoint->slope = point->slope; - if (point->temp != NoTemp && point->temptemp) + if (point->temp != NA && point->temptemp) minPoint->temp = point->temp; if (minPoint->lte == 0 || point->ltelte) minPoint->lte = point->lte; @@ -826,7 +826,7 @@ void RideFile::updateMax(RideFilePoint* point) maxPoint->headwind = point->headwind; if (point->slope>maxPoint->slope) maxPoint->slope = point->slope; - if (point->temp != NoTemp && point->temp>maxPoint->temp) + if (point->temp != NA && point->temp>maxPoint->temp) maxPoint->temp = point->temp; if (point->lte>maxPoint->lte) maxPoint->lte = point->lte; @@ -893,7 +893,7 @@ void RideFile::updateAvg(RideFilePoint* point) totalPoint->lat += point->lat; totalPoint->headwind += point->headwind; totalPoint->slope += point->slope; - totalPoint->temp += point->temp == NoTemp ? 0 : point->temp; + totalPoint->temp += point->temp == NA ? 0 : point->temp; totalPoint->lte += point->lte; totalPoint->rte += point->rte; totalPoint->lps += point->lps; @@ -919,7 +919,7 @@ void RideFile::updateAvg(RideFilePoint* point) totalPoint->tcore += point->tcore; ++totalCount; - if (point->temp != NoTemp) ++totalTemp; + if (point->temp != NA) ++totalTemp; // todo : division only for last after last point avgPoint->secs = totalPoint->secs/totalCount; @@ -1034,7 +1034,7 @@ void RideFile::appendPoint(double secs, double cad, double hr, double km, dataPresent.lat |= (lat != 0); dataPresent.headwind |= (headwind != 0); dataPresent.slope |= (slope != 0); - dataPresent.temp |= (temp != NoTemp); + dataPresent.temp |= (temp != NA); dataPresent.lrbalance|= (lrbalance != 0); dataPresent.lte |= (lte != 0); dataPresent.rte |= (rte != 0); @@ -1348,7 +1348,7 @@ RideFile::getPointValue(int index, SeriesType series) const QVariant RideFile::getPointFromValue(double value, SeriesType series) const { - if (series==RideFile::temp && value == RideFile::NoTemp) + if (series==RideFile::temp && value == RideFile::NA) return ""; else if (series==RideFile::wattsKg) return ""; diff --git a/src/RideFile.h b/src/RideFile.h index 1cd794252..f77581c5c 100644 --- a/src/RideFile.h +++ b/src/RideFile.h @@ -200,7 +200,7 @@ class RideFile : public QObject // QObject to emit signals wbal, tcore, none }; // none must ALWAYS be last - enum specialValues { NoTemp = -255 }; + enum specialValues { NA = -255 }; typedef enum seriestype SeriesType; static SeriesType lastSeriesType() { return none; } diff --git a/src/RideMetric.cpp b/src/RideMetric.cpp index 060894fa9..8dfe21d20 100644 --- a/src/RideMetric.cpp +++ b/src/RideMetric.cpp @@ -127,8 +127,9 @@ // 121 3 Nov 2015 Mark Liversedge Added Work in W'bal zones // 122 7 Nov 2015 Mark Liversedge Added HR Zones 9 and 10 // 123 19 Nov 2015 Mark Liversedge Force recompute of TSS/IF after logic fix +// 124 03 Dec 2015 Mark Liversedge Min Temp -int DBSchemaVersion = 123; +int DBSchemaVersion = 124; RideMetricFactory *RideMetricFactory::_instance; QVector RideMetricFactory::noDeps; diff --git a/src/RideNavigatorProxy.h b/src/RideNavigatorProxy.h index 9d0e020c9..e0ff81a4e 100644 --- a/src/RideNavigatorProxy.h +++ b/src/RideNavigatorProxy.h @@ -377,7 +377,7 @@ public: returning = sourceModel()->data(mapToSource(proxyIndex), role); // -255 temperature means not present - if (mapToSource(proxyIndex).column() == tempIndex && returning.toDouble() == RideFile::NoTemp) { + if (mapToSource(proxyIndex).column() == tempIndex && returning.toDouble() == RideFile::NA) { returning = ""; } } diff --git a/src/RideSummaryWindow.cpp b/src/RideSummaryWindow.cpp index e39b2876f..e89a3e9c5 100644 --- a/src/RideSummaryWindow.cpp +++ b/src/RideSummaryWindow.cpp @@ -731,7 +731,7 @@ RideSummaryWindow::htmlSummary() // when summarising a ride temperature is -255 when not present, when aggregating its 0.0 if ((symbol == "average_temp" || symbol == "max_temp") && ridesummary - && rideItem->getForSymbol(symbol) == RideFile::NoTemp) { + && rideItem->getForSymbol(symbol) == RideFile::NA) { s = s.arg(ride->getTag("Temperature", "-")); diff --git a/src/ScatterPlot.cpp b/src/ScatterPlot.cpp index e54774f1d..0f33b1c49 100644 --- a/src/ScatterPlot.cpp +++ b/src/ScatterPlot.cpp @@ -974,7 +974,7 @@ ScatterPlot::skipValues(double xv, double yv, ScatterSettings *settings) { && settings->ignore && (int(xv) == 0 || yv == 0.0f)) return true; // Temp 0 values are relevant - if ((settings->x == MODEL_TEMP && xv == RideFile::NoTemp) || (settings->y == MODEL_TEMP && yv == RideFile::NoTemp)) return true; + if ((settings->x == MODEL_TEMP && xv == RideFile::NA) || (settings->y == MODEL_TEMP && yv == RideFile::NA)) return true; // LR Balance : if skip 0% skip also 100% if ((settings->x == MODEL_LRBALANCE && settings->ignore && xv == 100) || (settings->y == MODEL_LRBALANCE && settings->ignore && yv == 100)) return true; diff --git a/src/SlfParser.cpp b/src/SlfParser.cpp index 493148ca1..a894979de 100644 --- a/src/SlfParser.cpp +++ b/src/SlfParser.cpp @@ -197,7 +197,7 @@ SlfParser::endElement( const QString&, const QString&, const QString& qName) distance *= KM_PER_MILE; alt *= FEET_PER_METER; } - rideFile->appendPoint(secs, cadence, hr, distance, speed, torque, power, alt, lon, lat, headwind, 0.0, RideFile::NoTemp, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, lap); + rideFile->appendPoint(secs, cadence, hr, distance, speed, torque, power, alt, lon, lat, headwind, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, lap); secs += samplingRate; } } diff --git a/src/SmlParser.cpp b/src/SmlParser.cpp index 1b2db08a0..f448fd9b6 100644 --- a/src/SmlParser.cpp +++ b/src/SmlParser.cpp @@ -44,7 +44,7 @@ SmlParser::SmlParser(RideFile* rideFile) : rideFile(rideFile) lon = 0; lat = 0; hr = 0; - temp = RideFile::NoTemp; + temp = RideFile::NA; periodic = false; swimming = false; header = false; @@ -74,7 +74,7 @@ SmlParser::startElement(const QString&, const QString&, watts = 0; alt = 0; hr = 0; - temp = RideFile::NoTemp; + temp = RideFile::NA; periodic = false; swimming = false; } diff --git a/src/SrdRideFile.cpp b/src/SrdRideFile.cpp index b7f352e30..17eb4daec 100644 --- a/src/SrdRideFile.cpp +++ b/src/SrdRideFile.cpp @@ -92,7 +92,7 @@ RideFile *SrdFileReader::openRideFile(QFile &file, QStringList &errorStrings, QL km = w->dist_data[i]; // add to ride - result->appendPoint(time, cad, hr, km, kph, nm, watts, alt, lon, lat, wind, 0.0, RideFile::NoTemp, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); + result->appendPoint(time, cad, hr, km, kph, nm, watts, alt, lon, lat, wind, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); // keep a track of time time += w->recording_interval; diff --git a/src/TacxCafRideFile.cpp b/src/TacxCafRideFile.cpp index 382b7f9d6..b2a9e17fa 100644 --- a/src/TacxCafRideFile.cpp +++ b/src/TacxCafRideFile.cpp @@ -186,7 +186,7 @@ struct RideFilePoint readSinglePoint(const QByteArray& record, const double& tim double speed = (relativeDistance - lastDistance) / recordingIntervalInSeconds; struct RideFilePoint point(timeInSeconds, cadence, heartRate, relativeDistance / 1000.0, speed * 3.6, 0.0, power, - 0.0, 0.0, 0.0, 0.0, 0.0, RideFile::NoTemp, + 0.0, 0.0, 0.0, 0.0, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0); @@ -211,7 +211,7 @@ bool readRideData(RideFile *rideFile, const QByteArray& block, const int nrOfRec nextDataPoint.kph, nextDataPoint.nm, nextDataPoint.watts, nextDataPoint.alt, nextDataPoint.lon, nextDataPoint.lat, - nextDataPoint.headwind, 0.0, RideFile::NoTemp, 0.0, + nextDataPoint.headwind, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0, nextDataPoint.interval); } diff --git a/src/TcxParser.cpp b/src/TcxParser.cpp index 35ed5ddf2..294744e9c 100644 --- a/src/TcxParser.cpp +++ b/src/TcxParser.cpp @@ -185,7 +185,7 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName) // first point rideFile->appendPoint(secs, cadence, hr, distance, speed, torque, - power, alt, lon, lat, headwind, 0.0, RideFile::NoTemp, 0.0, + power, alt, lon, lat, headwind, 0.0, RideFile::NA, 0.0, 0.0,0.0,0.0,0.0,0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,rcad,0.0, // no running dynamics in the schema ? @@ -214,7 +214,7 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName) // no smart recording, or delta exceeds HW treshold, just insert the data rideFile->appendPoint(secs, cadence, hr, distance, speed, torque, power, - alt, lon, lat, headwind, 0.0, RideFile::NoTemp, 0.0, + alt, lon, lat, headwind, 0.0, RideFile::NA, 0.0, 0.0,0.0,0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0, @@ -268,7 +268,7 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName) badgps ? 0 : prevPoint->lat + (deltaLat * weight), // lat headwind, // headwind 0.0, - RideFile::NoTemp, + RideFile::NA, 0.0, 0.0,0.0,0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, @@ -302,7 +302,7 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName) 0.0, // lat 0.0, // headwind 0.0, - RideFile::NoTemp, + RideFile::NA, 0.0, 0.0,0.0,0.0,0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, diff --git a/src/TxtRideFile.cpp b/src/TxtRideFile.cpp index 5bb57f031..28942e3f2 100644 --- a/src/TxtRideFile.cpp +++ b/src/TxtRideFile.cpp @@ -286,7 +286,7 @@ RideFile *TxtFileReader::openRideFile(QFile &file, QStringList &errors, QListappendPoint(sample.secs, sample.cad, sample.hr, sample.km, sample.kph, 0.0, sample.watts, 0.0, 0.0, 0.0, - sample.headwind, 0.0, RideFile::NoTemp, 0.0, + sample.headwind, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0,0.0,0.0, 0.0,0.0,0.0,0.0,0.0,0.0, 0); @@ -455,7 +455,7 @@ RideFile *TxtFileReader::openRideFile(QFile &file, QStringList &errors, QList= 0) torq = tokens.at(torqIndex).toDouble(); if (wattsIndex >= 0) watts = tokens.at(wattsIndex).toDouble(); - rideFile->appendPoint(secs, rpm, bpm, km, kph, torq, watts, 0.0, 0.0, 0.0, 0.0, 0.0, RideFile::NoTemp, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0, 0); + rideFile->appendPoint(secs, rpm, bpm, km, kph, torq, watts, 0.0, 0.0, 0.0, 0.0, 0.0, RideFile::NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0, 0); } } diff --git a/src/WkoRideFile.cpp b/src/WkoRideFile.cpp index 846e4c4d9..87f8242e0 100644 --- a/src/WkoRideFile.cpp +++ b/src/WkoRideFile.cpp @@ -207,7 +207,7 @@ WKO_UCHAR * WkoParser::parseRawData(WKO_UCHAR *fb) { WKO_ULONG WKO_xormasks[32]; // xormasks used all over - double cad=0, hr=0, km=0, kph=0, nm=0, watts=0, slope=0, alt=0, lon=0, lat=0, wind=0, temp=RideFile::NoTemp, interval=0; + double cad=0, hr=0, km=0, kph=0, nm=0, watts=0, slope=0, alt=0, lon=0, lat=0, wind=0, temp=RideFile::NA, interval=0; int isnull=0; WKO_ULONG records=0, data; @@ -308,7 +308,7 @@ WkoParser::parseRawData(WKO_UCHAR *fb) // reset point values; alt = slope = wind = cad= hr= km= kph= nm= watts= 0.0; - temp= RideFile::NoTemp; + temp= RideFile::NA; marker = get_bits(thelot, bit++, 1); diff --git a/test/charts/Temperature.xml b/test/charts/Temperature.xml new file mode 100644 index 000000000..ea0fd8731 --- /dev/null +++ b/test/charts/Temperature.xml @@ -0,0 +1,3 @@ + + "AAAAFgBUAGUAbQBwAGUAcgBhAHQAdQByAGUAAAAWAFQAZQBtAHAAZQByAGEAdAB1AHIAZQAldKQAAAAA/wAldkEAAAAA/wAAAAEAAP///////////////wAAABAAAAAEAAAACAD/////AAAAAAAAAAYATQBhAHgAAAACAEMAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAQBAAAAAAHD+OAAAAACAAAAAAH/////AAAAAAAAAAAAAD/wAAAAAAAAAAAAAAH//wAAAAAAAAAAAAAAAAAAAAPnAAAOEP////8AAAAKAAAAAAAAAAAAAAAADAAyACAAUABhAHIAbQAAAAAAAAPnAAAOEAAAAAAA/////wAAAQYAIwAgAHQAeQBwAGUAIABpAG4AIABhACAAZgBvAHIAbQB1AGwAYQAgAHQAbwAgAHUAcwBlAAoAIwAgAGYAbwByACAAZQAuAGcALgAgAFQAUwBTACAALwAgAEQAdQByAGEAdABpAG8AbgAKACMAIABhAHMAIAB5AG8AdQAgAHQAeQBwAGUAIAB0AGgAZQAgAGEAdgBhAGkAbABhAGIAbABlACAAbQBlAHQAcgBpAGMAcwAKACMAIAB3AGkAbABsACAAYgBlACAAbwBmAGYAZQByAGUAZAAgAGIAeQAgAGEAdQB0AG8AYwBvAG0AcABsAGUAdABlAAoATQBhAHgAXwBUAGUAbQBwAAAAAgAAAA4AcwBlAGEAcgBjAGgAOgAAAAgA/////wAAAAAAAAAOAEEAdgBlAHIAYQBnAGUAAAACAEMAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAQBAAAAAAHD+OAAAAACAAAAAAH//1VV//8AAAAAAAAAAD/wAAAAAAAAAAAAAAH//wAAAAAAAAAAAAAAAAAAAAPnAAAOEP////8AAAAKAAAAAAAAAAAAAAAADAAyACAAUABhAHIAbQAAAAQAAAAAAAAOEAAAAAAA/////wAAAQ4AIwAgAHQAeQBwAGUAIABpAG4AIABhACAAZgBvAHIAbQB1AGwAYQAgAHQAbwAgAHUAcwBlAAoAIwAgAGYAbwByACAAZQAuAGcALgAgAFQAUwBTACAALwAgAEQAdQByAGEAdABpAG8AbgAKACMAIABhAHMAIAB5AG8AdQAgAHQAeQBwAGUAIAB0AGgAZQAgAGEAdgBhAGkAbABhAGIAbABlACAAbQBlAHQAcgBpAGMAcwAKACMAIAB3AGkAbABsACAAYgBlACAAbwBmAGYAZQByAGUAZAAgAGIAeQAgAGEAdQB0AG8AYwBvAG0AcABsAGUAdABlAAoAQQB2AGUAcgBhAGcAZQBfAFQAZQBtAHAAAAABAAAADgBzAGUAYQByAGMAaAA6AAAACAD/////AAAAAAAAAAYATQBpAG4AAAACAEMAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAQBAAAAAAHD+OAAAAACAAAAAAH//1VVqqr//wAAAAAAAD/wAAAAAAAAAAAAAAH//wAAAAAAAAAAAAAAAAAAAAPnAAAOEP////8AAAAKAAAAAAAAAAAAAAAADAAyACAAUABhAHIAbQAAAAAAAAPnAAAOEAAAAAAA/////wAAAQYAIwAgAHQAeQBwAGUAIABpAG4AIABhACAAZgBvAHIAbQB1AGwAYQAgAHQAbwAgAHUAcwBlAAoAIwAgAGYAbwByACAAZQAuAGcALgAgAFQAUwBTACAALwAgAEQAdQByAGEAdABpAG8AbgAKACMAIABhAHMAIAB5AG8AdQAgAHQAeQBwAGUAIAB0AGgAZQAgAGEAdgBhAGkAbABhAGIAbABlACAAbQBlAHQAcgBpAGMAcwAKACMAIAB3AGkAbABsACAAYgBlACAAbwBmAGYAZQByAGUAZAAgAGIAeQAgAGEAdQB0AG8AYwBvAG0AcABsAGUAdABlAAoATQBpAG4AXwBUAGUAbQBwAAAAAwAAAA4AcwBlAGEAcgBjAGgAOgAAAAgA/////wAAAAAAAAAIAEMAbwByAGUAAAACAEMAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAQBAAAAAAHD+OAAAAACAAAAAAH//6qqqqp/fwAAAAAAAD/wAAAAAAAAAAAAAAH//wAAAAAAAAAAAAAAAAAAAAPnAAAOEP////8AAAAKAAAAAAAAAAAAAAAADAAyACAAUABhAHIAbQAAAAQAAAAAAAAOEAAAAAAA/////wAAASYAIwAgAHQAeQBwAGUAIABpAG4AIABhACAAZgBvAHIAbQB1AGwAYQAgAHQAbwAgAHUAcwBlAAoAIwAgAGYAbwByACAAZQAuAGcALgAgAFQAUwBTACAALwAgAEQAdQByAGEAdABpAG8AbgAKACMAIABhAHMAIAB5AG8AdQAgAHQAeQBwAGUAIAB0AGgAZQAgAGEAdgBhAGkAbABhAGIAbABlACAAbQBlAHQAcgBpAGMAcwAKACMAIAB3AGkAbABsACAAYgBlACAAbwBmAGYAZQByAGUAZAAgAGIAeQAgAGEAdQB0AG8AYwBvAG0AcABsAGUAdABlAAoAQQB2AGUAcgBhAGcAZQBfAEMAbwByAGUAXwBUAGUAbQBwAGUAcgBhAHQAdQByAGUAAAABAAAADgBzAGUAYQByAGMAaAA6AAAAAAAD" +