mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Allow TTS Files for VideoSync. (#3354)
Rewrite TTSReader Stream Merge Add gpi reset.
This commit is contained in:
committed by
GitHub
parent
cee6f0329a
commit
da3e8212cf
@@ -394,7 +394,6 @@ void GeoPointInterpolator::Push(double distance, double altitude)
|
||||
|
||||
geolocation geo(0, 0, altitude);
|
||||
xyz point = geo.toxyz();
|
||||
point.y() = distance;
|
||||
|
||||
DistancePointInterpolator<SphericalTwoPointInterpolator>::Push(distance, point);
|
||||
}
|
||||
|
||||
@@ -793,6 +793,11 @@ public:
|
||||
|
||||
GeoPointInterpolator() : DistancePointInterpolator<SphericalTwoPointInterpolator>(), m_locationState(Unset) {}
|
||||
|
||||
void Reset() {
|
||||
m_locationState = Unset;
|
||||
DistancePointInterpolator<SphericalTwoPointInterpolator>::Reset();
|
||||
}
|
||||
|
||||
geolocation Location(double distance);
|
||||
geolocation Location(double distance, double &slope);
|
||||
|
||||
|
||||
@@ -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<LinearTwoPointInterpolator> ti;
|
||||
DistancePointInterpolator<LinearTwoPointInterpolator> 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);
|
||||
}
|
||||
|
||||
@@ -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<Point> pointList;
|
||||
std::vector<Point> pointList; // frame mapping data
|
||||
std::vector<ProgramPoint> programList; // slope data
|
||||
std::vector<GPSPoint> gpsPoints; // gps data
|
||||
|
||||
std::vector<ProgramPoint> programList;
|
||||
std::vector<ByteArray> content;
|
||||
std::vector<ByteArray> content;
|
||||
ByteArray pre;
|
||||
|
||||
int imageId;
|
||||
|
||||
double frameRate;
|
||||
|
||||
double maxSlope;
|
||||
double minSlope;
|
||||
XYSeries series;
|
||||
|
||||
std::vector<Point> points;
|
||||
std::vector<Point> 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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -22,11 +22,14 @@
|
||||
#include <stdint.h>
|
||||
#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<NS_TTSReader::Point> &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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user