diff --git a/src/FileIO/LocationInterpolation.cpp b/src/FileIO/LocationInterpolation.cpp index 6e28d50d5..f60447778 100644 --- a/src/FileIO/LocationInterpolation.cpp +++ b/src/FileIO/LocationInterpolation.cpp @@ -394,7 +394,6 @@ void GeoPointInterpolator::Push(double distance, double altitude) geolocation geo(0, 0, altitude); xyz point = geo.toxyz(); - point.y() = distance; DistancePointInterpolator::Push(distance, point); } diff --git a/src/FileIO/LocationInterpolation.h b/src/FileIO/LocationInterpolation.h index 00d928788..591e5ebff 100644 --- a/src/FileIO/LocationInterpolation.h +++ b/src/FileIO/LocationInterpolation.h @@ -793,6 +793,11 @@ public: GeoPointInterpolator() : DistancePointInterpolator(), m_locationState(Unset) {} + void Reset() { + m_locationState = Unset; + DistancePointInterpolator::Reset(); + } + geolocation Location(double distance); geolocation Location(double distance, double &slope); diff --git a/src/FileIO/TTSReader.cpp b/src/FileIO/TTSReader.cpp index c34c9359e..71e21a750 100644 --- a/src/FileIO/TTSReader.cpp +++ b/src/FileIO/TTSReader.cpp @@ -16,7 +16,7 @@ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -// Adapted to C++ from Wattzap Community Edition Java source code. +// Data reader adapted to C++ from Wattzap Community Edition Java source code. #include "TTSReader.h" #include "LocationInterpolation.h" @@ -389,100 +389,6 @@ double TTSReader::getMinSlope() const { return minSlope; } -// The thing about duplicates is... you must treat the entire span of duplicates -// as bogus, not just the ones after the first. Because often they all have the same -// altitude and its important to amortize that change across the span of duplicates. -// -// The sad truth is that routes with duplicates have low accuracy gps data and we -// shouldn't even trust the non-duplicates. We should be averaging the 'good' points -// even before interpolating the bad ones. -// -// Function returns next 'non-duplicate' index. At end this function returns points.size(). -unsigned TTSReader::findNextNonDuplicateGeolocation(unsigned u) const -{ - geolocation geo(points[u].getLatitude(), points[u].getLongitude(), 0); - - unsigned idx = u + 1; - while (idx < points.size()) { - geolocation geoIdx(points[idx].getLatitude(), points[idx].getLongitude(), 0); - - double distance = geo.DistanceFrom(geoIdx); - if (distance == 0.) { - idx++; - continue; - } - - int distanceExp; - std::frexp(distance, &distanceExp); - - // If outside of 2^-12 then they are not the same location. - if (distanceExp < -12) { - idx++; - continue; - } - - break; - } - - return idx; -} - -unsigned TTSReader::interpolateDuplicateLocations() -{ - // Some tts files I see have an altitude crisis around the start: Fixup altitude - // of index 1 by linear interpolating from 2 using 1's gradient. - double d2d1delta = points[2].getDistanceFromStart() - points[1].getDistanceFromStart(); - points[1].setElevation(points[2].getElevation() - (0.01 * points[1].getGradient()*d2d1delta)); - - GeoPointInterpolator gpi; - - gpi.Push(points[1].getDistanceFromStart(), geolocation(points[1].getLatitude(), points[1].getLongitude(), points[1].getElevation())); - unsigned ii = 2; - - unsigned uRewriteCount = 0; - unsigned idx = 2; - while (idx < points.size()) { - - unsigned duplicateEndIdx = findNextNonDuplicateGeolocation(idx); - - if (duplicateEndIdx != (idx + 1)) { - - while (idx < duplicateEndIdx) { - - Point & reP = points[idx]; - double reDist = reP.getDistanceFromStart(); - while (gpi.WantsInput(reDist)) { - - ii = findNextNonDuplicateGeolocation(ii); - - // At end, push that final point (stored at index 0) and notify complete. - if (ii >= (points.size() - 1)) { - Point &p = points[0]; - gpi.Push(p.getDistanceFromStart(), geolocation(p.getLatitude(), p.getLongitude(), p.getElevation())); - gpi.NotifyInputComplete(); - } else { - Point &p = points[ii]; - gpi.Push(p.getDistanceFromStart(), geolocation(p.getLatitude(), p.getLongitude(), p.getElevation())); - } - } - - geolocation n = gpi.Location(reDist); - - reP.setLatitude(n.Lat()); - reP.setLongitude(n.Long()); - reP.setElevation(n.Alt()); - - uRewriteCount++; - idx++; - } - } - - idx = duplicateEndIdx; - } - - return uRewriteCount; -} - bool TTSReader::deriveMinMaxSlopes(double &minSlope, double &maxSlope, double &variance) const { minSlope = 0; @@ -511,10 +417,16 @@ bool TTSReader::deriveMinMaxSlopes(double &minSlope, double &maxSlope, double &v void TTSReader::recomputeAltitudeFromGradient() { - // Wish there was a fix... TTS Files with no altitude will start at elevation 0... - double alt = points[1].getElevation(); + // Some tts files I see have an altitude crisis around the start: Fixup altitude + // of index 0 and 1 by linear interpolating. + double d2d1delta = points[2].getDistanceFromStart() - points[1].getDistanceFromStart(); + points[1].setElevation(points[2].getElevation() - (0.01 * points[1].getGradient()*d2d1delta)); + points[0].setElevation(points[1].getElevation() - (0.01 * points[0].getGradient()*d2d1delta)); - for (unsigned u = 2; u < points.size(); u++) { + // Wish there was a fix... TTS Files with no altitude will start at elevation 0... + double alt = points[0].getElevation(); + + for (unsigned u = 1; u < points.size(); u++) { const Point &curr = points[u]; const Point &prev = points[u - 1]; @@ -531,9 +443,6 @@ void TTSReader::recomputeAltitudeFromGradient() { points[u].setElevation(alt); } - // Rewrite teh first point which is actually last point. - points[0].setElevation(alt); - // Even if tts file started without altitude, now that it is generated // we will claim to have it. fHasAlt = true; @@ -599,25 +508,7 @@ bool TTSReader::parseFile(QDataStream &file) { // Clean up passes. - // I can't tell if this is the design but it seems to work - // well. Move all trip distance values forward by one with points[1] - // being given 0. The symptom I'm fixing is that points[0]'s distance - // is the final distance, and point[1]'s distance looks like it - // should be the second point's distance. - double nextDist = 0; - for (int i = 1; i < points.size(); i++) { - double t = points[i].getDistanceFromStart(); - points[i].setDistanceFromStart(nextDist); - nextDist = t; - } - - // First some tts files have lat/lon that were - // stored as float32. These have ranges of duplicate location - // that should be removed. This pass also fixes up common errors - // with the first sample on the ride. - interpolateDuplicateLocations(); - - // Now recompute all altitude data using the recorded gradient. + // Recompute all altitude data using the recorded gradient. // This is necessary because generally the altitude data in tts // files is far far too noisy to use to compute slope. For example // see IT_LOMBARDY08.TTS. @@ -816,67 +707,186 @@ bool TTSReader::loadHeaders() { bytes += (int)data.size(); } - // merge program points + // At this... point... + // - pointList holds framemapping info (if any) + // - programList holds slope info + // - gpsList holds gps info + // + // GPS Info is optional. + // FrameMapping Info is optional. + // + // If this is a slope program then slope info is NOT optional. + // + // So we need to decide what is our base list. + // + // WattZap implementation based everything on framemapping info, + // which doesn't work when framemapping isn't in the tts. + // + // One stream is chosen to be the basis for the interpolation of + // the other streams. Choose whichever has the most data points, + // then interpolate the other two onto it. + size_t gpsCount = gpsPoints.size(); + size_t slopeCount = programList.size(); + size_t frameMapCount = pointList.size(); + + enum Basis { NoBasis, FrameMap, GPS, Slope }; + + Basis basis = NoBasis; + + // Copy basis stream onto points[]. + if (frameMapCount > gpsCount && frameMapCount > slopeCount) + { + // pointList basis. This holds frame mapping. + // Is already correct type so just copy. + points = pointList; + basis = FrameMap; + } else if (gpsCount >= slopeCount) { + // GpsList basis + for (int i = 0; i < gpsCount; i++) { + Point p; + + p.setDistanceFromStart(gpsPoints[i].distance); + p.setLatitude(gpsPoints[i].lat); + p.setLongitude(gpsPoints[i].lon); + p.setElevation(gpsPoints[i].alt); + + points.push_back(p); + } + basis = GPS; + } else { + // slope basis + for (int i = 0; i < slopeCount; i++) { + Point p; + + p.setDistanceFromStart(programList[i].distance); + p.setGradient(programList[i].slope); + + points.push_back(p); + } + basis = Slope; + } + + if (basis == NoBasis) { + return false; + } + + GeoPointInterpolator gpi; // geo interpolation + + // Interpolate non-geo data in xyz. + DistancePointInterpolator ti; + DistancePointInterpolator si; + + // Interpolate non-basis streams onto points[]. + int frameMapIdx = 0; + int gpsIdx = 0; + int slopeIdx = 0; + + for (int i = 0; i < points.size(); i++) { + + fHasKM = true; + + Point &p = points[i]; + + // Interpolate framemap + if (basis != FrameMap && frameMapCount) { + + while (ti.WantsInput(p.getDistanceFromStart())) { + if (frameMapIdx >= frameMapCount) { + ti.NotifyInputComplete(); + break; + } + ti.Push(pointList[frameMapIdx].getDistanceFromStart(), + xyz(pointList[frameMapIdx].getTime(), 0, 0)); + frameMapIdx++; + } + + xyz interp = ti.Location(p.getDistanceFromStart()); + double time = interp.x(); + + p.setTime(time); + + fHasFrameMapping = true; + } + + // Interpolate gps location + if (basis != GPS && gpsCount) { + while (gpi.WantsInput(p.getDistanceFromStart())) { + if (gpsIdx >= gpsCount) { + gpi.NotifyInputComplete(); + break; + } + gpi.Push(gpsPoints[gpsIdx].distance, + geolocation(gpsPoints[gpsIdx].lat, + gpsPoints[gpsIdx].lon, + gpsPoints[gpsIdx].alt)); + gpsIdx++; + } + + geolocation geoloc = gpi.Location(p.getDistanceFromStart()); + + p.setLatitude (geoloc.Lat()); + p.setLongitude(geoloc.Long()); + p.setElevation(geoloc.Alt()); + + fHasGPS = true; + fHasAlt = true; + } + + // Interpolate gradient + if (basis != Slope && slopeCount) { + + while (si.WantsInput(p.getDistanceFromStart())) { + if (slopeIdx >= frameMapCount) { + si.NotifyInputComplete(); + break; + } + si.Push(programList[slopeIdx].distance, + xyz(programList[slopeIdx].slope, 0, 0)); + slopeIdx++; + } + + xyz interp = si.Location(p.getDistanceFromStart()); + double slope = interp.x(); + + p.setGradient(slope); + + fHasSlope = true; + } + } + + // Convet units for distance and time. Compute speed. + Point *pPrevPoint = &(points[0]); + unsigned int pointCount = 0; - long lastDistance = 0; - double lastSlope = 0.0; + while (pointCount < points.size()) { + Point &p = points[pointCount]; - Point &lastPoint = pointList[0]; - int programCount = (int)programList.size(); + // turn into meters + p.setDistanceFromStart(p.getDistanceFromStart() / 100); - for (int i = 0; i < programCount; i++) { - ProgramPoint pp = programList[i]; - while (pointCount < pointList.size()) { - Point &p = pointList[pointCount]; - if (pp.distance > p.getDistanceFromStart() || i == programCount - 1) { + // convert to mS + p.setTime(p.getTime() * 1000); - this->fHasSlope = true; - this->fHasKM = true; + // speed = d/t + if (pointCount > 0) { + p.setSpeed((3600 * (p.getDistanceFromStart() - pPrevPoint->getDistanceFromStart())) / ((p.getTime() - pPrevPoint->getTime()))); + } - // see which point is closest - if ((p.getDistanceFromStart() - lastDistance) < (p.getDistanceFromStart() - pp.distance)) { - p.setGradient(pp.slope); - } - else { - p.setGradient(lastSlope); - } + pPrevPoint = &(points[pointCount]); - // turn into meters - p.setDistanceFromStart(p.getDistanceFromStart() / 100); + // meters / meters + p.print(); - // convert to mS - p.setTime(p.getTime() * 1000); + series.push_back({ p.getDistanceFromStart() / 1000, p.getElevation() }); - // speed = d/t - if (pointCount > 0) { - p.setSpeed((3600 * (p.getDistanceFromStart() - lastPoint.getDistanceFromStart())) / ((p.getTime() - lastPoint.getTime()))); - } - - lastPoint = p; - - // meters / meters - p.print(); - - series.push_back({ p.getDistanceFromStart() / 1000, p.getElevation() }); - } - else { - break; - } - - lastDistance = pp.distance; - lastSlope = pp.slope; - - pointCount++; - }// while - }// for + pointCount++; + }// while // fill in speed for first point - pointList[0].setSpeed(pointList[1].getSpeed()); + points[0].setSpeed(points[1].getSpeed()); - totalDistance = pointList[pointList.size() - 1].getDistanceFromStart(); - - points = pointList; + totalDistance = points[points.size() - 1].getDistanceFromStart(); return true; } @@ -986,14 +996,7 @@ void TTSReader::GPSData(int version, ByteArray &data) { DEBUG_LOG << "[" << (data.size() / 16) << " gps points]\n"; - unsigned int pointCount = 0; - int lastDistance = 0; - double lastLat = 0; - double lastLon = 0; - double lastAlt = 0; - int gpsCount = (int)data.size() / 16; - for (int i = 0; i < gpsCount; i++) { int distance = getUInt(data, i * 16); @@ -1002,60 +1005,41 @@ void TTSReader::GPSData(int version, ByteArray &data) { double lon = AsFloat(getUInt(data, i * 16 + 8)); double altitude = AsFloat(getUInt(data, i * 16 + 12)); - while (pointCount < pointList.size()) { + if (lat != 0. || lon != 0.) { + fHasGPS = true; + } - Point &p = pointList[pointCount]; + if (altitude != 0.) { + fHasAlt = true; + } - /* - * we're straddling two points or we've run out of gps points to - * use - */ - if (distance > p.getDistanceFromStart() || i == gpsCount - 1) { + bool fIsDuplicate = false; - fHasLat = true; - fHasLon = true; - fHasAlt = true; + // Some tts files contain duplicate points. Remove these + // since they only screw up interpolation during stream + // merging. + if (gpsPoints.size()) { + GPSPoint &prevGPS = gpsPoints[gpsPoints.size() - 1]; - // see which point is closest - if ((p.getDistanceFromStart() - lastDistance) < (p.getDistanceFromStart() - distance)) { + // Compare geo distance independent of altitude, + // altitude noise is a separate problem. + geolocation curGPS(lat, lon, 0); + geolocation newGPS(prevGPS.lat, prevGPS.lon, 0); - DEBUG_LOG_VERBOSE << "[" << pointCount << "]" - << p.getDistanceFromStart() << "/" - << distance << " : " << p.getTime() - << " secs " << lat << "/" << lon << ", " - << altitude << " meters\n"; + double distance = curGPS.DistanceFrom(newGPS); - p.setElevation(altitude); - p.setLatitude(lat); - p.setLongitude(lon); - } - else { + int distanceExp; + std::frexp(distance, &distanceExp); - DEBUG_LOG_VERBOSE << "[" << pointCount << "]" - << (long) p.getDistanceFromStart() << "/" - << lastDistance << " : " << p.getTime() - << " secs " << lastLat << "/" << lastLon - << ", " << lastAlt << " meters\n"; - - p.setElevation(lastAlt); - p.setLatitude(lastLat); - p.setLongitude(lastLon); - } - - pointList[pointCount] = p; - } - else { - break; + // If outside of 2^-12 then they are not the same location. + if (distanceExp < -12) { + fIsDuplicate = true; } + } - pointCount++; - - }// while - - lastDistance = distance; - lastLat = lat; - lastLon = lon; - lastAlt = altitude; + if (!fIsDuplicate) { + gpsPoints.push_back(GPSPoint(distance, lat, lon, altitude)); + } } return; @@ -1111,6 +1095,10 @@ void TTSReader::distanceToFrame(int version, ByteArray &data) { p.setTime((int)(frame / frameRate)); pointList.push_back(p); } + + // This reader was premised on all tts files having frame mapping, but + // some don't... + fHasFrameMapping = pointList.size() != 0; } /** @@ -1134,10 +1122,9 @@ void TTSReader::programData(int version, ByteArray &data) { DEBUG_LOG << "[" << data.size() / 6 << " program points]"; - long distance = 0; + double distance = 0; int pointCount = (int)data.size() / 6; - - programList.resize(pointCount); + double bias = 0; for (int i = 0; i < pointCount; i++) { int slope = getUShort(data, i * 6); @@ -1167,7 +1154,15 @@ void TTSReader::programData(int version, ByteArray &data) { maxSlope = p.slope; } - p.distance = distance; + // If first point isn't zero distance then bias + // it and all subseequent points by this distance. + // This might be rubbish but appears to help every + // tts I've tried. + if (i == 0 && distance != 0.) { + bias = -distance; + } + + p.distance = distance + bias; programList.push_back(p); } diff --git a/src/FileIO/TTSReader.h b/src/FileIO/TTSReader.h index e2338c066..11b15ab2b 100644 --- a/src/FileIO/TTSReader.h +++ b/src/FileIO/TTSReader.h @@ -74,9 +74,9 @@ namespace NS_TTSReader { double elevation; double distanceFromStart; double gradient; - int power; + double power; double speed; - long time; + double time; public: @@ -90,11 +90,11 @@ namespace NS_TTSReader { this->speed = _speed; } - long getTime() const { + double getTime() const { return time; } - void setTime(long _time) { + void setTime(double _time) { this->time = _time; } @@ -102,7 +102,7 @@ namespace NS_TTSReader { return gradient; } - int getPower() const { + double getPower() const { return power; // overload gradient with power } @@ -110,7 +110,7 @@ namespace NS_TTSReader { this->gradient = _gradient; } - void setPower(int _power) { + void setPower(double _power) { this->power = _power; } @@ -158,34 +158,46 @@ namespace NS_TTSReader { */ struct ProgramPoint { - long distance; + double distance; double slope; }; + struct GPSPoint{ + double distance; + double lat; + double lon; + double alt; + + GPSPoint(double _distance, double _lat, double _lon, double _alt) : + distance(_distance), lat(_lat), lon(_lon), alt(_alt) {} + }; + class TTSReader { - double frameRate; - std::vector pointList; + std::vector pointList; // frame mapping data + std::vector programList; // slope data + std::vector gpsPoints; // gps data - std::vector programList; - std::vector content; + std::vector content; ByteArray pre; int imageId; + double frameRate; + double maxSlope; double minSlope; XYSeries series; - std::vector points; + std::vector points; // Final list of combined data double totalDistance; bool fHasKM; - bool fHasLat; - bool fHasLon; + bool fHasGPS; bool fHasAlt; bool fHasSlope; + bool fHasFrameMapping; // Block Types static const int PROGRAM_DATA = 1032; @@ -199,7 +211,9 @@ namespace NS_TTSReader { public: - TTSReader() : maxSlope(0), minSlope(0), totalDistance(0), fHasKM(false), fHasLat(false), fHasLon(false), fHasAlt(false), fHasSlope(false) {} + TTSReader() : maxSlope(0), minSlope(0), totalDistance(0), fHasKM(false), + fHasGPS(false), fHasAlt(false), fHasSlope(false), + fHasFrameMapping(false) {} enum StringType { NONPRINTABLE, BLOCK, STRING, IMAGE, CRC @@ -216,9 +230,6 @@ namespace NS_TTSReader { bool deriveMinMaxSlopes(double &minSlope, double &maxSlope, double &variance) const; - unsigned findNextNonDuplicateGeolocation(unsigned u) const; - unsigned interpolateLocationsWithinSpan(unsigned u0, unsigned u1); - unsigned interpolateDuplicateLocations(); void recomputeAltitudeFromGradient(); #if 0 @@ -239,12 +250,14 @@ namespace NS_TTSReader { } #endif - bool hasKm() { return fHasKM; } - bool hasGradient() { return fHasSlope; } - bool hasLatitude() { return fHasLat; } - bool hasLongitude() { return fHasLon; } - bool hasElevation() { return fHasAlt; } + double getFrameRate() const { return frameRate; } + double getTotalDistance() const { return totalDistance; } + bool hasKm() const { return fHasKM; } + bool hasGradient() const { return fHasSlope; } + bool hasGPS() const { return fHasGPS; } + bool hasElevation() const { return fHasAlt; } + bool hasFrameMapping() const { return fHasFrameMapping; } bool loadHeaders(); void blockProcessing(int blockType, int version, ByteArray &data); diff --git a/src/Train/ErgFile.cpp b/src/Train/ErgFile.cpp index b8d69fb38..6b6e190f3 100644 --- a/src/Train/ErgFile.cpp +++ b/src/Train/ErgFile.cpp @@ -135,6 +135,8 @@ void ErgFile::parseZwift() Points.clear(); Laps.clear(); + gpi.Reset(); + // parse the file QFile zwo(filename); QXmlInputSource source(&zwo); @@ -194,6 +196,8 @@ void ErgFile::parseErg2(QString p) Points.clear(); Laps.clear(); + gpi.Reset(); + // open the file if (p == "" && ergFile.open(QIODevice::ReadOnly | QIODevice::Text) == false) { valid = false; @@ -259,6 +263,8 @@ void ErgFile::parseTacx() Points.clear(); Laps.clear(); + gpi.Reset(); + // running totals double rdist = 0; // running total for distance double ralt = 200; // always start at 200 meters just to prettify the graph @@ -705,6 +711,8 @@ void ErgFile::parseGpx() Points.clear(); Laps.clear(); + gpi.Reset(); + // TTS File Gradient Should be smoothly interpolated from Altitude. StrictGradient = false; @@ -832,6 +840,8 @@ void ErgFile::parseTTS() Points.clear(); Laps.clear(); + gpi.Reset(); + // TTS File Gradient Should be smoothly interpolated from Altitude. StrictGradient = false; @@ -861,8 +871,7 @@ void ErgFile::parseTTS() // Enumerate the data types that are available. bool fHasKm = ttsReader.hasKm(); - bool fHasLat = ttsReader.hasLatitude(); - bool fHasLon = ttsReader.hasLongitude(); + bool fHasGPS = ttsReader.hasGPS(); bool fHasAlt = ttsReader.hasElevation(); bool fHasSlope = ttsReader.hasGradient(); @@ -877,20 +886,19 @@ void ErgFile::parseTTS() prevPoint = NULL; point = NULL; - nextPoint = &ttsPoints[1]; + nextPoint = &ttsPoints[0]; double alt = 0; if (fHasAlt) { - // Start with altitude of second point. - alt = ttsPoints[1].getElevation(); + alt = ttsPoints[0].getElevation(); } - for (int i = 1; i <= pointCount; i++) { // first data point is actually the last... + for (int i = 0; i <= pointCount; i++) { // first data point is actually the last... ErgFilePoint add; prevPoint = point; point = nextPoint; - nextPoint = ((i + 1) >= pointCount) ? &ttsPoints[0] : &ttsPoints[i + 1]; + nextPoint = ((i + 1) >= pointCount) ? &ttsPoints[i] : &ttsPoints[i + 1]; // Determine slope to next point double slope = 0.0; @@ -922,8 +930,11 @@ void ErgFile::parseTTS() add.y = alt; add.val = slope; - if (fHasLat) add.lat = point->getLatitude(); - if (fHasLon) add.lon = point->getLongitude(); + if (fHasGPS) { + add.lat = point->getLatitude(); + add.lon = point->getLongitude(); + } + if (add.y > MaxWatts) MaxWatts = add.y; Points.append(add); diff --git a/src/Train/VideoSyncFile.cpp b/src/Train/VideoSyncFile.cpp index 085178fbc..89030f9b7 100644 --- a/src/Train/VideoSyncFile.cpp +++ b/src/Train/VideoSyncFile.cpp @@ -22,11 +22,14 @@ #include #include "Units.h" +#include "TTSReader.h" + // Supported file types static QStringList supported; static bool setSupported() { ::supported << ".rlv"; + ::supported << ".tts"; //TODO ::supported << ".gpx"; return true; } @@ -54,9 +57,83 @@ void VideoSyncFile::reload() { // which parser to call? if (filename.endsWith(".rlv", Qt::CaseInsensitive)) parseRLV(); + if (filename.endsWith(".tts", Qt::CaseInsensitive)) parseTTS(); //TODO else if (filename.endsWith(".gpx", Qt::CaseInsensitive)) parseGPX(); } +void VideoSyncFile::parseTTS() +{ + // Initialise + manualOffset = 0; + Version = ""; + Units = ""; + Filename = ""; + Name = ""; + Duration = -1; + valid = false; // did it parse ok as sync file? + format = RLV; + Points.clear(); + + QFile ttsFile(filename); + if (ttsFile.open(QIODevice::ReadOnly)) { + + QStringList errors_; + + QDataStream qttsStream(&ttsFile); + qttsStream.setByteOrder(QDataStream::LittleEndian); + + NS_TTSReader::TTSReader ttsReader; + bool success = ttsReader.parseFile(qttsStream); + if (success) { + + // ----------------------------------------------------------------- + // VideoFrameRate + VideoFrameRate = ttsReader.getFrameRate(); + + // ----------------------------------------------------------------- + // VideoSyncFilePoint + const std::vector &ttsPoints = ttsReader.getPoints(); + if (ttsReader.hasFrameMapping()) { + + NS_TTSReader::Point fakeFirstPoint; + + fakeFirstPoint = ttsPoints[1]; + fakeFirstPoint.setDistanceFromStart(0); + fakeFirstPoint.setTime(0); + + size_t pointCount = ttsPoints.size(); + for (size_t i = 0; i < pointCount; i++) { + + const NS_TTSReader::Point &point = ttsPoints[i]; + + VideoSyncFilePoint add; + + // distance + add.km = point.getDistanceFromStart() / 1000.0; + + // time + add.secs = point.getTime() / 1000.; + + // speed + add.kph = point.getSpeed(); + + Points.append(add); + } + + Duration = Points.last().secs * 1000.0; + Distance = Points.last().km; + + valid = true; + + } // hasFrameMapping + + } // parseFile + + ttsFile.close(); + + } // ttsFile.open +} + void VideoSyncFile::parseRLV() { // Initialise diff --git a/src/Train/VideoSyncFile.h b/src/Train/VideoSyncFile.h index f68ddcd15..39473f180 100644 --- a/src/Train/VideoSyncFile.h +++ b/src/Train/VideoSyncFile.h @@ -75,7 +75,8 @@ class VideoSyncFile static bool isVideoSync(QString); // is this a supported videosync? void reload(); // reload after messed about - void parseRLV(); // its a rlv file + void parseRLV(); // its a rlv file + void parseTTS(); // its a tts file bool isValid(); // is the file valid or not? double VideoFrameRate;