Reimplement the internal repesentation of GPS fixes

Instead of using the Settings as our data structure and constantly accessing
them, we now have a QMap for the GPS fixes and only access the Settings to keep
them in sync with the QMap. This should significantly speed things up.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Dirk Hohndel 2016-01-09 14:07:58 -08:00
parent 7701975d98
commit 33142ed7f8
3 changed files with 110 additions and 129 deletions

View file

@ -1,5 +1,6 @@
#include "gpslistmodel.h" #include "gpslistmodel.h"
#include "helpers.h" #include "helpers.h"
#include <QVector>
GpsListModel *GpsListModel::m_instance = NULL; GpsListModel *GpsListModel::m_instance = NULL;
@ -17,7 +18,7 @@ void GpsListModel::addGpsFix(gpsTracker g)
void GpsListModel::update() void GpsListModel::update()
{ {
QVector<gpsTracker> trackers = GpsLocation::instance()->currentGPSInfo(); QVector<gpsTracker> trackers = QVector<gpsTracker>::fromList(GpsLocation::instance()->currentGPSInfo().values());
beginResetModel(); beginResetModel();
m_gpsFixes = trackers; m_gpsFixes = trackers;
endResetModel(); endResetModel();

View file

@ -98,16 +98,12 @@ QString GpsLocation::currentPosition()
{ {
if (!hasLocationsSource()) if (!hasLocationsSource())
return tr("Unknown GPS location"); return tr("Unknown GPS location");
int nr = geoSettings->value("count", 0).toInt(); if (m_trackers.count() && m_trackers.lastKey() + 300 >= QDateTime::currentMSecsSinceEpoch() / 1000) {
if (nr) { // we can simply use the last position that we tracked
qDebug() << "last fix at" << geoSettings->value(QString("gpsFix%1_time").arg(nr - 1)).toULongLong() << gpsTracker gt = m_trackers.last();
"right now" << QDateTime::currentMSecsSinceEpoch() / 1000; QString gpsString = printGPSCoords(gt.latitude.udeg, gt.longitude.udeg);
if (geoSettings->value(QString("gpsFix%1_time").arg(nr - 1)).toULongLong() + 300 > QDateTime::currentMSecsSinceEpoch() / 1000) { qDebug() << "returning last position" << gpsString;
QString gpsString = printGPSCoords(geoSettings->value(QString("gpsFix%1_lat").arg(nr - 1)).toInt(), return gpsString;
geoSettings->value(QString("gpsFix%1_lon").arg(nr - 1)).toInt());
qDebug() << "returning last position" << gpsString;
return gpsString;
}
} }
qDebug() << "requesting new GPS position"; qDebug() << "requesting new GPS position";
m_GpsSource->requestUpdate(); m_GpsSource->requestUpdate();
@ -123,11 +119,12 @@ void GpsLocation::newPosition(QGeoPositionInfo pos)
QGeoCoordinate lastCoord; QGeoCoordinate lastCoord;
QString msg("received new position %1"); QString msg("received new position %1");
status(qPrintable(msg.arg(pos.coordinate().toString()))); status(qPrintable(msg.arg(pos.coordinate().toString())));
int nr = geoSettings->value("count", 0).toInt(); int nr = m_trackers.count();
if (nr) { if (nr) {
lastCoord.setLatitude(geoSettings->value(QString("gpsFix%1_lat").arg(nr - 1)).toInt() / 1000000.0); gpsTracker gt = m_trackers.last();
lastCoord.setLongitude(geoSettings->value(QString("gpsFix%1_lon").arg(nr - 1)).toInt() / 1000000.0); lastCoord.setLatitude(gt.latitude.udeg / 1000000.0);
lastTime = geoSettings->value(QString("gpsFix%1_time").arg(nr - 1)).toULongLong(); lastCoord.setLongitude(gt.longitude.udeg / 1000000.0);
lastTime = gt.when;
} }
// if we are waiting for a position update or // if we are waiting for a position update or
// if we have no record stored or if at least the configured minimum // if we have no record stored or if at least the configured minimum
@ -136,14 +133,12 @@ void GpsLocation::newPosition(QGeoPositionInfo pos)
(int64_t)pos.timestamp().toTime_t() > lastTime + prefs.time_threshold || (int64_t)pos.timestamp().toTime_t() > lastTime + prefs.time_threshold ||
lastCoord.distanceTo(pos.coordinate()) > prefs.distance_threshold) { lastCoord.distanceTo(pos.coordinate()) > prefs.distance_threshold) {
waitingForPosition = false; waitingForPosition = false;
geoSettings->setValue("count", nr + 1); gpsTracker gt;
gt.when = pos.timestamp().toTime_t();
int64_t when = pos.timestamp().toTime_t(); gt.when += gettimezoneoffset(gt.when);
when += gettimezoneoffset(when); gt.latitude.udeg = rint(pos.coordinate().latitude() * 1000000);
geoSettings->setValue(QString("gpsFix%1_time").arg(nr), (quint64)when); gt.longitude.udeg = rint(pos.coordinate().longitude() * 1000000);
geoSettings->setValue(QString("gpsFix%1_lat").arg(nr), rint(pos.coordinate().latitude() * 1000000)); addFixToStorage(gt);
geoSettings->setValue(QString("gpsFix%1_lon").arg(nr), rint(pos.coordinate().longitude() * 1000000));
geoSettings->sync();
} }
} }
@ -200,14 +195,14 @@ QString GpsLocation::getUserid(QString user, QString passwd)
int GpsLocation::getGpsNum() const int GpsLocation::getGpsNum() const
{ {
return geoSettings->value("count", 0).toInt(); return m_trackers.count();
} }
static void copy_gps_location(struct gpsTracker *gps, struct dive *d) static void copy_gps_location(struct gpsTracker &gps, struct dive *d)
{ {
struct dive_site *ds = get_dive_site_by_uuid(d->dive_site_uuid); struct dive_site *ds = get_dive_site_by_uuid(d->dive_site_uuid);
ds->latitude = gps->latitude; ds->latitude = gps.latitude;
ds->longitude = gps->longitude; ds->longitude = gps.longitude;
} }
#define SAME_GROUP 6 * 3600 /* six hours */ #define SAME_GROUP 6 * 3600 /* six hours */
@ -216,17 +211,12 @@ void GpsLocation::applyLocations()
int i; int i;
bool changed = false; bool changed = false;
int last = 0; int last = 0;
int cnt = geoSettings->value("count", 0).toInt(); int cnt = m_trackers.count();
if (cnt == 0) if (cnt == 0)
return; return;
// create a table with the GPS information // create a table with the GPS information
struct gpsTracker *gpsTable = (struct gpsTracker *)calloc(cnt, sizeof(struct gpsTracker)); QList<struct gpsTracker> gpsTable = m_trackers.values();
for (int i = 0; i < cnt; i++) {
gpsTable[i].latitude.udeg = geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt();
gpsTable[i].longitude.udeg = geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt();
gpsTable[i].when = geoSettings->value(QString("gpsFix%1_time").arg(i)).toULongLong();
}
// now walk the dive table and see if we can fill in missing gps data // now walk the dive table and see if we can fill in missing gps data
struct dive *d; struct dive *d;
@ -247,7 +237,7 @@ void GpsLocation::applyLocations()
if (time_during_dive_with_offset(d, gpsTable[j].when, 0)) { if (time_during_dive_with_offset(d, gpsTable[j].when, 0)) {
if (verbose) if (verbose)
qDebug() << "gpsFix is during the dive, pick that one"; qDebug() << "gpsFix is during the dive, pick that one";
copy_gps_location(gpsTable + j, d); copy_gps_location(gpsTable[j], d);
changed = true; changed = true;
last = j; last = j;
break; break;
@ -262,7 +252,7 @@ void GpsLocation::applyLocations()
if (time_during_dive_with_offset(d, gpsTable[j+1].when, 0)) { if (time_during_dive_with_offset(d, gpsTable[j+1].when, 0)) {
if (verbose) if (verbose)
qDebug() << "which is during the dive, pick that one"; qDebug() << "which is during the dive, pick that one";
copy_gps_location(gpsTable + j + 1, d); copy_gps_location(gpsTable[j+1], d);
changed = true; changed = true;
last = j + 1; last = j + 1;
break; break;
@ -277,7 +267,7 @@ void GpsLocation::applyLocations()
} else if (gpsTable[j].when > d->when + d->duration.seconds) { } else if (gpsTable[j].when > d->when + d->duration.seconds) {
if (verbose) if (verbose)
qDebug() << "which is even later after the end of the dive, so pick the previous one"; qDebug() << "which is even later after the end of the dive, so pick the previous one";
copy_gps_location(gpsTable + j, d); copy_gps_location(gpsTable[j], d);
changed = true; changed = true;
last = j; last = j;
break; break;
@ -286,14 +276,14 @@ void GpsLocation::applyLocations()
if (d->when - gpsTable[j].when <= gpsTable[j+1].when - (d->when + d->duration.seconds)) { if (d->when - gpsTable[j].when <= gpsTable[j+1].when - (d->when + d->duration.seconds)) {
if (verbose) if (verbose)
qDebug() << "pick the one before as it's closer to the start"; qDebug() << "pick the one before as it's closer to the start";
copy_gps_location(gpsTable + j, d); copy_gps_location(gpsTable[j], d);
changed = true; changed = true;
last = j; last = j;
break; break;
} else { } else {
if (verbose) if (verbose)
qDebug() << "pick the one after as it's closer to the start"; qDebug() << "pick the one after as it's closer to the start";
copy_gps_location(gpsTable + j + 1, d); copy_gps_location(gpsTable[j+1], d);
changed = true; changed = true;
last = j + 1; last = j + 1;
break; break;
@ -305,7 +295,7 @@ void GpsLocation::applyLocations()
} else { } else {
if (verbose) if (verbose)
qDebug() << "which seems to be the best one for this dive, so pick it"; qDebug() << "which seems to be the best one for this dive, so pick it";
copy_gps_location(gpsTable + j, d); copy_gps_location(gpsTable[j], d);
changed = true; changed = true;
last = j; last = j;
break; break;
@ -327,71 +317,72 @@ void GpsLocation::applyLocations()
mark_divelist_changed(true); mark_divelist_changed(true);
} }
static int timeCompare(const gpsTracker &a, const gpsTracker &b) QMap<qint64, gpsTracker> GpsLocation::currentGPSInfo() const
{ {
return a.when <= b.when; return m_trackers;
} }
QVector< gpsTracker > GpsLocation::currentGPSInfo() const void GpsLocation::addFixToStorage(gpsTracker &gt)
{ {
QVector<gpsTracker> trackers; int nr = m_trackers.count();
geoSettings->setValue("count", nr + 1);
int cnt = geoSettings->value("count", 0).toInt(); geoSettings->setValue(QString("gpsFix%1_time").arg(nr), gt.when);
if (cnt == 0) { geoSettings->setValue(QString("gpsFix%1_lat").arg(nr), gt.latitude.udeg);
qDebug() << "no gps fixes"; geoSettings->setValue(QString("gpsFix%1_lon").arg(nr), gt.longitude.udeg);
return trackers; geoSettings->setValue(QString("gpsFix%1_name").arg(nr), gt.name);
} gt.idx = nr;
geoSettings->sync();
// create a table with the GPS information m_trackers.insert(gt.when, gt);
trackers.reserve(cnt);
struct gpsTracker gt;
for (int i = 0; i < cnt; i++) {
gt.latitude.udeg = geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt();
gt.longitude.udeg = geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt();
gt.when = geoSettings->value(QString("gpsFix%1_time").arg(i)).toULongLong();
gt.name = geoSettings->value(QString("gpsFix%1_name").arg(i)).toString();
trackers.append(gt);
}
std::sort(trackers.begin(), trackers.end(), timeCompare);
return trackers;
} }
void GpsLocation::deleteGpsFix(quint64 when) void GpsLocation::deleteFixFromStorage(gpsTracker &gt)
{ {
int cnt = geoSettings->value("count", 0).toInt();
if (cnt == 0) {
qDebug() << "no gps fixes";
return;
}
bool found = false; bool found = false;
int i; int i;
struct gpsTracker gt; qint64 when = gt.when;
for (i = 0; i < cnt; i++) { int cnt = m_trackers.count();
if (geoSettings->value(QString("gpsFix%1_time").arg(i)).toULongLong() == when) { if (cnt == 0 || !m_trackers.keys().contains(when)) {
if (i < cnt - 1) { qDebug() << "no gps fix with timestamp" << when;
geoSettings->setValue(QString("gpsFix%1_lat").arg(i), geoSettings->value(QString("gpsFix%1_lat").arg(cnt - 1))); return;
geoSettings->setValue(QString("gpsFix%1_lon").arg(i), geoSettings->value(QString("gpsFix%1_lon").arg(cnt - 1)));
geoSettings->setValue(QString("gpsFix%1_time").arg(i), geoSettings->value(QString("gpsFix%1_time").arg(cnt - 1)));
geoSettings->setValue(QString("gpsFix%1_name").arg(i), geoSettings->value(QString("gpsFix%1_name").arg(cnt - 1)));
}
found = true;
break;
}
} }
if (found) { if (geoSettings->value(QString("gpsFix%1_time").arg(gt.idx)).toULongLong() != when) {
qDebug() << "uh oh - index for tracker has gotten out of sync...";
return;
}
if (cnt > 1) {
// now we move the last tracker into that spot (so our settings stay consecutive)
// and delete the last settings entry
when = geoSettings->value(QString("gpsFix%1_time").arg(cnt - 1)).toLongLong();
struct gpsTracker movedTracker = m_trackers.value(when);
movedTracker.idx = gt.idx;
geoSettings->setValue(QString("gpsFix%1_time").arg(movedTracker.idx), when);
geoSettings->setValue(QString("gpsFix%1_lat").arg(movedTracker.idx), movedTracker.latitude.udeg);
geoSettings->setValue(QString("gpsFix%1_lon").arg(movedTracker.idx), movedTracker.longitude.udeg);
geoSettings->setValue(QString("gpsFix%1_name").arg(movedTracker.idx), movedTracker.name);
geoSettings->remove(QString("gpsFix%1_lat").arg(cnt - 1)); geoSettings->remove(QString("gpsFix%1_lat").arg(cnt - 1));
geoSettings->remove(QString("gpsFix%1_lon").arg(cnt - 1)); geoSettings->remove(QString("gpsFix%1_lon").arg(cnt - 1));
geoSettings->remove(QString("gpsFix%1_time").arg(cnt - 1)); geoSettings->remove(QString("gpsFix%1_time").arg(cnt - 1));
geoSettings->remove(QString("gpsFix%1_name").arg(cnt - 1)); geoSettings->remove(QString("gpsFix%1_name").arg(cnt - 1));
cnt--;
geoSettings->setValue("count", cnt);
} }
qDebug() << "found" << found << "at position" << i << "of" << cnt + 1; geoSettings->setValue("count", cnt - 1);
geoSettings->sync();
}
void GpsLocation::deleteGpsFix(qint64 when)
{
struct gpsTracker defaultTracker = { .when = 0 };
struct gpsTracker deletedTracker = m_trackers.value(when, defaultTracker);
if (deletedTracker.when != when) {
qDebug() << "can't find tracker for timestamp" << when;
return;
}
deleteFixFromStorage(deletedTracker);
m_trackers.remove(when);
} }
void GpsLocation::clearGpsData() void GpsLocation::clearGpsData()
{ {
m_trackers.clear();
geoSettings->clear(); geoSettings->clear();
geoSettings->sync(); geoSettings->sync();
} }
@ -415,24 +406,25 @@ void GpsLocation::uploadToServer()
QNetworkAccessManager *manager = new QNetworkAccessManager(qApp); QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
QUrl url(GPS_FIX_ADD_URL); QUrl url(GPS_FIX_ADD_URL);
int count = geoSettings->value("count", 0).toInt(); QList<qint64> keys = m_trackers.keys();
for (int i = 0; i < count; i++) { qint64 key;
Q_FOREACH(key, keys) {
struct gpsTracker gt = m_trackers.value(key);
int idx = gt.idx;
QDateTime dt; QDateTime dt;
QUrlQuery data; QUrlQuery data;
if (geoSettings->contains(QString("gpsFix%1_uploaded").arg(i))) if (geoSettings->contains(QString("gpsFix%1_uploaded").arg(idx)))
continue; continue;
time_t when = geoSettings->value(QString("gpsFix%1_time").arg(i), 0).toULongLong(); dt.setTime_t(gt.when);
dt.setTime_t(when); qDebug() << dt.toString() << get_dive_date_string(gt.when);
qDebug() << dt.toString() << get_dive_date_string(when);
data.addQueryItem("login", prefs.userid); data.addQueryItem("login", prefs.userid);
data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd")); data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd"));
data.addQueryItem("dive_time", dt.toString("hh:mm")); data.addQueryItem("dive_time", dt.toString("hh:mm"));
data.addQueryItem("dive_latitude", QString::number(geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt() / 1000000.0)); data.addQueryItem("dive_latitude", QString::number(gt.latitude.udeg / 1000000.0, 'f', 6));
data.addQueryItem("dive_longitude", QString::number(geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt() / 1000000.0)); data.addQueryItem("dive_longitude", QString::number(gt.longitude.udeg / 1000000.0, 'f', 6));
QString name(geoSettings->value(QString("gpsFix%1_name").arg(i)).toString()); if (gt.name.isEmpty())
if (name.isEmpty()) gt.name = "Auto-created dive";
name = "Auto-created dive"; data.addQueryItem("dive_name", gt.name);
data.addQueryItem("dive_name", name);
status(data.toString(QUrl::FullyEncoded).toUtf8()); status(data.toString(QUrl::FullyEncoded).toUtf8());
QNetworkRequest request; QNetworkRequest request;
request.setUrl(url); request.setUrl(url);
@ -462,8 +454,8 @@ void GpsLocation::uploadToServer()
break; break;
} }
reply->deleteLater(); reply->deleteLater();
status(QString("completed sending gps fix %1 - response: ").arg(i) + reply->readAll()); status(QString("completed sending gps fix %1 - response: ").arg(gt.idx) + reply->readAll());
geoSettings->setValue(QString("gpsFix%1_uploaded").arg(i), 1); geoSettings->setValue(QString("gpsFix%1_uploaded").arg(idx), 1);
} }
} }
@ -497,21 +489,11 @@ void GpsLocation::downloadFromServer()
qDebug() << "problems downloading GPS fixes"; qDebug() << "problems downloading GPS fixes";
return; return;
} }
// create a table with the GPS information qDebug() << "already have" << m_trackers.count() << "GPS fixes";
QHash<int, struct gpsTracker> gpsFixes; QJsonArray downloadedFixes = object.value("dives").toArray();
int existing = geoSettings->value("count", 0).toInt(); qDebug() << downloadedFixes.count() << "GPS fixes downloaded";
for (int i = 0; i < existing; i++) { for (int i = 0; i < downloadedFixes.count(); i++) {
struct gpsTracker gt; QJsonObject fix = downloadedFixes[i].toObject();
gt.latitude.udeg = geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt();
gt.longitude.udeg = geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt();
gt.when = geoSettings->value(QString("gpsFix%1_time").arg(i)).toULongLong();
gpsFixes.insert(gt.when, gt);
}
qDebug() << "already have" << gpsFixes.count() << "GPS fixes";
QJsonArray dives = object.value("dives").toArray();
qDebug() << dives.count() << "GPS fixes downloaded";
for (int i = 0; i < dives.count(); i++) {
QJsonObject fix = dives[i].toObject();
QString date = fix.value("date").toString(); QString date = fix.value("date").toString();
QString time = fix.value("time").toString(); QString time = fix.value("time").toString();
QString name = fix.value("name").toString(); QString name = fix.value("name").toString();
@ -524,18 +506,11 @@ void GpsLocation::downloadFromServer()
gt.latitude.udeg = latitude.toDouble() * 1000000; gt.latitude.udeg = latitude.toDouble() * 1000000;
gt.longitude.udeg = longitude.toDouble() * 1000000; gt.longitude.udeg = longitude.toDouble() * 1000000;
gt.name = name; gt.name = name;
gpsFixes.insert(gt.when, gt); // add this GPS fix to the QMap and the settings (remove existing fix at the same timestamp first)
if (m_trackers.keys().contains(gt.when))
deleteGpsFix(gt.when);
addFixToStorage(gt);
} }
QList<int> keys = gpsFixes.keys();
qSort(keys);
for (int i = 0; i < keys.count(); i++) {
struct gpsTracker gt = gpsFixes.value(keys[i]);
geoSettings->setValue(QString("gpsFix%1_time").arg(i), (quint64)gt.when);
geoSettings->setValue(QString("gpsFix%1_name").arg(i), gt.name);
geoSettings->setValue(QString("gpsFix%1_lat").arg(i), gt.latitude.udeg);
geoSettings->setValue(QString("gpsFix%1_lon").arg(i), gt.longitude.udeg);
}
geoSettings->setValue("count", keys.count());
} else { } else {
qDebug() << "network error" << reply->error() << reply->errorString() << reply->readAll(); qDebug() << "network error" << reply->error() << reply->errorString() << reply->readAll();
} }

