mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Merge pull request #2856 from bwalding/lap-distance
Add Lap Distance and Lap Distance Remaining dials and telemetry
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user