1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2025-06-27 11:39:16 +02:00

Added cadence and power graphs

This commit is contained in:
2016-11-06 03:28:08 +01:00
parent aac9bf024b
commit 8a90a736d2
15 changed files with 469 additions and 184 deletions

84
src/cadencegraph.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "data.h"
#include "cadencegraph.h"
CadenceGraph::CadenceGraph(QWidget *parent) : GraphTab(parent)
{
_units = Metric;
_showTracks = true;
GraphView::setYUnits(tr("1/min"));
setYLabel(tr("Cadence"));
setSliderPrecision(1);
}
void CadenceGraph::setInfo()
{
if (_showTracks) {
GraphView::addInfo(tr("Average"), QString::number(avg() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), QString::number(max() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
} else
clearInfo();
}
void CadenceGraph::loadData(const Data &data, const QList<PathItem *> &paths)
{
for (int i = 0; i < data.tracks().count(); i++) {
const Graph &graph = data.tracks().at(i)->cadence();
qreal sum = 0, w = 0;
if (graph.size() < 2) {
skipColor();
continue;
}
for (int j = 1; j < graph.size(); j++) {
qreal ds = graph.at(j).s() - graph.at(j-1).s();
sum += graph.at(j).y() * ds;
w += ds;
}
_avg.append(QPointF(data.tracks().at(i)->distance(), sum/w));
GraphView::loadGraph(graph, paths.at(i));
}
for (int i = 0; i < data.routes().count(); i++)
skipColor();
setInfo();
redraw();
}
qreal CadenceGraph::avg() const
{
qreal sum = 0, w = 0;
QList<QPointF>::const_iterator it;
for (it = _avg.begin(); it != _avg.end(); it++) {
sum += it->y() * it->x();
w += it->x();
}
return (sum / w);
}
void CadenceGraph::clear()
{
_avg.clear();
GraphView::clear();
}
void CadenceGraph::showTracks(bool show)
{
_showTracks = show;
showGraph(show);
setInfo();
redraw();
}

31
src/cadencegraph.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef CADENCEGRAPH_H
#define CADENCEGRAPH_H
#include "graphtab.h"
class CadenceGraph : public GraphTab
{
Q_OBJECT
public:
CadenceGraph(QWidget *parent = 0);
QString label() const {return tr("Cadence");}
void loadData(const Data &data, const QList<PathItem *> &paths);
void clear();
void setUnits(enum Units) {}
void showTracks(bool show);
void showRoutes(bool show) {Q_UNUSED(show);}
private:
qreal avg() const;
qreal max() const {return bounds().bottom();}
void setInfo();
QList<QPointF> _avg;
enum Units _units;
bool _showTracks;
};
#endif // CADENCEGRAPH_H

View File

@ -230,10 +230,18 @@ bool FITParser::parseData(MessageDefinition *def, quint8 offset)
if (val != 0xff)
trackpoint.setHeartRate(val);
break;
case 4:
if (val != 0xff)
trackpoint.setCadence(val);
break;
case 6:
if (val != 0xffff)
trackpoint.setSpeed(val / 1000.0f);
break;
case 7:
if (val != 0xffff)
trackpoint.setPower(val);
break;
case 13:
if (val != 0x7f)
trackpoint.setTemperature((qint8)val);

View File

@ -51,56 +51,13 @@ Coordinates GPXParser::coordinates()
return Coordinates(lon, lat);
}
void GPXParser::handleTrackpointData(DataType type, Trackpoint &trackpoint)
{
switch (type) {
case Elevation:
trackpoint.setElevation(number());
break;
case Time:
trackpoint.setTimestamp(time());
break;
case Speed:
trackpoint.setSpeed(number());
break;
case HeartRate:
trackpoint.setHeartRate(number());
break;
case Temperature:
trackpoint.setTemperature(number());
break;
default:
break;
}
}
void GPXParser::handleWaypointData(DataType type, Waypoint &waypoint)
{
switch (type) {
case Name:
waypoint.setName(_reader.readElementText());
break;
case Description:
waypoint.setDescription(_reader.readElementText());
break;
case Time:
waypoint.setTimestamp(time());
break;
case Elevation:
waypoint.setElevation(number());
break;
default:
break;
}
}
void GPXParser::tpExtension(Trackpoint &trackpoint)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "hr")
handleTrackpointData(HeartRate, trackpoint);
trackpoint.setHeartRate(number());
else if (_reader.name() == "atemp")
handleTrackpointData(Temperature, trackpoint);
trackpoint.setTemperature(number());
else
_reader.skipCurrentElement();
}
@ -110,11 +67,15 @@ void GPXParser::extensions(Trackpoint &trackpoint)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "speed")
handleTrackpointData(Speed, trackpoint);
trackpoint.setSpeed(number());
else if (_reader.name() == "hr" || _reader.name() == "heartrate")
handleTrackpointData(HeartRate, trackpoint);
trackpoint.setHeartRate(number());
else if (_reader.name() == "temp")
handleTrackpointData(Temperature, trackpoint);
trackpoint.setTemperature(number());
else if (_reader.name() == "cadence")
trackpoint.setCadence(number());
else if (_reader.name() == "power")
trackpoint.setPower(number());
else if (_reader.name() == "TrackPointExtension")
tpExtension(trackpoint);
else
@ -128,9 +89,9 @@ void GPXParser::trackpointData(Trackpoint &trackpoint)
while (_reader.readNextStartElement()) {
if (_reader.name() == "ele")
handleTrackpointData(Elevation, trackpoint);
trackpoint.setElevation(number());
else if (_reader.name() == "time")
handleTrackpointData(Time, trackpoint);
trackpoint.setTimestamp(time());
else if (_reader.name() == "geoidheight")
gh = number();
else if (_reader.name() == "extensions")
@ -149,15 +110,15 @@ void GPXParser::waypointData(Waypoint &waypoint)
while (_reader.readNextStartElement()) {
if (_reader.name() == "name")
handleWaypointData(Name, waypoint);
waypoint.setName(_reader.readElementText());
else if (_reader.name() == "desc")
handleWaypointData(Description, waypoint);
waypoint.setDescription(_reader.readElementText());
else if (_reader.name() == "ele")
handleWaypointData(Elevation, waypoint);
waypoint.setElevation(number());
else if (_reader.name() == "geoidheight")
gh = number();
else if (_reader.name() == "time")
handleWaypointData(Time, waypoint);
waypoint.setTimestamp(time());
else
_reader.skipCurrentElement();
}

View File

@ -17,10 +17,6 @@ public:
int errorLine() const {return _reader.lineNumber();}
private:
enum DataType {
Name, Description, Elevation, Time, Speed, HeartRate, Temperature
};
bool parse();
void gpx();
void track(TrackData &track);
@ -34,9 +30,6 @@ private:
QDateTime time();
Coordinates coordinates();
void handleWaypointData(DataType type, Waypoint &waypoint);
void handleTrackpointData(DataType type, Trackpoint &trackpoint);
QXmlStreamReader _reader;
};

