summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Keller <rainer.keller@nokia.com>2011-02-08 11:19:25 +0000
committerRainer Keller <rainer.keller@nokia.com>2011-02-17 08:20:23 +0000
commit30b310edb7f2a925aa82ff1dbd16f39d6e309e69 (patch)
treef0122807f3d966cd9aa120524fbfea58cb8fe586
parent31c7f1967646a347b3dfafa357329c5aeb052b7d (diff)
Add GPS replay support
In the scripting section one can now open NMEA and GPX files as scripts.
-rw-r--r--library/components/locationui.cpp189
-rw-r--r--library/components/locationui.h60
-rw-r--r--library/components/scriptui.cpp40
-rw-r--r--library/components/scriptui.h1
-rw-r--r--library/remotecontrolwidget.pro4
-rw-r--r--library/scriptadapter.cpp70
-rw-r--r--library/scriptadapter.h5
7 files changed, 363 insertions, 6 deletions
diff --git a/library/components/locationui.cpp b/library/components/locationui.cpp
index 8d8972d..3f1d919 100644
--- a/library/components/locationui.cpp
+++ b/library/components/locationui.cpp
@@ -48,6 +48,10 @@
#include <QtGui/QFormLayout>
#include <QtGui/QInputDialog>
#include <QtGui/QCheckBox>
+#include <QtXml/QDomDocument>
+#include <QtCore/QFile>
+#include <QtCore/QTimer>
+#include <QtLocation/QNmeaPositionInfoSource>
LocationUi::LocationUi(QWidget *parent)
: ToolBoxPage(parent)
@@ -703,3 +707,188 @@ bool LocationScriptInterface::setInUse(int prn, bool inUse)
}
return false;
}
+
+QObject *LocationScriptInterface::loadGPX(const QString &fileName)
+{
+ QGpxPositionInfoSource *source = new QGpxPositionInfoSource();
+ QFile *device = new QFile(source);
+ device->setFileName(fileName);
+
+ if (!device->open(QFile::ReadOnly))
+ delete device;
+ else
+ source->setDevice(device);
+
+ return source;
+}
+
+QObject *LocationScriptInterface::loadNMEA(const QString &fileName)
+{
+ QNmeaPositionInfoSourceWithFinish *source = new QNmeaPositionInfoSourceWithFinish(QtMobility::QNmeaPositionInfoSource::SimulationMode);
+ QFile *device = new QFile(source);
+ device->setFileName(fileName);
+
+ if (!device->open(QFile::ReadOnly))
+ delete device;
+ else
+ source->setDevice(device);
+
+ return source;
+}
+
+QGpxPositionInfoSource::QGpxPositionInfoSource(QObject *parent)
+ :QGeoPositionInfoSource(parent)
+ ,mDevice(0)
+ ,mTimer(new QTimer(this))
+{
+ connect(mTimer, SIGNAL(timeout()), this, SLOT(doUpdate()));
+}
+
+QGpxPositionInfoSource::~QGpxPositionInfoSource()
+{
+}
+
+QtMobility::QGeoPositionInfoSource::PositioningMethods QGpxPositionInfoSource::supportedPositioningMethods() const
+{
+ return QtMobility::QGeoPositionInfoSource::SatellitePositioningMethods;
+}
+
+QIODevice * QGpxPositionInfoSource::device() const
+{
+ return mDevice;
+}
+
+void QGpxPositionInfoSource::setDevice(QIODevice *device)
+{
+ mPositions.clear();
+ mDevice = device;
+
+ QDomDocument doc("gpx");
+ if (!doc.setContent(mDevice))
+ return;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName() != "gpx")
+ return;
+ QDomNodeList track = docElem.elementsByTagName("trk");
+ if (track.count() == 0)
+ return;
+ if (!track.at(0).isElement())
+ return;
+ QString name = track.at(0).toElement().elementsByTagName("name").at(0).toElement().text();
+ QDomNodeList trackpoints = track.at(0).toElement().elementsByTagName("trkpt");
+
+ for (int i = 0; i<trackpoints.count(); i++) {
+ QDomElement trackpoint = trackpoints.at(i).toElement();
+ if (trackpoint.isNull())
+ continue;
+
+ QtMobility::QGeoPositionInfo item;
+ QtMobility::QGeoCoordinate coordinate;
+ QDateTime timestamp;
+ bool ok;
+
+ if (trackpoint.hasAttribute("lat")) {
+ double lat = trackpoint.attribute("lat").toDouble(&ok);
+ if (ok)
+ coordinate.setLatitude(lat);
+ }
+ if (trackpoint.hasAttribute("lon")) {
+ double lon = trackpoint.attribute("lon").toDouble(&ok);
+ if (ok)
+ coordinate.setLongitude(lon);
+ }
+
+ QDomElement elevationElement = trackpoint.firstChildElement("ele");
+ if (!elevationElement.isNull()) {
+ double ele;
+ ele = elevationElement.text().toDouble(&ok);
+ if (ok)
+ coordinate.setAltitude(ele);
+
+ item.setCoordinate(coordinate);
+
+ QDomElement timestampElement = trackpoint.firstChildElement("time");
+ if (timestampElement.isNull())
+ continue;
+
+ timestamp = QDateTime::fromString(timestampElement.text(), Qt::ISODate);
+ if (!timestamp.isValid())
+ continue;
+ item.setTimestamp(timestamp);
+
+ }
+ if (item.isValid())
+ mPositions += item;
+ }
+}
+
+void QGpxPositionInfoSource::startUpdates()
+{
+ if (mPositions.isEmpty())
+ return;
+
+ doUpdate();
+}
+
+void QGpxPositionInfoSource::stopUpdates()
+{
+ mTimer->stop();
+}
+
+void QGpxPositionInfoSource::requestUpdate(int /*timeout*/)
+{
+ startUpdates(); // Should consider timeout
+}
+
+QtMobility::QGeoPositionInfo QGpxPositionInfoSource::lastKnownPosition(bool /*fromSatellitePositioningMethodsOnly*/) const
+{
+ return mLastKnownPosition;
+}
+
+int QGpxPositionInfoSource::minimumUpdateInterval() const
+{
+ return 100;
+}
+
+void QGpxPositionInfoSource::setUpdateInterval(int msec)
+{
+ QGeoPositionInfoSource::setUpdateInterval(msec);
+}
+void QGpxPositionInfoSource::doUpdate()
+{
+ if (mPositions.isEmpty()) {
+ mTimer->stop();
+ return;
+ }
+
+ mLastKnownPosition = mPositions.first();
+ mPositions.pop_front();
+
+ if (!mPositions.isEmpty()) {
+ mTimer->setInterval(mLastKnownPosition.timestamp().secsTo(mPositions[0].timestamp())*1000);
+ mTimer->start();
+ }
+
+ emit positionUpdated(mLastKnownPosition);
+
+ if (mPositions.isEmpty()) {
+ emit finished();
+ }
+}
+
+QNmeaPositionInfoSourceWithFinish::QNmeaPositionInfoSourceWithFinish(QtMobility::QNmeaPositionInfoSource::UpdateMode mode, QObject *parent)
+ :QNmeaPositionInfoSource(mode, parent)
+{
+ connect(this, SIGNAL(positionUpdated(const QGeoPositionInfo &)), this, SLOT(positionUpdatedWrapper()));
+}
+
+QNmeaPositionInfoSourceWithFinish::~QNmeaPositionInfoSourceWithFinish()
+{
+}
+
+void QNmeaPositionInfoSourceWithFinish::positionUpdatedWrapper()
+{
+ if (device()->atEnd())
+ emit finished();
+}
diff --git a/library/components/locationui.h b/library/components/locationui.h
index d4aa0ed..2d214b5 100644
--- a/library/components/locationui.h
+++ b/library/components/locationui.h
@@ -39,6 +39,8 @@
#include <QtCore/QDateTime>
#include <QtCore/QHash>
+#include <QtLocation/QGeoPositionInfoSource>
+#include <QtLocation/QNmeaPositionInfoSource>
class LocationScriptInterface;
class QLineEdit;
@@ -46,6 +48,11 @@ class QDateTimeEdit;
class QRadioButton;
class QComboBox;
class QCheckBox;
+class QGeoPositionInfo;
+
+namespace QtMobility {
+ class QNmeaPositionInfoSource;
+}
class REMOTECONTROLWIDGETSHARED_EXPORT LocationUi : public ToolBoxPage
{
@@ -89,6 +96,9 @@ public:
LocationData locationData() const;
SatelliteData satelliteData() const;
+ QString scriptFromGPX(const QString &);
+ QString scriptFromNMEA(const QString &);
+
public slots:
void setLocation(const LocationUi::LocationData &location);
void setDisplayedLocation(const LocationUi::LocationData &location);
@@ -168,6 +178,8 @@ public:
Q_INVOKABLE qreal azimuth(int satellite) const;
Q_INVOKABLE int signalStrength(int satellite) const;
Q_INVOKABLE bool inUse(int satellite) const;
+ Q_INVOKABLE QObject *loadGPX(const QString &fileName);
+ Q_INVOKABLE QObject *loadNMEA(const QString &fileName);
double latitude() const;
void setLatitude(double);
@@ -216,4 +228,52 @@ private:
LocationUi *ui;
};
+class QNmeaPositionInfoSourceWithFinish : public QtMobility::QNmeaPositionInfoSource
+{
+ Q_OBJECT
+
+public:
+ QNmeaPositionInfoSourceWithFinish(QtMobility::QNmeaPositionInfoSource::UpdateMode, QObject *parent = 0);
+ virtual ~QNmeaPositionInfoSourceWithFinish();
+
+private slots:
+ void positionUpdatedWrapper();
+
+signals:
+ void finished();
+};
+
+class QGpxPositionInfoSource : public QtMobility::QGeoPositionInfoSource
+{
+ Q_OBJECT
+
+public:
+ explicit QGpxPositionInfoSource(QObject *parent = 0);
+ virtual ~QGpxPositionInfoSource();
+
+ QtMobility::QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const;
+ int minimumUpdateInterval() const;
+ QtMobility::QGeoPositionInfoSource::PositioningMethods supportedPositioningMethods() const;
+ QIODevice *device() const;
+ void setDevice(QIODevice *device);
+ void setUpdateInterval(int msec);
+
+signals:
+ void finished();
+
+public slots:
+ void requestUpdate(int timeout = 0);
+ void startUpdates();
+ void stopUpdates();
+
+private slots:
+ void doUpdate();
+
+private:
+ QIODevice *mDevice;
+ QList<QtMobility::QGeoPositionInfo> mPositions;
+ QTimer *mTimer;
+ QtMobility::QGeoPositionInfo mLastKnownPosition;
+};
+
#endif // LOCATIONUI_H
diff --git a/library/components/scriptui.cpp b/library/components/scriptui.cpp
index 43ab657..ada8161 100644
--- a/library/components/scriptui.cpp
+++ b/library/components/scriptui.cpp
@@ -91,7 +91,7 @@ ScriptUi::ScriptUi(ScriptAdapter *adapter, QWidget *parent)
QDir scriptsDir(QCoreApplication::applicationDirPath());
scriptsDir.cd(QLatin1String("scripts"));
QStringList nameFilters;
- nameFilters << "*.js" << "*.qs";
+ nameFilters << "*.js" << "*.qs" << "*.nmea" << "*.gpx";
mFileModel->setNameFilters(nameFilters);
mFileModel->setNameFilterDisables(false);
mFileModel->setRootPath(scriptsDir.path());
@@ -111,7 +111,7 @@ ScriptUi::ScriptUi(ScriptAdapter *adapter, QWidget *parent)
connect(runButton, SIGNAL(clicked()), this, SLOT(runSelectedScript()));
scriptRunnerButtonLayout->addWidget(runButton);
- QPushButton *openDirectoryButton = new QPushButton(tr("Open Folder"));
+ QPushButton *openDirectoryButton = new QPushButton(tr("Show Folder Content"));
connect(openDirectoryButton, SIGNAL(clicked()),
this, SLOT(openScriptDirectory()));
scriptRunnerButtonLayout->addWidget(openDirectoryButton);
@@ -154,7 +154,12 @@ void ScriptUi::runSelectedScript()
if (index.isValid() && !mFileModel->isDir(index)) {
QString scriptFilePath = mFileModel->filePath(index);
- runScript(scriptFilePath);
+ if (scriptFilePath.endsWith(".gpx"))
+ generateLocationReplayScript("GPX", scriptFilePath);
+ else if (scriptFilePath.endsWith(".nmea"))
+ generateLocationReplayScript("NMEA", scriptFilePath);
+ else
+ runScript(scriptFilePath);
}
}
@@ -218,3 +223,32 @@ void ScriptUi::openScriptDirectory()
{
QDesktopServices::openUrl(QUrl::fromLocalFile(mFileModel->rootPath()));
}
+
+void ScriptUi::generateLocationReplayScript(const QString &functionSuffix, const QString &fileName)
+{
+ QString script = "var finished = 0;"
+ "function myFinished() {"
+ " finished = 1;"
+ "}"
+ "function myFunc(update) {"
+ " location.latitude = update.latitude;"
+ " location.longitude = update.longitude;"
+ " location.elevation = update.altitude;"
+ " location.direction = update.direction;"
+ " location.groundSpeed = update.groundSpeed;"
+ " location.verticalSpeed = update.verticalSpeed;"
+ " location.magneticVariation = update.magneticVariation;"
+ " location.horizontalAccuracy = update.horizontalAccuracy;"
+ " location.verticalAccuracy = update.verticalAccuracy;"
+ " location.timestamp = update.timestamp;"
+ "}"
+ "var source = location.load%2(\"%1\");"
+ "source.positionUpdated.connect(myFunc);"
+ "source.finished.connect(myFinished);"
+ "source.startUpdates();"
+ "while(finished==0) yield(250)"
+ ;
+
+ script = script.arg(fileName).arg(functionSuffix);
+ mAdapter->runCode(fileName, script);
+}
diff --git a/library/components/scriptui.h b/library/components/scriptui.h
index 0ba0305..d9bbb69 100644
--- a/library/components/scriptui.h
+++ b/library/components/scriptui.h
@@ -65,6 +65,7 @@ private slots:
void updatePauseButtonText();
void openScriptDirectory();
+ void generateLocationReplayScript(const QString &functionSuffix, const QString &fileName);
private:
ScriptAdapter *mAdapter;
diff --git a/library/remotecontrolwidget.pro b/library/remotecontrolwidget.pro
index ed18905..e151251 100644
--- a/library/remotecontrolwidget.pro
+++ b/library/remotecontrolwidget.pro
@@ -9,12 +9,14 @@ TEMPLATE = lib
include(adjusttarget.pri)
CONFIG += hide_symbols
+CONFIG += mobility
DEFINES += REMOTECONTROLWIDGET_LIBRARY
INCLUDEPATH += .
-QT += core gui script
+QT += core gui script xml
+MOBILITY+=location
SOURCES += \
remotecontrolwidget.cpp \
diff --git a/library/scriptadapter.cpp b/library/scriptadapter.cpp
index b04cca6..bdeef23 100644
--- a/library/scriptadapter.cpp
+++ b/library/scriptadapter.cpp
@@ -43,11 +43,62 @@
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtGui/QMessageBox>
+#include <QtLocation/QGeoPositionInfo>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
+
+QScriptValue toScriptValue(QScriptEngine *engine, const QtMobility::QGeoPositionInfo &s)
+{
+ QScriptValue obj = engine->newObject();
+ obj.setProperty("latitude", s.coordinate().latitude());
+ obj.setProperty("longitude", s.coordinate().longitude());
+ obj.setProperty("altitude", s.coordinate().altitude());
+ obj.setProperty("timestamp", engine->newDate(s.timestamp()));
+ if (s.hasAttribute(QGeoPositionInfo::Direction))
+ obj.setProperty("direction", s.attribute(QGeoPositionInfo::Direction));
+ if (s.hasAttribute(QGeoPositionInfo::GroundSpeed))
+ obj.setProperty("groundSpeed", s.attribute(QGeoPositionInfo::GroundSpeed));
+ if (s.hasAttribute(QGeoPositionInfo::VerticalSpeed))
+ obj.setProperty("verticalSpeed", s.attribute(QGeoPositionInfo::VerticalSpeed));
+ if (s.hasAttribute(QGeoPositionInfo::MagneticVariation))
+ obj.setProperty("magneticVariation", s.attribute(QGeoPositionInfo::MagneticVariation));
+ if (s.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
+ obj.setProperty("horizontalAccuracy", s.attribute(QGeoPositionInfo::HorizontalAccuracy));
+ if (s.hasAttribute(QGeoPositionInfo::VerticalAccuracy))
+ obj.setProperty("verticalAccuracy", s.attribute(QGeoPositionInfo::VerticalAccuracy));
+ return obj;
+}
+
+void fromScriptValue(const QScriptValue &obj, QtMobility::QGeoPositionInfo &s)
+{
+ QtMobility::QGeoCoordinate coord;
+ if (obj.property("latitude").isValid())
+ coord.setLatitude(obj.property("latitude").toNumber());
+ if (obj.property("longitude").isValid())
+ coord.setLongitude(obj.property("longitude").toNumber());
+ if (obj.property("altitude").isValid())
+ coord.setLongitude(obj.property("altitude").toNumber());
+ s.setCoordinate(coord);
+
+ if (obj.property("direction").isValid())
+ s.setAttribute(QGeoPositionInfo::Direction, obj.property("direction").toNumber());
+ if (obj.property("groundSpeed").isValid())
+ s.setAttribute(QGeoPositionInfo::GroundSpeed, obj.property("groundSpeed").toNumber());
+ if (obj.property("verticalSpeed").isValid())
+ s.setAttribute(QGeoPositionInfo::VerticalSpeed, obj.property("verticalSpeed").toNumber());
+ if (obj.property("magneticVariation").isValid())
+ s.setAttribute(QGeoPositionInfo::MagneticVariation, obj.property("magneticVariation").toNumber());
+ if (obj.property("horizontalAccuracy").isValid())
+ s.setAttribute(QGeoPositionInfo::HorizontalAccuracy, obj.property("horizontalAccuracy").toNumber());
+ if (obj.property("verticalAccuracy").isValid())
+ s.setAttribute(QGeoPositionInfo::VerticalAccuracy, obj.property("verticalAccuracy").toNumber());
+ if (obj.property("timestamp").isValid())
+ s.setTimestamp(obj.property("timestamp").toDateTime());
+}
+
ScriptAdapter::ScriptAdapter(QObject *parent)
: QObject(parent)
{
@@ -109,7 +160,12 @@ Script *ScriptAdapter::run(const QString &filePath)
}
QFileInfo fileInfo(file);
- Script *script = new Script(fileInfo.baseName(), file.readAll(), this);
+ return runCode(fileInfo.baseName(), file.readAll());
+}
+
+Script * ScriptAdapter::runCode(const QString &name, const QString &scriptCode)
+{
+ Script *script = new Script(name, scriptCode, this);
script->start();
mActiveScripts += script;
@@ -174,6 +230,8 @@ void Script::setupScriptEngine()
mEngine->globalObject().setProperty(functionName, functionValue);
}
+ qScriptRegisterMetaType(mEngine, toScriptValue, fromScriptValue);
+
// setup script interfaces
QHashIterator<QString, QObject *> iter(mAdapter->mScriptInterfaces);
while (iter.hasNext()) {
@@ -208,7 +266,14 @@ void Script::run()
QScriptValue value = mEngine->evaluate(mScriptCode);
if (value.isError()) {
- messageBox(value.toString(), tr("Script errors"));
+ QString errorText;
+ errorText += value.toString();
+ if (mEngine->hasUncaughtException()) {
+ int line = mEngine->uncaughtExceptionLineNumber();
+ errorText += " in line " + QString::number(line);
+ }
+
+ messageBox(errorText, tr("Script errors"));
}
mMutex.unlock();
@@ -235,6 +300,7 @@ void Script::suspend()
mSuspended = true;
mWaitCondition.wait(&mMutex);
+ QCoreApplication::processEvents();
mStopTimer->start();
}
diff --git a/library/scriptadapter.h b/library/scriptadapter.h
index e36b02f..d86218e 100644
--- a/library/scriptadapter.h
+++ b/library/scriptadapter.h
@@ -43,6 +43,10 @@
#include <QtCore/QMutex>
#include <QtCore/QWaitCondition>
#include <QtScript/QScriptEngine>
+#include <QtLocation/QGeoPositionInfo>
+
+using namespace QtMobility;
+Q_DECLARE_METATYPE(QGeoPositionInfo);
class Script;
class QMessageBox;
@@ -61,6 +65,7 @@ public:
public slots:
Script *run(const QString &filePath);
+ Script *runCode(const QString &name, const QString &scriptCode);
signals:
void scriptStart(Script *script) const;