1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-07 07:13:21 +02:00
GPXSee/src/data/track.cpp

297 lines
6.3 KiB
C++
Raw Normal View History

2015-10-05 01:43:48 +02:00
#include "track.h"
2017-05-22 14:54:22 +02:00
int Track::_elevationWindow = 3;
2017-05-23 16:06:05 +02:00
int Track::_speedWindow = 5;
2017-05-22 14:54:22 +02:00
int Track::_heartRateWindow = 3;
int Track::_cadenceWindow = 3;
int Track::_powerWindow = 3;
2016-11-05 17:33:29 +01:00
2017-05-22 14:54:22 +02:00
qreal Track::_pauseSpeed = 0.5;
int Track::_pauseInterval = 10;
2016-01-14 00:37:51 +01:00
2017-05-22 14:54:22 +02:00
bool Track::_outlierEliminate = true;
2016-09-19 23:35:04 +02:00
static qreal median(QVector<qreal> &v)
2015-10-05 01:43:48 +02:00
{
2016-11-05 17:33:29 +01:00
qSort(v.begin(), v.end());
return v.at(v.size() / 2);
2015-10-05 01:43:48 +02:00
}
static qreal MAD(QVector<qreal> &v, qreal m)
2015-10-05 01:43:48 +02:00
{
for (int i = 0; i < v.size(); i++)
2016-11-05 17:33:29 +01:00
v[i] = qAbs(v.at(i) - m);
qSort(v.begin(), v.end());
return v.at(v.size() / 2);
2015-12-06 00:51:02 +01:00
}
static QSet<int> eliminate(const QVector<qreal> &v)
2015-12-06 00:51:02 +01:00
{
2016-11-05 17:33:29 +01:00
QSet<int> rm;
2015-11-23 02:37:08 +01:00
QVector<qreal> w(v);
qreal m = median(w);
qreal M = MAD(w, m);
2015-10-05 01:43:48 +02:00
for (int i = 0; i < v.size(); i++)
if (qAbs((0.6745 * (v.at(i) - m)) / M) > 5)
2016-11-05 17:33:29 +01:00
rm.insert(i);
2015-11-25 23:17:39 +01:00
2016-11-05 17:33:29 +01:00
return rm;
2015-10-05 01:43:48 +02:00
}
static Graph filter(const Graph &g, int window)
2015-10-05 01:43:48 +02:00
{
if (g.size() < window || window < 2)
2017-05-22 22:37:35 +02:00
return Graph(g);
qreal acc = 0;
Graph ret(g.size());
2015-10-05 01:43:48 +02:00
for (int i = 0; i < window; i++)
acc += g.at(i).y();
for (int i = 0; i <= window/2; i++)
2017-05-22 22:37:35 +02:00
ret[i] = GraphPoint(g.at(i).s(), g.at(i).t(), acc/window);
2015-10-05 01:43:48 +02:00
for (int i = window/2 + 1; i < g.size() - window/2; i++) {
acc += g.at(i + window/2).y() - g.at(i - (window/2 + 1)).y();
2017-05-22 22:37:35 +02:00
ret[i] = GraphPoint(g.at(i).s(), g.at(i).t(), acc/window);
2015-10-05 01:43:48 +02:00
}
for (int i = g.size() - window/2; i < g.size(); i++)
2017-05-22 22:37:35 +02:00
ret[i] = GraphPoint(g.at(i).s(), g.at(i).t(), acc/window);
2015-11-23 02:37:08 +01:00
return ret;
2016-01-14 00:37:51 +01:00
}
Track::Track(const TrackData &data) : _data(data)
{
QVector<qreal> acceleration;
2016-09-19 23:35:04 +02:00
_time.append(0);
2016-11-05 17:33:29 +01:00
_distance.append(0);
_speed.append(0);
acceleration.append(0);
2016-11-05 17:33:29 +01:00
int last = 0;
2018-02-17 13:40:25 +01:00
2018-02-15 23:03:20 +01:00
for (int i = 1; i < _data.count(); i++) {
qreal ds = _data.at(i).coordinates().distanceTo(_data.at(i-1).coordinates());
2016-11-05 17:33:29 +01:00
_distance.append(ds);
2016-09-19 00:56:10 +02:00
2018-02-15 23:03:20 +01:00
if (_data.first().hasTimestamp() && _data.at(i).hasTimestamp()
&& _data.at(i).timestamp() >= _data.at(last).timestamp()) {
2016-09-19 23:35:04 +02:00
_time.append(_data.first().timestamp().msecsTo(
_data.at(i).timestamp()) / 1000.0);
2018-02-17 13:40:25 +01:00
last = i;
} else
2016-09-19 23:35:04 +02:00
_time.append(NAN);
2016-11-05 17:33:29 +01:00
if (std::isnan(_time.at(i)) || std::isnan(_time.at(i-1)))
_speed.append(NAN);
else {
qreal dt = _time.at(i) - _time.at(i-1);
2018-02-17 13:40:25 +01:00
if (dt < 1e-3) {
2016-11-05 17:33:29 +01:00
_speed.append(_speed.at(i-1));
acceleration.append(acceleration.at(i-1));
2016-11-05 17:33:29 +01:00
continue;
}
_speed.append(ds / dt);
qreal dv = _speed.at(i) - _speed.at(i-1);
acceleration.append(dv / dt);
2016-11-05 17:33:29 +01:00
}
}
_pause = 0;
2018-02-15 23:03:20 +01:00
for (int i = 1; i < _data.count(); i++) {
2017-05-22 14:54:22 +02:00
if (_time.at(i) > _time.at(i-1) + _pauseInterval
&& _speed.at(i) < _pauseSpeed) {
_pause += _time.at(i) - _time.at(i-1);
_stop.insert(i-1);
_stop.insert(i);
}
}
2017-05-22 14:54:22 +02:00
if (_outlierEliminate)
_outliers = eliminate(acceleration);
2016-11-05 17:33:29 +01:00
QSet<int>::const_iterator it;
for (it = _stop.constBegin(); it != _stop.constEnd(); ++it)
_outliers.remove(*it);
qreal total = 0;
2016-11-05 17:33:29 +01:00
for (int i = 0; i < _data.size(); i++) {
if (_outliers.contains(i))
continue;
if (!discardStopPoint(i))
total += _distance.at(i);
2016-11-05 17:33:29 +01:00
_distance[i] = total;
2016-09-19 23:35:04 +02:00
}
}
2016-09-19 00:56:10 +02:00
Graph Track::elevation() const
2015-11-23 02:37:08 +01:00
{
2016-11-05 17:33:29 +01:00
Graph raw;
2015-10-05 01:43:48 +02:00
2016-11-06 03:28:08 +01:00
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasElevation() && !_outliers.contains(i))
2016-09-19 23:35:04 +02:00
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).elevation()));
2015-10-05 01:43:48 +02:00
2017-05-22 14:54:22 +02:00
return filter(raw, _elevationWindow);
2015-10-05 01:43:48 +02:00
}
2016-09-19 00:56:10 +02:00
Graph Track::speed() const
2015-10-05 01:43:48 +02:00
{
Graph raw, filtered;
2016-11-05 17:33:29 +01:00
qreal v;
QList<int> stop;
2015-10-05 01:43:48 +02:00
2016-11-05 17:33:29 +01:00
for (int i = 0; i < _data.size(); i++) {
if (_stop.contains(i) && (!std::isnan(_speed.at(i))
|| _data.at(i).hasSpeed())) {
v = 0;
stop.append(raw.size());
} else if (_data.at(i).hasSpeed() && !_outliers.contains(i))
v = _data.at(i).speed();
2016-11-06 03:28:08 +01:00
else if (!std::isnan(_speed.at(i)) && !_outliers.contains(i))
2016-11-05 17:33:29 +01:00
v = _speed.at(i);
else
2016-06-16 20:47:32 +02:00
continue;
2015-10-05 01:43:48 +02:00
2016-09-19 23:35:04 +02:00
raw.append(GraphPoint(_distance.at(i), _time.at(i), v));
}
2015-10-05 01:43:48 +02:00
2017-05-22 14:54:22 +02:00
filtered = filter(raw, _speedWindow);
for (int i = 0; i < stop.size(); i++)
filtered[stop.at(i)].setY(0);
return filtered;
2015-10-05 01:43:48 +02:00
}
2016-09-19 00:56:10 +02:00
Graph Track::heartRate() const
2015-11-23 02:37:08 +01:00
{
2016-11-05 17:33:29 +01:00
Graph raw;
2016-11-06 03:28:08 +01:00
for (int i = 0; i < _data.count(); i++)
if (_data.at(i).hasHeartRate() && !_outliers.contains(i))
2016-09-19 23:35:04 +02:00
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).heartRate()));
2015-11-23 02:37:08 +01:00
2017-05-22 14:54:22 +02:00
return filter(raw, _heartRateWindow);
}
2015-11-23 02:37:08 +01:00
2016-09-19 00:56:10 +02:00
Graph Track::temperature() const
2016-06-16 20:47:32 +02:00
{
2016-11-05 17:33:29 +01:00
Graph raw;
2016-11-06 03:28:08 +01:00
for (int i = 0; i < _data.size(); i++)
if (_data.at(i).hasTemperature() && !_outliers.contains(i))
2016-09-19 23:35:04 +02:00
raw.append(GraphPoint(_distance.at(i), _time.at(i),
_data.at(i).temperature()));
2016-09-19 00:56:10 +02:00
2016-11-06 03:28:08 +01:00
return raw;
}
Graph Track::cadence() const
{
Graph raw, filtered;
QList<int> stop;
qreal c;
2016-11-06 03:28:08 +01:00
for (int i = 0; i < _data.size(); i++) {
if (_data.at(i).hasCadence() && _stop.contains(i)) {
c = 0;
stop.append(raw.size());
} else if (_data.at(i).hasCadence() && !_outliers.contains(i))
c = _data.at(i).cadence();
else
continue;
2016-11-06 03:28:08 +01:00
raw.append(GraphPoint(_distance.at(i), _time.at(i), c));
}
2017-05-22 14:54:22 +02:00
filtered = filter(raw, _cadenceWindow);
for (int i = 0; i < stop.size(); i++)
filtered[stop.at(i)].setY(0);
return filtered;
2016-11-06 03:28:08 +01:00
}
Graph Track::power() const
{
Graph raw, filtered;
QList<int> stop;
qreal p;
2016-11-06 03:28:08 +01:00
for (int i = 0; i < _data.size(); i++) {
if (_data.at(i).hasPower() && _stop.contains(i)) {
p = 0;
stop.append(raw.size());
} else if (_data.at(i).hasPower() && !_outliers.contains(i))
p = _data.at(i).power();
else
continue;
2016-11-06 03:28:08 +01:00
raw.append(GraphPoint(_distance.at(i), _time.at(i), p));
}
2017-05-22 14:54:22 +02:00
filtered = filter(raw, _powerWindow);
for (int i = 0; i < stop.size(); i++)
filtered[stop.at(i)].setY(0);
return filtered;
2016-06-16 20:47:32 +02:00
}
qreal Track::distance() const
2015-12-20 09:33:40 +01:00
{
for (int i = _distance.size() - 1; i >= 0; i--)
if (!_outliers.contains(i))
return _distance.at(i);
return 0;
}
2015-12-20 09:33:40 +01:00
qreal Track::time() const
{
for (int i = _data.size() - 1; i >= 0; i--)
if (!_outliers.contains(i))
return _data.first().timestamp().msecsTo(_data.at(i).timestamp())
/ 1000.0;
return 0;
2015-12-20 09:33:40 +01:00
}
2016-01-14 00:37:51 +01:00
qreal Track::movingTime() const
{
return (time() - _pause);
}
QDateTime Track::date() const
2016-01-14 00:37:51 +01:00
{
return (_data.size()) ? _data.first().timestamp() : QDateTime();
2016-01-14 00:37:51 +01:00
}
2016-11-05 17:33:29 +01:00
2016-11-13 09:49:31 +01:00
Path Track::path() const
2016-11-05 17:33:29 +01:00
{
Path ret;
2016-11-06 03:28:08 +01:00
for (int i = 0; i < _data.size(); i++)
if (!_outliers.contains(i) && !discardStopPoint(i))
2016-11-14 22:12:43 +01:00
ret.append(PathPoint(_data.at(i).coordinates(), _distance.at(i)));
2016-11-05 17:33:29 +01:00
return ret;
}
bool Track::discardStopPoint(int i) const
{
return (_stop.contains(i) && i > 0 && _stop.contains(i-1)
&& i < _data.size() - 1 && _stop.contains(i+1));
}