View File

@ -33,6 +33,8 @@
#include "speedgraph.h"
#include "heartrategraph.h"
#include "temperaturegraph.h"
#include "cadencegraph.h"
#include "powergraph.h"
#include "pathview.h"
#include "trackinfo.h"
#include "filebrowser.h"
@ -512,6 +514,8 @@ void GUI::createGraphTabs()
_tabs.append(new ElevationGraph);
_tabs.append(new SpeedGraph);
_tabs.append(new HeartRateGraph);
_tabs.append(new CadenceGraph);
_tabs.append(new PowerGraph);
_tabs.append(new TemperatureGraph);
for (int i = 0; i < _tabs.count(); i++)

84
src/powergraph.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "data.h"
#include "powergraph.h"
PowerGraph::PowerGraph(QWidget *parent) : GraphTab(parent)
{
_units = Metric;
_showTracks = true;
GraphView::setYUnits(tr("W"));
setYLabel(tr("Power"));
setSliderPrecision(1);
}
void PowerGraph::setInfo()
{
if (_showTracks) {
GraphView::addInfo(tr("Average"), QString::number(avg() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
GraphView::addInfo(tr("Maximum"), QString::number(max() * yScale()
+ yOffset(), 'f', 1) + UNIT_SPACE + yUnits());
} else
clearInfo();
}
void PowerGraph::loadData(const Data &data, const QList<PathItem *> &paths)
{
for (int i = 0; i < data.tracks().count(); i++) {
const Graph &graph = data.tracks().at(i)->power();
qreal sum = 0, w = 0;
if (graph.size() < 2) {
skipColor();
continue;
}
for (int j = 1; j < graph.size(); j++) {
qreal ds = graph.at(j).s() - graph.at(j-1).s();
sum += graph.at(j).y() * ds;
w += ds;
}
_avg.append(QPointF(data.tracks().at(i)->distance(), sum/w));
GraphView::loadGraph(graph, paths.at(i));
}
for (int i = 0; i < data.routes().count(); i++)
skipColor();
setInfo();
redraw();
}
qreal PowerGraph::avg() const
{
qreal sum = 0, w = 0;
QList<QPointF>::const_iterator it;
for (it = _avg.begin(); it != _avg.end(); it++) {
sum += it->y() * it->x();
w += it->x();
}
return (sum / w);
}
void PowerGraph::clear()
{
_avg.clear();
GraphView::clear();
}
void PowerGraph::showTracks(bool show)
{
_showTracks = show;
showGraph(show);
setInfo();
redraw();
}

31
src/powergraph.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef POWERGRAPH_H
#define POWERGRAPH_H
#include "graphtab.h"
class PowerGraph : public GraphTab
{
Q_OBJECT
public:
PowerGraph(QWidget *parent = 0);
QString label() const {return tr("Power");}
void loadData(const Data &data, const QList<PathItem *> &paths);
void clear();
void setUnits(enum Units) {}
void showTracks(bool show);
void showRoutes(bool show) {Q_UNUSED(show);}
private:
qreal avg() const;
qreal max() const {return bounds().bottom();}
void setInfo();
QList<QPointF> _avg;
enum Units _units;
bool _showTracks;
};
#endif // POWERGRAPH_H

View File

@ -56,6 +56,18 @@ Coordinates TCXParser::position()
return pos;
}
void TCXParser::extensions(Trackpoint &trackpoint)
{
while (_reader.readNextStartElement()) {
if (_reader.name() == "RunCadence")
trackpoint.setCadence(number());
else if (_reader.name() == "Watts")
trackpoint.setPower(number());
else
_reader.skipCurrentElement();
}
}
void TCXParser::trackpointData(Trackpoint &trackpoint)
{
while (_reader.readNextStartElement()) {
@ -67,6 +79,10 @@ void TCXParser::trackpointData(Trackpoint &trackpoint)
trackpoint.setTimestamp(time());
else if (_reader.name() == "HeartRateBpm")
trackpoint.setHeartRate(number());
else if (_reader.name() == "Cadence")
trackpoint.setCadence(number());
else if (_reader.name() == "Extensions")
extensions(trackpoint);
else
_reader.skipCurrentElement();
}

View File

@ -27,6 +27,7 @@ private:
void trackpoints(TrackData &track);
void trackpointData(Trackpoint &trackpoint);
void waypointData(Waypoint &waypoint);
void extensions(Trackpoint &trackpoint);
Coordinates position();
qreal number();
QDateTime time();

View File

@ -6,6 +6,8 @@
#define WINDOW_EF 3
#define WINDOW_SF 7
#define WINDOW_HF 3
#define WINDOW_CF 3
#define WINDOW_PF 3
static qreal median(QVector<qreal> v)
@ -114,14 +116,10 @@ Graph Track::elevation() const
if (!_data.size())
return raw;
for (int i = 0; i < _data.size(); i++) {
if (_outliers.contains(i))
continue;
if (_data.at(i).hasElevation())
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasElevation() && !_outliers.contains(i))
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).elevation()));
}
return filter(raw, WINDOW_EF);
}
@ -135,12 +133,9 @@ Graph Track::speed() const
return raw;
for (int i = 0; i < _data.size(); i++) {
if (_outliers.contains(i))
continue;
if (_data.at(i).hasSpeed())
if (_data.at(i).hasSpeed() && !_outliers.contains(i))
v = _data.at(i).speed();
else if (!std::isnan(_speed.at(i)))
else if (!std::isnan(_speed.at(i)) && !_outliers.contains(i))
v = _speed.at(i);
else
continue;
@ -158,14 +153,10 @@ Graph Track::heartRate() const
if (!_data.size())
return raw;
for (int i = 0; i < _data.count(); i++) {
if (_outliers.contains(i))
continue;
if (_data.at(i).hasHeartRate())
for (int i = 0; i < _data.count(); i++)
if (_data.at(i).hasHeartRate() && !_outliers.contains(i))
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).heartRate()));
}
return filter(raw, WINDOW_HF);
}
@ -174,16 +165,36 @@ Graph Track::temperature() const
{
Graph raw;
for (int i = 0; i < _data.size(); i++) {
if (_outliers.contains(i))
continue;
if (_data.at(i).hasTemperature())
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasTemperature() && !_outliers.contains(i))
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).temperature()));
}
return Graph(raw);
return raw;
}
Graph Track::cadence() const
{
Graph raw;
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasCadence() && !_outliers.contains(i))
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).cadence()));
return filter(raw, WINDOW_CF);
}
Graph Track::power() const
{
Graph raw;
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasPower() && !_outliers.contains(i))
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).power()));
return filter(raw, WINDOW_PF);
}
qreal Track::distance() const
@ -206,12 +217,9 @@ Path Track::track() const
{
Path ret;
for (int i = 0; i < _data.size(); i++) {
if (_outliers.contains(i))
continue;
ret.append(_data.at(i).coordinates());
}
for (int i = 0; i < _data.size(); i++)
if (!_outliers.contains(i))
ret.append(_data.at(i).coordinates());
return ret;
}