View file

@ -8,12 +8,14 @@
#include <QGeoPositionInfo> #include <QGeoPositionInfo>
#include <QSettings> #include <QSettings>
#include <QNetworkReply> #include <QNetworkReply>
#include <QMap>
struct gpsTracker { struct gpsTracker {
degrees_t latitude; degrees_t latitude;
degrees_t longitude; degrees_t longitude;
quint64 when; qint64 when;
QString name; QString name;
int idx;
}; };
class GpsLocation : QObject class GpsLocation : QObject
@ -29,7 +31,7 @@ public:
bool hasLocationsSource(); bool hasLocationsSource();
QString currentPosition(); QString currentPosition();
QVector<gpsTracker> currentGPSInfo() const; QMap<qint64, gpsTracker> currentGPSInfo() const;
private: private:
QGeoPositionInfo lastPos; QGeoPositionInfo lastPos;
@ -42,6 +44,9 @@ private:
void (*showMessageCB)(const char *msg); void (*showMessageCB)(const char *msg);
static GpsLocation *m_Instance; static GpsLocation *m_Instance;
bool waitingForPosition; bool waitingForPosition;
QMap<qint64, gpsTracker> m_trackers;
void addFixToStorage(gpsTracker &gt);
void deleteFixFromStorage(gpsTracker &gt);
public slots: public slots:
void serviceEnable(bool toggle); void serviceEnable(bool toggle);
@ -52,7 +57,7 @@ public slots:
void postError(QNetworkReply::NetworkError error); void postError(QNetworkReply::NetworkError error);
void getUseridError(QNetworkReply::NetworkError error); void getUseridError(QNetworkReply::NetworkError error);
void clearGpsData(); void clearGpsData();
void deleteGpsFix(quint64 when); void deleteGpsFix(qint64 when);
}; };
#endif // GPSLOCATION_H #endif // GPSLOCATION_H