From 721cd2646b6ba434aa98752d2ed16a72e13877bd Mon Sep 17 00:00:00 2001 From: peterbrant14 Date: Mon, 5 Mar 2018 10:48:21 +0000 Subject: [PATCH 1/4] Fix error in RLV distance calculation when using ffwd or rwd In guiUpdate, don't add manualOffset from VideoSyncFile.cpp when updating displayWorkoutDistance as its already been included in the km value. --- src/Train/TrainSidebar.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Train/TrainSidebar.cpp b/src/Train/TrainSidebar.cpp index f60d8ee35..6aa81c2a9 100644 --- a/src/Train/TrainSidebar.cpp +++ b/src/Train/TrainSidebar.cpp @@ -1667,9 +1667,9 @@ void TrainSidebar::guiUpdate() // refreshes the telemetry displayLapDistance += distanceTick; displayLapDistanceRemaining -= distanceTick; - - if (!(status&RT_MODE_ERGO) && (context->currentVideoSyncFile())) { - displayWorkoutDistance = context->currentVideoSyncFile()->km + context->currentVideoSyncFile()->manualOffset; + if (!(status&RT_MODE_ERGO) && (context->currentVideoSyncFile())) + { + displayWorkoutDistance = context->currentVideoSyncFile()->km; // TODO : graphs to be shown at seek position } else { displayWorkoutDistance += distanceTick; From 3f177cae9e24ae2a65e3ee56742f26cc6007d32f Mon Sep 17 00:00:00 2001 From: peterbrant14 Date: Wed, 14 Mar 2018 18:25:59 +0000 Subject: [PATCH 2/4] Fix truncation of Workouts with respect to pgmf file and corresponding rlv Add a final point to the ErgFilePoint list to terminate the last section of the ride --- src/Train/ErgFile.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Train/ErgFile.cpp b/src/Train/ErgFile.cpp index c0e2867a3..0ab214349 100644 --- a/src/Train/ErgFile.cpp +++ b/src/Train/ErgFile.cpp @@ -401,6 +401,13 @@ void ErgFile::parseTacx() // if we got here and are still happy then it // must have been a valid file. if (happy) { + // Set the end point for the workout + ErgFilePoint end; + end.x = rdist; + end.y = Points.last().y; + end.val = Points.last().val; + Points.append(end); + valid = true; // set ErgFile duration From 49bc592a5634f713d740abc973d556c93b661bdb Mon Sep 17 00:00:00 2001 From: peterbrant14 Date: Thu, 22 Mar 2018 17:43:44 +0000 Subject: [PATCH 3/4] Improve RLV video syncronization. Revised code in VideoSyncFile::parseTacx to build syncpoint list with correct distances when speed varies between two points Revised code in VideoWindow::telemetryUpdate to interpolate position between 2 sync points. Also do not update video if paused or not running. Revised code in VideoWindow::startPlayback to set a minimal rate to start if video is controlled by syncfile. This avoids the initial "rush" that otherwise happened and makes for a smoother start. Also reset distance to 0 on start. As the rlv length now more accurately matches the workout length, we also need a check in TrainSidebar::guiUpdate that will terminate the workout if the end of the video is reached. --- src/Train/TrainSidebar.cpp | 5 ++++ src/Train/VideoSyncFile.cpp | 12 ++++++-- src/Train/VideoWindow.cpp | 56 +++++++++++++++++++++++++++++++------ 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/Train/TrainSidebar.cpp b/src/Train/TrainSidebar.cpp index 6aa81c2a9..6493f03dc 100644 --- a/src/Train/TrainSidebar.cpp +++ b/src/Train/TrainSidebar.cpp @@ -1670,6 +1670,11 @@ void TrainSidebar::guiUpdate() // refreshes the telemetry if (!(status&RT_MODE_ERGO) && (context->currentVideoSyncFile())) { displayWorkoutDistance = context->currentVideoSyncFile()->km; + // If we reached the end of the RLV then stop + if (displayWorkoutDistance >= context->currentVideoSyncFile()->Distance) { + Stop(DEVICE_OK); + return; + } // TODO : graphs to be shown at seek position } else { displayWorkoutDistance += distanceTick; diff --git a/src/Train/VideoSyncFile.cpp b/src/Train/VideoSyncFile.cpp index bc3121ce8..61bb0bc07 100644 --- a/src/Train/VideoSyncFile.cpp +++ b/src/Train/VideoSyncFile.cpp @@ -164,6 +164,8 @@ void VideoSyncFile::parseRLV() { // read in the mapping records uint32_t PreviousFrameNbr=0; + double PreviousDistance=0; + for (unsigned int record=0; record < info.records; record++) { // get the next record if (sizeof(framemapping) != input.readRawData((char*)&framemapping, sizeof(framemapping))) { @@ -171,11 +173,16 @@ void VideoSyncFile::parseRLV() break; } + if (record == 0) PreviousDistance = framemapping.distance; VideoSyncFilePoint add; if (format == RLV) { - double distance = framemapping.distance; // in meters per frame - rdist += distance * (double) (framemapping.frameNbr - PreviousFrameNbr); + // Calculate average distance per video frame between this sync point and the last + // using the formula (2a+b)/3 where a is the point of lower speed and b is the higher. + double distance = framemapping.distance; + double avgDistance = (qMin(distance, PreviousDistance)*2 + qMax(distance, PreviousDistance)) / 3; + + rdist += avgDistance * (double) (framemapping.frameNbr - PreviousFrameNbr); add.km = rdist / 1000.0; // time @@ -186,6 +193,7 @@ void VideoSyncFile::parseRLV() // FIXME : do we have to consider video offset and weight ? PreviousFrameNbr = framemapping.frameNbr; + PreviousDistance = distance; } Points.append(add); diff --git a/src/Train/VideoWindow.cpp b/src/Train/VideoWindow.cpp index 07c0375b0..f964ffe6b 100644 --- a/src/Train/VideoWindow.cpp +++ b/src/Train/VideoWindow.cpp @@ -206,8 +206,10 @@ void VideoWindow::resizeEvent(QResizeEvent * ) void VideoWindow::startPlayback() { - if (context->currentVideoSyncFile()) + if (context->currentVideoSyncFile()) { context->currentVideoSyncFile()->manualOffset = 0.0; + context->currentVideoSyncFile()->km = 0.0; + } #ifdef GC_VIDEO_VLC if (!m) return; // ignore if no media selected @@ -218,8 +220,12 @@ void VideoWindow::startPlayback() /* set the media to playback */ libvlc_media_player_set_media (mp, m); - /* set the playback rate to the media default - since there may be a different one set from RLV */ - libvlc_media_player_set_rate(mp, libvlc_media_player_get_fps(mp)); + /* Reset playback rate */ + /* If video speed will be controlled by a sync file, set almost stationary + until first telemetry update. Otherwise (re)set to normal rate */ + if (context->currentVideoSyncFile() && context->currentVideoSyncFile()->Points.count() > 1) + libvlc_media_player_set_rate(mp, 0.1f); + else libvlc_media_player_set_rate(mp, 1.0f); /* play the media_player */ libvlc_media_player_play (mp); @@ -402,7 +408,8 @@ void VideoWindow::telemetryUpdate(RealtimeData rtd) #endif #ifdef GC_VIDEO_VLC - if (!m) return; + if (!m || !context->isRunning || context->isPaused) + return; QList *ErgFilePoints = NULL; if (context->currentErgFile()) { @@ -431,11 +438,44 @@ void VideoWindow::telemetryUpdate(RealtimeData rtd) while ((VideoSyncFiledataPoints[curPosition].km <= CurrentDistance) && (curPosition < VideoSyncFiledataPoints.count()-1)) curPosition++; - // update the rfp - float weighted_average = (VideoSyncFiledataPoints[curPosition].km - VideoSyncFiledataPoints[curPosition-1].km != 0.0)?(CurrentDistance-VideoSyncFiledataPoints[curPosition-1].km) / (VideoSyncFiledataPoints[curPosition].km - VideoSyncFiledataPoints[curPosition-1].km):0.0; + /* Create an RFP to represent where we are */ + VideoSyncFilePoint syncPrevious = VideoSyncFiledataPoints[curPosition-1]; + VideoSyncFilePoint syncNext = VideoSyncFiledataPoints[curPosition]; + double syncKmDelta = syncNext.km - syncPrevious.km; + double syncKphDelta = syncNext.kph - syncPrevious.kph; + double syncTimeDelta = syncNext.secs - syncPrevious.secs; + double distanceFactor, speedFactor, timeFactor, timeExtra; + + // Calculate how far we are between points in terms of distance + if (syncKmDelta == 0) distanceFactor = 0.0; + else distanceFactor = (CurrentDistance - syncPrevious.km) / syncKmDelta; + + // Now create the appropriate factors and interpolate the + // video speed and time for the point we have reached. + // If there has been no acceleration we can just use use the distance factor + if (syncKphDelta == 0) { + // Constant filming speed + rfp.kph = syncPrevious.kph; + rfp.secs = syncPrevious.secs + syncTimeDelta * distanceFactor; + } + else { + // Calculate time difference because of change in speed + timeExtra = syncTimeDelta - ((syncKmDelta / syncPrevious.kph) * 3600); + if (syncKphDelta > 0) { + // The filming speed increased + speedFactor = qPow(distanceFactor, 0.66667); + timeFactor = qPow(distanceFactor, 0.33333); + rfp.kph = syncPrevious.kph + speedFactor * syncKphDelta; + } + else { + // The filming speed decreased + speedFactor = 1 - qPow(distanceFactor, 1.5); + timeFactor = qPow(distanceFactor, 3.0); + rfp.kph = syncNext.kph - speedFactor * syncKphDelta; + } + rfp.secs = syncPrevious.secs + (distanceFactor * (syncTimeDelta - timeExtra)) + (timeFactor * timeExtra); + } rfp.km = CurrentDistance; - rfp.secs = VideoSyncFiledataPoints[curPosition-1].secs + weighted_average * (VideoSyncFiledataPoints[curPosition].secs - VideoSyncFiledataPoints[curPosition-1].secs); - rfp.kph = VideoSyncFiledataPoints[curPosition-1].kph + weighted_average * (VideoSyncFiledataPoints[curPosition].kph - VideoSyncFiledataPoints[curPosition-1].kph); /* //TODO : GPX file format From 7cce47526f2f204a4c583945f7205661d0bcafcc Mon Sep 17 00:00:00 2001 From: peterbrant14 Date: Mon, 5 Mar 2018 10:15:46 +0000 Subject: [PATCH 4/4] Allow for RLV files with no final speed indicator Some older (Tacx) RLVs do not have a sync point at the end of the course. This work-around calculates the total distance of the course and sets a final sync point if one does not already exist. --- src/Train/VideoSyncFile.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Train/VideoSyncFile.cpp b/src/Train/VideoSyncFile.cpp index 61bb0bc07..085178fbc 100644 --- a/src/Train/VideoSyncFile.cpp +++ b/src/Train/VideoSyncFile.cpp @@ -72,6 +72,7 @@ void VideoSyncFile::parseRLV() // running totals double rdist = 0; // running total for distance + double rend = 0; // Length of entire course // open the file for binary reading and open a datastream QFile RLVFile(filename); @@ -228,6 +229,7 @@ void VideoSyncFile::parseRLV() happy = false; break; } + if (courseInfo.end > rend) rend = courseInfo.end; } // FIXME : add those data to training messages } @@ -244,6 +246,17 @@ void VideoSyncFile::parseRLV() } else happy = false; } + // Make sure we have a sync point to represent the end of the course + // Some earlier rlvs don't supply an end point frame reference, + // so we need to assume the speed from the last ref point continues to the end + if ((!Points.isEmpty()) && (rend > Points.last().km * 1000) && Points.last().kph > 0) { + VideoSyncFilePoint add; + add.km = rend / 1000; + add.kph = Points.last().kph; + add.secs = Points.last().secs + ((add.km - Points.last().km) * 3600) / add.kph; + Points.append(add); + } + // done RLVFile.close();