View File

@ -19,6 +19,8 @@ public:
Graph speed() const;
Graph heartRate() const;
Graph temperature() const;
Graph cadence() const;
Graph power() const;
qreal distance() const;
qreal time() const;

View File

@ -10,9 +10,11 @@ class Trackpoint
{
public:
Trackpoint()
{_elevation = NAN; _speed = NAN; _heartRate = NAN; _temperature = NAN;}
{_elevation = NAN; _speed = NAN; _heartRate = NAN; _temperature = NAN;
_cadence = NAN; _power = NAN;}
Trackpoint(const Coordinates &coordinates) : _coordinates(coordinates)
{_elevation = NAN; _speed = NAN; _heartRate = NAN; _temperature = NAN;}
{_elevation = NAN; _speed = NAN; _heartRate = NAN; _temperature = NAN;
_cadence = NAN; _power = NAN;}
const Coordinates &coordinates() const {return _coordinates;}
Coordinates &rcoordinates() {return _coordinates;}
@ -21,6 +23,8 @@ public:
qreal speed() const {return _speed;}
qreal heartRate() const {return _heartRate;}
qreal temperature() const {return _temperature;}
qreal cadence() const {return _cadence;}
qreal power() const {return _power;}
void setCoordinates(const Coordinates &coordinates)
{_coordinates = coordinates;}
@ -29,12 +33,16 @@ public:
void setSpeed(qreal speed) {_speed = speed;}
void setHeartRate(qreal heartRate) {_heartRate = heartRate;}
void setTemperature(qreal temperature) {_temperature = temperature;}
void setCadence(qreal cadence) {_cadence = cadence;}
void setPower(qreal power) {_power = power;}
bool hasTimestamp() const {return !_timestamp.isNull();}
bool hasElevation() const {return !std::isnan(_elevation);}
bool hasSpeed() const {return !std::isnan(_speed);}
bool hasHeartRate() const {return !std::isnan(_heartRate);}
bool hasTemperature() const {return !std::isnan(_temperature);}
bool hasCadence() const {return !std::isnan(_cadence);}
bool hasPower() const {return !std::isnan(_power);}
private:
Coordinates _coordinates;
@ -43,6 +51,8 @@ private:
qreal _speed;
qreal _heartRate;
qreal _temperature;
qreal _cadence;
qreal _power;
};
QDebug operator<<(QDebug dbg, const Trackpoint &trackpoint);