From c5a060ed6b134516330c856eef810b717ea825e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Tue, 19 Jun 2018 23:56:36 +0200 Subject: [PATCH] Outlier detection is now based on acceleration instead of speed --- src/data/track.cpp | 82 ++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/src/data/track.cpp b/src/data/track.cpp index a84f3821..283b2c92 100644 --- a/src/data/track.cpp +++ b/src/data/track.cpp @@ -1,7 +1,5 @@ #include "track.h" -#define OUTLIER_WINDOW 21 - int Track::_elevationWindow = 3; int Track::_speedWindow = 5; int Track::_heartRateWindow = 3; @@ -14,13 +12,13 @@ int Track::_pauseInterval = 10; bool Track::_outlierEliminate = true; -static qreal median(QVector v) +static qreal median(QVector &v) { qSort(v.begin(), v.end()); return v.at(v.size() / 2); } -static qreal MAD(QVector v, qreal m) +static qreal MAD(QVector &v, qreal m) { for (int i = 0; i < v.size(); i++) v[i] = qAbs(v.at(i) - m); @@ -28,21 +26,17 @@ static qreal MAD(QVector v, qreal m) return v.at(v.size() / 2); } -static QSet eliminate(const QVector &v, int window) +static QSet eliminate(const QVector &v) { QSet rm; - qreal m, M; + QVector w(v); + qreal m = median(w); + qreal M = MAD(w, m); - if (v.size() < window) - return rm; - - 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) + for (int i = 0; i < v.size(); i++) + if (qAbs((0.6745 * (v.at(i) - m)) / M) > 5) rm.insert(i); - } return rm; } @@ -74,18 +68,17 @@ static Graph filter(const Graph &g, int window) Track::Track(const TrackData &data) : _data(data) { - qreal dt, ds, total; - int last; - + QVector acceleration; _time.append(0); _distance.append(0); _speed.append(0); + acceleration.append(0); - last = 0; + int last = 0; 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); 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))) _speed.append(NAN); else { - dt = _time.at(i) - _time.at(i-1); + qreal dt = _time.at(i) - _time.at(i-1); if (dt < 1e-3) { _speed.append(_speed.at(i-1)); + acceleration.append(acceleration.at(i-1)); continue; } _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) - _outliers = eliminate(_speed, OUTLIER_WINDOW); + _outliers = eliminate(acceleration); QSet::const_iterator it; for (it = _stop.constBegin(); it != _stop.constEnd(); ++it) _outliers.remove(*it); - total = 0; + qreal total = 0; for (int i = 0; i < _data.size(); i++) { if (_outliers.contains(i)) continue; @@ -151,13 +148,13 @@ Graph Track::speed() const { Graph raw, filtered; qreal v; - QSet stop; + QList stop; for (int i = 0; i < _data.size(); i++) { if (_stop.contains(i) && (!std::isnan(_speed.at(i)) || _data.at(i).hasSpeed())) { v = 0; - stop.insert(raw.size()); + stop.append(raw.size()); } else if (_data.at(i).hasSpeed() && !_outliers.contains(i)) v = _data.at(i).speed(); else if (!std::isnan(_speed.at(i)) && !_outliers.contains(i)) @@ -170,9 +167,8 @@ Graph Track::speed() const filtered = filter(raw, _speedWindow); - QSet::const_iterator it; - for (it = stop.constBegin(); it != stop.constEnd(); ++it) - filtered[*it].setY(0); + for (int i = 0; i < stop.size(); i++) + filtered[stop.at(i)].setY(0); return filtered; } @@ -204,13 +200,13 @@ Graph Track::temperature() const Graph Track::cadence() const { Graph raw, filtered; - QSet stop; + QList stop; qreal c; for (int i = 0; i < _data.size(); i++) { if (_data.at(i).hasCadence() && _stop.contains(i)) { c = 0; - stop.insert(raw.size()); + stop.append(raw.size()); } else if (_data.at(i).hasCadence() && !_outliers.contains(i)) c = _data.at(i).cadence(); else @@ -221,9 +217,8 @@ Graph Track::cadence() const filtered = filter(raw, _cadenceWindow); - QSet::const_iterator it; - for (it = stop.constBegin(); it != stop.constEnd(); ++it) - filtered[*it].setY(0); + for (int i = 0; i < stop.size(); i++) + filtered[stop.at(i)].setY(0); return filtered; } @@ -231,13 +226,13 @@ Graph Track::cadence() const Graph Track::power() const { Graph raw, filtered; - QSet stop; + QList stop; qreal p; for (int i = 0; i < _data.size(); i++) { if (_data.at(i).hasPower() && _stop.contains(i)) { p = 0; - stop.insert(raw.size()); + stop.append(raw.size()); } else if (_data.at(i).hasPower() && !_outliers.contains(i)) p = _data.at(i).power(); else @@ -248,22 +243,29 @@ Graph Track::power() const filtered = filter(raw, _powerWindow); - QSet::const_iterator it; - for (it = stop.constBegin(); it != stop.constEnd(); ++it) - filtered[*it].setY(0); + for (int i = 0; i < stop.size(); i++) + filtered[stop.at(i)].setY(0); return filtered; } 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 { - return (_data.size() < 2) ? 0 : - (_data.first().timestamp().msecsTo(_data.last().timestamp()) / 1000.0); + 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; } qreal Track::movingTime() const