mirror of
https://github.com/tumic0/GPXSee.git
synced 2025-02-07 12:05:14 +01:00
Outlier detection is now based on acceleration instead of speed
This commit is contained in:
parent
026cc68bf2
commit
c5a060ed6b
@ -1,7 +1,5 @@
|
|||||||
#include "track.h"
|
#include "track.h"
|
||||||
|
|
||||||
#define OUTLIER_WINDOW 21
|
|
||||||
|
|
||||||
int Track::_elevationWindow = 3;
|
int Track::_elevationWindow = 3;
|
||||||
int Track::_speedWindow = 5;
|
int Track::_speedWindow = 5;
|
||||||
int Track::_heartRateWindow = 3;
|
int Track::_heartRateWindow = 3;
|
||||||
@ -14,13 +12,13 @@ int Track::_pauseInterval = 10;
|
|||||||
bool Track::_outlierEliminate = true;
|
bool Track::_outlierEliminate = true;
|
||||||
|
|
||||||
|
|
||||||
static qreal median(QVector<qreal> v)
|
static qreal median(QVector<qreal> &v)
|
||||||
{
|
{
|
||||||
qSort(v.begin(), v.end());
|
qSort(v.begin(), v.end());
|
||||||
return v.at(v.size() / 2);
|
return v.at(v.size() / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static qreal MAD(QVector<qreal> v, qreal m)
|
static qreal MAD(QVector<qreal> &v, qreal m)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < v.size(); i++)
|
for (int i = 0; i < v.size(); i++)
|
||||||
v[i] = qAbs(v.at(i) - m);
|
v[i] = qAbs(v.at(i) - m);
|
||||||
@ -28,21 +26,17 @@ static qreal MAD(QVector<qreal> v, qreal m)
|
|||||||
return v.at(v.size() / 2);
|
return v.at(v.size() / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QSet<int> eliminate(const QVector<qreal> &v, int window)
|
static QSet<int> eliminate(const QVector<qreal> &v)
|
||||||
{
|
{
|
||||||
QSet<int> rm;
|
QSet<int> rm;
|
||||||
qreal m, M;
|
|
||||||
|
|
||||||
|
QVector<qreal> w(v);
|
||||||
|
qreal m = median(w);
|
||||||
|
qreal M = MAD(w, m);
|
||||||
|
|
||||||
if (v.size() < window)
|
for (int i = 0; i < v.size(); i++)
|
||||||
return rm;
|
if (qAbs((0.6745 * (v.at(i) - m)) / M) > 5)
|
||||||
|
|
||||||
for (int i = window/2; i < v.size() - window/2; i++) {
|
|
||||||
m = median(v.mid(i - window/2, window));
|
|
||||||
M = MAD(v.mid(i - window/2, window), m);
|
|
||||||
if (qAbs((0.6745 * (v.at(i) - m)) / M) > 3.5)
|
|
||||||
rm.insert(i);
|
rm.insert(i);
|
||||||
}
|
|
||||||
|
|
||||||
return rm;
|
return rm;
|
||||||
}
|
}
|
||||||
@ -74,18 +68,17 @@ static Graph filter(const Graph &g, int window)
|
|||||||
|
|
||||||
Track::Track(const TrackData &data) : _data(data)
|
Track::Track(const TrackData &data) : _data(data)
|
||||||
{
|
{
|
||||||
qreal dt, ds, total;
|
QVector<qreal> acceleration;
|
||||||
int last;
|
|
||||||
|
|
||||||
|
|
||||||
_time.append(0);
|
_time.append(0);
|
||||||
_distance.append(0);
|
_distance.append(0);
|
||||||
_speed.append(0);
|
_speed.append(0);
|
||||||
|
acceleration.append(0);
|
||||||
|
|
||||||
last = 0;
|
int last = 0;
|
||||||
|
|
||||||
for (int i = 1; i < _data.count(); i++) {
|
for (int i = 1; i < _data.count(); i++) {
|
||||||
ds = _data.at(i).coordinates().distanceTo(_data.at(i-1).coordinates());
|
qreal ds = _data.at(i).coordinates().distanceTo(_data.at(i-1).coordinates());
|
||||||
_distance.append(ds);
|
_distance.append(ds);
|
||||||
|
|
||||||
if (_data.first().hasTimestamp() && _data.at(i).hasTimestamp()
|
if (_data.first().hasTimestamp() && _data.at(i).hasTimestamp()
|
||||||
@ -99,12 +92,16 @@ Track::Track(const TrackData &data) : _data(data)
|
|||||||
if (std::isnan(_time.at(i)) || std::isnan(_time.at(i-1)))
|
if (std::isnan(_time.at(i)) || std::isnan(_time.at(i-1)))
|
||||||
_speed.append(NAN);
|
_speed.append(NAN);
|
||||||
else {
|
else {
|
||||||
dt = _time.at(i) - _time.at(i-1);
|
qreal dt = _time.at(i) - _time.at(i-1);
|
||||||
if (dt < 1e-3) {
|
if (dt < 1e-3) {
|
||||||
_speed.append(_speed.at(i-1));
|
_speed.append(_speed.at(i-1));
|
||||||
|
acceleration.append(acceleration.at(i-1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_speed.append(ds / dt);
|
_speed.append(ds / dt);
|
||||||
|
|
||||||
|
qreal dv = _speed.at(i) - _speed.at(i-1);
|
||||||
|
acceleration.append(dv / dt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,13 +116,13 @@ Track::Track(const TrackData &data) : _data(data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_outlierEliminate)
|
if (_outlierEliminate)
|
||||||
_outliers = eliminate(_speed, OUTLIER_WINDOW);
|
_outliers = eliminate(acceleration);
|
||||||
|
|
||||||
QSet<int>::const_iterator it;
|
QSet<int>::const_iterator it;
|
||||||
for (it = _stop.constBegin(); it != _stop.constEnd(); ++it)
|
for (it = _stop.constBegin(); it != _stop.constEnd(); ++it)
|
||||||
_outliers.remove(*it);
|
_outliers.remove(*it);
|
||||||
|
|
||||||
total = 0;
|
qreal total = 0;
|
||||||
for (int i = 0; i < _data.size(); i++) {
|
for (int i = 0; i < _data.size(); i++) {
|
||||||
if (_outliers.contains(i))
|
if (_outliers.contains(i))
|
||||||
continue;
|
continue;
|
||||||
@ -151,13 +148,13 @@ Graph Track::speed() const
|
|||||||
{
|
{
|
||||||
Graph raw, filtered;
|
Graph raw, filtered;
|
||||||
qreal v;
|
qreal v;
|
||||||
QSet<int> stop;
|
QList<int> stop;
|
||||||
|
|
||||||
for (int i = 0; i < _data.size(); i++) {
|
for (int i = 0; i < _data.size(); i++) {
|
||||||
if (_stop.contains(i) && (!std::isnan(_speed.at(i))
|
if (_stop.contains(i) && (!std::isnan(_speed.at(i))
|
||||||
|| _data.at(i).hasSpeed())) {
|
|| _data.at(i).hasSpeed())) {
|
||||||
v = 0;
|
v = 0;
|
||||||
stop.insert(raw.size());
|
stop.append(raw.size());
|
||||||
} else if (_data.at(i).hasSpeed() && !_outliers.contains(i))
|
} else if (_data.at(i).hasSpeed() && !_outliers.contains(i))
|
||||||
v = _data.at(i).speed();
|
v = _data.at(i).speed();
|
||||||
else if (!std::isnan(_speed.at(i)) && !_outliers.contains(i))
|
else if (!std::isnan(_speed.at(i)) && !_outliers.contains(i))
|
||||||
@ -170,9 +167,8 @@ Graph Track::speed() const
|
|||||||
|
|
||||||
filtered = filter(raw, _speedWindow);
|
filtered = filter(raw, _speedWindow);
|
||||||
|
|
||||||
QSet<int>::const_iterator it;
|
for (int i = 0; i < stop.size(); i++)
|
||||||
for (it = stop.constBegin(); it != stop.constEnd(); ++it)
|
filtered[stop.at(i)].setY(0);
|
||||||
filtered[*it].setY(0);
|
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
@ -204,13 +200,13 @@ Graph Track::temperature() const
|
|||||||
Graph Track::cadence() const
|
Graph Track::cadence() const
|
||||||
{
|
{
|
||||||
Graph raw, filtered;
|
Graph raw, filtered;
|
||||||
QSet<int> stop;
|
QList<int> stop;
|
||||||
qreal c;
|
qreal c;
|
||||||
|
|
||||||
for (int i = 0; i < _data.size(); i++) {
|
for (int i = 0; i < _data.size(); i++) {
|
||||||
if (_data.at(i).hasCadence() && _stop.contains(i)) {
|
if (_data.at(i).hasCadence() && _stop.contains(i)) {
|
||||||
c = 0;
|
c = 0;
|
||||||
stop.insert(raw.size());
|
stop.append(raw.size());
|
||||||
} else if (_data.at(i).hasCadence() && !_outliers.contains(i))
|
} else if (_data.at(i).hasCadence() && !_outliers.contains(i))
|
||||||
c = _data.at(i).cadence();
|
c = _data.at(i).cadence();
|
||||||
else
|
else
|
||||||
@ -221,9 +217,8 @@ Graph Track::cadence() const
|
|||||||
|
|
||||||
filtered = filter(raw, _cadenceWindow);
|
filtered = filter(raw, _cadenceWindow);
|
||||||
|
|
||||||
QSet<int>::const_iterator it;
|
for (int i = 0; i < stop.size(); i++)
|
||||||
for (it = stop.constBegin(); it != stop.constEnd(); ++it)
|
filtered[stop.at(i)].setY(0);
|
||||||
filtered[*it].setY(0);
|
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
@ -231,13 +226,13 @@ Graph Track::cadence() const
|
|||||||
Graph Track::power() const
|
Graph Track::power() const
|
||||||
{
|
{
|
||||||
Graph raw, filtered;
|
Graph raw, filtered;
|
||||||
QSet<int> stop;
|
QList<int> stop;
|
||||||
qreal p;
|
qreal p;
|
||||||
|
|
||||||
for (int i = 0; i < _data.size(); i++) {
|
for (int i = 0; i < _data.size(); i++) {
|
||||||
if (_data.at(i).hasPower() && _stop.contains(i)) {
|
if (_data.at(i).hasPower() && _stop.contains(i)) {
|
||||||
p = 0;
|
p = 0;
|
||||||
stop.insert(raw.size());
|
stop.append(raw.size());
|
||||||
} else if (_data.at(i).hasPower() && !_outliers.contains(i))
|
} else if (_data.at(i).hasPower() && !_outliers.contains(i))
|
||||||
p = _data.at(i).power();
|
p = _data.at(i).power();
|
||||||
else
|
else
|
||||||
@ -248,22 +243,29 @@ Graph Track::power() const
|
|||||||
|
|
||||||
filtered = filter(raw, _powerWindow);
|
filtered = filter(raw, _powerWindow);
|
||||||
|
|
||||||
QSet<int>::const_iterator it;
|
for (int i = 0; i < stop.size(); i++)
|
||||||
for (it = stop.constBegin(); it != stop.constEnd(); ++it)
|
filtered[stop.at(i)].setY(0);
|
||||||
filtered[*it].setY(0);
|
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal Track::distance() const
|
qreal Track::distance() const
|
||||||
{
|
{
|
||||||
return _distance.isEmpty() ? 0 : _distance.last();
|
for (int i = _distance.size() - 1; i >= 0; i--)
|
||||||
|
if (!_outliers.contains(i))
|
||||||
|
return _distance.at(i);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal Track::time() const
|
qreal Track::time() const
|
||||||
{
|
{
|
||||||
return (_data.size() < 2) ? 0 :
|
for (int i = _data.size() - 1; i >= 0; i--)
|
||||||
(_data.first().timestamp().msecsTo(_data.last().timestamp()) / 1000.0);
|
if (!_outliers.contains(i))
|
||||||
|
return _data.first().timestamp().msecsTo(_data.at(i).timestamp())
|
||||||
|
/ 1000.0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal Track::movingTime() const
|
qreal Track::movingTime() const
|
||||||
|
Loading…
x
Reference in New Issue
Block a user