2

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?

charles
  • 192
  • 1
  • 14
  • unless you specifically need gpsd, you can switch it off and use the serialnmea positioning plugin to read from the serial port directly. Alternately, there are 2 other backends, geoclue2 and gipsy, and, IIRC, both also wrap gpsd – Pa_ Dec 26 '19 at 23:24
  • No, you're right, I don't need gpsd, and thank you for the suggestions. I looked at Gipsy and got a lot of errors in the build so put that aside for now given how little support it seems to have. Geoclue is installed on my setup and the Qt app is getting an IP-based position from it (which is great), but it's not getting a GPS position either from the serial device or from gpsd. Any suggestions on how to configure Geoclue to get a GPS position from gpsd or a serial device? – charles Dec 30 '19 at 00:23
  • If you don't need multiple applications to read from it, i'd suggest you give the serialnmea backend a go. As for geoclue, i have never used it, but a quick googling suggests that, at least a few years back, geoclue was, like most gnome-backed projects, outdated and unsupported. Now there is geoclue2 (with its associated QtPositioning plugin), which seems to be able to read from serial directly, without gpsd. – Pa_ Dec 31 '19 at 08:36

1 Answers1

3

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 {
...
charles
  • 192
  • 1
  • 14