Merge pull request #2856 from bwalding/lap-distance

Add Lap Distance and Lap Distance Remaining dials and telemetry
This commit is contained in:
Mark Liversedge
2018-04-29 10:37:25 +01:00
committed by GitHub
7 changed files with 177 additions and 8 deletions

View File

@@ -241,7 +241,16 @@ DialWindow::telemetryUpdate(const RealtimeData &rtData)
valueLabel->setText(QString("%1").arg(value, 0, 'f', 1));
break;
// If the distance remaining is negative, it is either irrelevant, or we've passed the last lap marker
case RealtimeData::LapDistanceRemaining:
if (value < 0) {
valueLabel->setText("N/A");
break;
}
// intentional fall-through to standard distance rendering
case RealtimeData::Distance:
case RealtimeData::LapDistance:
if (!context->athlete->useMetricUnits) value *= MILES_PER_KM;
valueLabel->setText(QString("%1").arg(value, 0, 'f', 3));
break;
@@ -543,6 +552,8 @@ void DialWindow::seriesChanged()
case RealtimeData::LapTime:
case RealtimeData::LapTimeRemaining:
case RealtimeData::Distance:
case RealtimeData::LapDistance:
case RealtimeData::LapDistanceRemaining:
case RealtimeData::LRBalance:
case RealtimeData::Lap:
case RealtimeData::RI:

View File

@@ -553,14 +553,23 @@ void ErgFile::parseComputrainer(QString p)
if (section == END && Points.count() > 0) {
valid = true;
// add the last point for a crs file
if (mode == CRS) {
// Add the last point for a crs file
ErgFilePoint add;
add.x = rdist;
add.val = 0.0;
add.y = ralt;
Points.append(add);
if (add.y > MaxWatts) MaxWatts=add.y;
// Add a final meta-lap at the end - causes the system to correctly mark off laps.
// An improvement would be to check for a lap marker at end, and only add this if required.
ErgFileLap lap;
lap.x = rdist;
lap.LapNum = ++lapcounter;
lap.selected = false;
lap.name = lapmarker.cap(2).simplified();
Laps.append(lap);
}
// add a start point if it doesn't exist
@@ -1014,12 +1023,16 @@ ErgFile::gradientAt(long x, int &lapnum)
return Points.at(leftPoint).val;
}
// Retrieve the offset for the start of next lap.
// Params: x - current workout distance (m) / time (ms)
// Returns: distance (m) / time (ms) offset for next lap.
int ErgFile::nextLap(long x)
{
if (!isValid()) return -1; // not a valid ergfile
// do we need to return the Lap marker?
if (Laps.count() > 0) {
// If the current position is before the start the lap, then the lap is next
for (int i=0; i<Laps.count(); i++) {
if (x<Laps.at(i).x) return Laps.at(i).x;
}
@@ -1027,6 +1040,21 @@ int ErgFile::nextLap(long x)
return -1; // nope, no marker ahead of there
}
// Retrieve the offset for the start of current lap.
// Params: x - current workout distance (m) / time (ms)
// Returns: distance (m) / time (ms) offset for start of current lap.
int
ErgFile::currentLap(long x)
{
if (!isValid()) return -1; // not a valid ergfile
// If the current position is before the start of the next lap, return this lap
for (int i=0; i<Laps.count() - 1; i++) {
if (x<Laps.at(i+1).x) return Laps.at(i).x;
}
return -1; // No matching lap
}
void
ErgFile::calculateMetrics()
{

View File

@@ -112,7 +112,9 @@ class ErgFile
int format; // ERG, CRS or MRC currently supported
int wattsAt(long, int&); // return the watts value for the passed msec
double gradientAt(long, int&); // return the gradient value for the passed meter
int nextLap(long); // return the msecs value for the next Lap marker
int nextLap(long); // return the start value (erg - time(ms) or slope - distance(m)) for the next lap
int currentLap(long); // return the start value (erg - time(ms) or slope - distance(m)) for the current lap
// turn the ergfile into a series of sections rather
// than a list of points

View File

@@ -106,6 +106,16 @@ void RealtimeData::setDistance(double x)
this->distance = x;
}
void RealtimeData::setLapDistance(double x)
{
this->lapDistance = x;
}
void RealtimeData::setLapDistanceRemaining(double x)
{
this->lapDistanceRemaining = x;
}
void RealtimeData::setLRBalance(double x)
{
this->lrbalance = x;
@@ -194,6 +204,14 @@ double RealtimeData::getDistance() const
{
return distance;
}
double RealtimeData::getLapDistance() const
{
return lapDistance;
}
double RealtimeData::getLapDistanceRemaining() const
{
return lapDistanceRemaining;
}
double RealtimeData::getLRBalance() const
{
return lrbalance;
@@ -298,6 +316,12 @@ double RealtimeData::value(DataSeries series) const
case Distance: return distance;
break;
case LapDistance: return lapDistance;
break;
case LapDistanceRemaining: return lapDistanceRemaining;
break;
case AltWatts: return altWatts;
break;
@@ -406,6 +430,8 @@ const QList<RealtimeData::DataSeries> &RealtimeData::listDataSeries()
seriesList << LeftPedalSmoothness;
seriesList << RightPedalSmoothness;
seriesList << Slope;
seriesList << LapDistance;
seriesList << LapDistanceRemaining;
}
return seriesList;
}
@@ -537,6 +563,12 @@ QString RealtimeData::seriesName(DataSeries series)
case Slope: return tr("Slope");
break;
case LapDistance: return tr("Lap Distance");
break;
case LapDistanceRemaining: return tr("Lap Distance Remaining");
break;
}
}

