I have a computer with a GPS connected to a serial port that is running gpsd with a pretty basic configuration. Here is the contents of /etc/default/gpsd:
START_DAEMON="true"
USBAUTO="false"
DEVICES="/dev/ttyS0"
GPSD_OPTIONS="-n -G"
GPSD_SOCKET="/var/run/gpsd.sock"
With this config, gpsd runs fine and all gpsd client utilities, e.g. cgps, gpspipe, gpsmon, can get data from the GPS.
I am trying to access GPS data from a Qt QML program using the PositionSource
element with the following syntax but lat and long show as NaN so it doesn't work:
PositionSource {
id: gpsPos
updateInterval: 500
active: true
nmeaSource: "socket://localhost:2947"
onPositionChanged: {
myMap.update( gpsPos.position )
}
}
I tried piping the NMEA data from the GPS to another port using gpspipe -r | nc -l 6000
and specifying nmeaSource: "socket://localhost:6000
and everything works fine!
How do I make Qt talk to gpsd directly?
After tinkering (i.e. compiling from source, installing, configuring, testing, etc.) with gps-share, Gypsy, geoclue2, serialnmea and other ways to access data from a GPS connected to a serial port (thanks to Pa_ for all the suggestions), but all with no results while gpsd was working perfectly for other apps, I decided to make Qt support gpsd by making a very crude change to the QDeclarativePositionSource class to implement support for a gpsd scheme in the URL for the nmeaSource property. With this change, a gpsd source can now be defined as nmeaSource: "gpsd://hostname:2947"
(2947 is the standard gpsd port).
The changed code is shown below. I would suggest this should be added to Qt at some point but in the meantime, I guess I need to derive this class to implement my change in a new QML component but, being new to QML, I have no idea how that is done. I suppose it would also probably be a good idea to stop and start the NMEA stream from gpsd based on the active
property of the PositionSource item... I will get to it at some point but would appreciate pointers on how to do this in a more elegant way.
void QDeclarativePositionSource::setNmeaSource(const QUrl &nmeaSource)
{
if ((nmeaSource.scheme() == QLatin1String("socket") )
|| (nmeaSource.scheme() == QLatin1String("gpsd"))) {
if (m_nmeaSocket
&& nmeaSource.host() == m_nmeaSocket->peerName()
&& nmeaSource.port() == m_nmeaSocket->peerPort()) {
return;
}
delete m_nmeaSocket;
m_nmeaSocket = new QTcpSocket();
connect(m_nmeaSocket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)> (&QAbstractSocket::error),
this, &QDeclarativePositionSource::socketError);
connect(m_nmeaSocket, &QTcpSocket::connected,
this, &QDeclarativePositionSource::socketConnected);
// If scheme is gpsd, NMEA stream must be initiated by writing a command
// on the socket (gpsd WATCH_ENABLE | WATCH_NMEA flags)
// (ref.: gps_sock_stream function in gpsd source file libgps_sock.c)
if( nmeaSource.scheme() == QLatin1String("gpsd")) {
m_nmeaSocket->connectToHost(nmeaSource.host(),
nmeaSource.port(),
QTcpSocket::ReadWrite);
char const *gpsdInit = "?WATCH={\"enable\":true,\"nmea\":true}";
m_nmeaSocket->write( gpsdInit, strlen(gpsdInit);
} else {
m_nmeaSocket->connectToHost(nmeaSource.host(), nmeaSource.port(), QTcpSocket::ReadOnly);
}
} else {
...