View File

@@ -43,7 +43,8 @@ public:
AvgWattsLap, AvgSpeedLap, AvgCadenceLap, AvgHeartRateLap,
VirtualSpeed, AltWatts, LRBalance, LapTimeRemaining,
LeftTorqueEffectiveness, RightTorqueEffectiveness,
LeftPedalSmoothness, RightPedalSmoothness, Slope};
LeftPedalSmoothness, RightPedalSmoothness, Slope,
LapDistance, LapDistanceRemaining };
typedef enum dataseries DataSeries;
@@ -75,6 +76,8 @@ public:
void setJoules(long);
void setXPower(long);
void setLap(long);
void setLapDistance(double distance);
void setLapDistanceRemaining(double distance);
void setLRBalance(double);
void setLTE(double);
void setRTE(double);
@@ -107,6 +110,8 @@ public:
long getLapMsecs() const;
double getDistance() const;
long getLap() const;
double getLapDistance() const;
double getLapDistanceRemaining() const;
double getLRBalance() const;
double getLTE() const;
double getRTE() const;
@@ -141,6 +146,8 @@ private:
// derived data
double distance;
double lapDistance;
double lapDistanceRemaining;
double virtualSpeed;
double wbal;
double hhb, o2hb;

View File

@@ -812,11 +812,67 @@ TrainSidebar::workoutTreeWidgetSelectionChanged()
foreach(int dev, activeDevices) Devices[dev].controller->setMode(RT_MODE_SPIN);
}
updateMetricLapDistanceRemaining();
// clean last
if (prior) delete prior;
}
/*
* Calculates the current lap distance
*
* Needs to be called as the lapping occurs, otherwise you get
* skew due to inaccuracies in lap calculations.
*/
void
TrainSidebar::updateMetricLapDistance()
{
// lapDistance is only relevant for SLOPE ERG files
if (!ergFile || !(status&RT_MODE_SLOPE)) {
displayLapDistance = 0;
return;
}
// XXX This might have sub-optimal display in the final lap of a file.
double currentposition = displayWorkoutDistance*1000;
double lapmarker = ergFile->currentLap(currentposition);
if (lapmarker == -1) {
displayLapDistance = 0;
return;
}
displayLapDistance = (currentposition - lapmarker) / (double) 1000;
}
/*
* Calculates the lap distance remaining in the current lap.
*
* Can be called at any time, but better to just decrement the displayLapDistanceRemaining
* variable as the workout progresses.
*/
void
TrainSidebar::updateMetricLapDistanceRemaining()
{
// lapDistanceRemaining is only relevant for SLOPE ERG files
if (!ergFile || !(status&RT_MODE_SLOPE)) {
displayLapDistanceRemaining = -1;
return;
}
// Review what happens when we are at the end of the course and there are no more lap markers.
// perhaps we should look at course length.
double currentposition = displayWorkoutDistance*1000;
double lapmarker = ergFile->nextLap(currentposition);
if (lapmarker == -1) {
// In this case, there are either no lap markers, or we are in last lap (and so no next lap)
displayLapDistanceRemaining = -1;
return;
}
displayLapDistanceRemaining = (lapmarker - currentposition) / (double) 1000;
}
QStringList
TrainSidebar::listWorkoutFiles(const QDir &dir) const
{
@@ -1352,6 +1408,8 @@ void TrainSidebar::Stop(int deviceStatus) // when stop button is pressed
lap_elapsed_msec = 0;
lap_time.restart();
displayWorkoutDistance = displayDistance = 0;
displayLapDistance = 0;
displayLapDistanceRemaining = -1;
guiUpdate();
emit setNotification(tr("Stopped.."), 2);
@@ -1556,6 +1614,8 @@ void TrainSidebar::guiUpdate() // refreshes the telemetry
if (dev == kphTelemetry) {
rtData.setSpeed(local.getSpeed());
rtData.setDistance(local.getDistance());
rtData.setLapDistance(local.getLapDistance());
rtData.setLapDistanceRemaining(local.getLapDistanceRemaining());
}
if (dev == wattsTelemetry) {
rtData.setWatts(local.getWatts());
@@ -1580,16 +1640,29 @@ void TrainSidebar::guiUpdate() // refreshes the telemetry
// only update time & distance if actively running (not just connected, and not running but paused)
if ((status&RT_RUNNING) && ((status&RT_PAUSED) == 0)) {
// Distance assumes current speed for the last second. from km/h to km/sec
displayDistance += displaySpeed / (5 * 3600); // assumes 200ms refreshrate
double distanceTick = displaySpeed / (5 * 3600); // assumes 200ms refreshrate
displayDistance += distanceTick;
displayLapDistance += distanceTick;
displayLapDistanceRemaining -= distanceTick;
if (!(status&RT_MODE_ERGO) && (context->currentVideoSyncFile()))
{
if (!(status&RT_MODE_ERGO) && (context->currentVideoSyncFile())) {
displayWorkoutDistance = context->currentVideoSyncFile()->km + context->currentVideoSyncFile()->manualOffset;
// TODO : graphs to be shown at seek position
} else {
displayWorkoutDistance += distanceTick;
}
else
displayWorkoutDistance += displaySpeed / (5 * 3600); // assumes 200ms refreshrate
// If we just tripped over the end of the lap, we need to look at base data
// to find distance to next lap. This is primarily due to lap display updates
// -0.999 is chosen as a number that is less than 0, but greater than -1
if (displayLapDistanceRemaining < 0 && displayLapDistanceRemaining > -0.999) {
updateMetricLapDistanceRemaining();
}
rtData.setDistance(displayDistance);
rtData.setLapDistance(displayLapDistance);
rtData.setLapDistanceRemaining(displayLapDistanceRemaining);
// time
total_msecs = session_elapsed_msec + session_time.elapsed();
@@ -1632,6 +1705,8 @@ void TrainSidebar::guiUpdate() // refreshes the telemetry
rtData.setLapMsecsRemaining(lapTimeRemaining);
} else {
rtData.setDistance(displayDistance);
rtData.setLapDistance(displayLapDistance);
rtData.setLapDistanceRemaining(displayLapDistanceRemaining);
rtData.setMsecs(session_elapsed_msec);
rtData.setLapMsecs(lap_elapsed_msec);
}
@@ -1737,6 +1812,10 @@ void TrainSidebar::newLap()
hrcount = 0;
spdcount = 0;
// This forces a hard reset of the lap marker.
displayLapDistance = 0;
updateMetricLapDistanceRemaining();
context->notifyNewLap();
emit setNotification(tr("New lap.."), 2);
@@ -1748,6 +1827,8 @@ void TrainSidebar::resetLapTimer()
lap_time.restart();
lap_elapsed_msec = 0;
lapAudioThisLap = true;
displayLapDistance = 0;
this->updateMetricLapDistanceRemaining();
}
// Can be called from the controller - when user steers to scroll display
@@ -1836,6 +1917,8 @@ void TrainSidebar::loadUpdate()
if(displayWorkoutLap != curLap)
{
context->notifyNewLap();
updateMetricLapDistance();
updateMetricLapDistanceRemaining();
}
displayWorkoutLap = curLap;
@@ -1852,6 +1935,8 @@ void TrainSidebar::loadUpdate()
if(displayWorkoutLap != curLap)
{
context->notifyNewLap();
updateMetricLapDistance();
updateMetricLapDistanceRemaining();
}
displayWorkoutLap = curLap;

View File

@@ -247,6 +247,7 @@ class TrainSidebar : public GcWindow
double displayLRBalance, displayLTE, displayRTE, displayLPS, displayRPS;
double displaySMO2, displayTHB, displayO2HB, displayHHB;
double displayDistance, displayWorkoutDistance;
double displayLapDistance, displayLapDistanceRemaining;
long load;
double slope;
int displayLap; // user increment for Lap
@@ -254,6 +255,9 @@ class TrainSidebar : public GcWindow
bool lapAudioEnabled;
bool lapAudioThisLap;
void updateMetricLapDistance();
void updateMetricLapDistanceRemaining();
// for non-zero average calcs
int pwrcount, cadcount, hrcount, spdcount, lodcount, grdcount; // for NZ average calc
int status;