A Beginner’s Guide To Using gpsd (GPS Devices) In Linux

Today, most public vehicles are fitted with Vehicle Location Trackers (VLT) that send location and other diagnostic data to government servers. Most modern connected vehicles around us relay a lot of diagnostic data including geo-location data to their owner’s mobile apps via dedicated servers. You can see the food delivery person bringing your dinner to you on an (almost) realtime basis. Not to mention the wonderful pay-as-you-drive vehicle rental services!

In the modern world, more and more applications have started using geo-location data and services to improve their user experience and even invent new business models!

What Enables Accurate Geo-Location?

GNSS or Global Navigation Satellite Systems enable accurate geo-location. It is a general term for describing any satellite constellation that provides positioning, navigation, and timing (PNT) services on a global or regional basis. While there are a large number of augmentation systems across the world, the most popular international GNSS and their owners are as below.

  • [USA] GPS (Global Positioning System)
  • [China] BDS (BeiDou Navigation Satellite System)
  • [EU] Galileo
  • [Russia] GLONASS (Global Navigation Satellite System)
  • [India] IRNSS/NavIC (Indian Regional Navigation Satellite System / Navigation Indian Constellation)
  • [Japan] QZSS (Quasi-Zenith Satellite System)

Location sensors can be designed to use any of these GNSS – very often even multiple constellations are also supported by the same sensor.

What about Data Formats?

The circuitry within the location sensor communicates with the satellite constellation using RF waves for exchanging raw data. This raw data is then processed to output something called NMEA sentences. NMEA stands for National Marine Electronics Association. NMEA sentences are basically comma-delimited sentences that convey various essential information for accurately obtaining the location of the system.

Rather than dive deep into NMEA and create yet another tutorial, I would point you to a couple of articles that succinctly talk about NMEA.

From RF waves to NMEA sentences, the sensor does a lot! In fact, it has done its job. Over to the host which will be using this data. The host is often a microcontroller or a microprocessor running an operating system based on linux.

Parsing NMEA Sentences On MCU

You can always write your own parser library for NMEA sentences. But, the world has done a lot of work in this direction already and also (very kindly) open-sourced it! If you are just starting off, why not take advantage of it?

Arduino is a pretty popular development framework for microcontroller systems and supports a wide-variety of MCUs today. If Arduino is your thing, check out the wildly popular TinyGPS library for Arduino. If you want to download this from the IDE itself, you can check out this guide on Arduino website.

Of course, one can “skin” the Arduino library to port to your non-Arduino system. Another approach is to use pure C libraries that can be ported to your source tree. The minmea library on GitHub is one such library that I have used successfully. I would also highly recommend you to check out all results on this topic search on GitHub.

Parsing NMEA Sentences On A Linux System

Modern applications tend to not have just location sensing but also other complex stacks like vehicle diagnostics, display drivers, network stacks, filesystems, and so on. Also, in order to differentiate themselves in the market, manufacturers have started providing value added services like geo-fencing, location-based alarms, route calculations, etc.

As a result, it is becoming increasingly common to see geo-location systems based on linux. Such an architecture simplifies implementation, maintenance and most importantly – scaling.

How do you get geo-location data in linux? Let us talk about this now.

About gpsd

gpsd is a service daemon that monitors one or more GPSes or AIS receivers attached to a host computer through serial or USB ports, making all data on the location/course/velocity of the sensors available to be queried on TCP port 2947 of the host computer.

gpsd can be built for a variety of platforms based on linux. A detailed guide can be found here.

Obtaining gpsd

On systems with package manager (desktop distro, RPi, etc.)

On systems that have a package manager like apt, you can install gpsd and related packages very easily. The below steps work on an Ubuntu or any other debian system.

$ sudo apt update
$ sudo apt install gpsd gpsd-clients libgps-dev libgps<n> 

NOTE: Check the value of n above in your package repository by using the Tab key before pressing return.

On systems using Yocto

To add gpsd and related packages to your Yocto build, ensure that the meta-openembedded layer is added to your bblayers.conf file. Alternatively, you can use bitbake-layers to add the meta-openembedded layer to your bitbake configuration.

$ bitbake-layers add-layer <path to meta-openembedded layer>

Once done, you can now add the below line to your conf/local.conf file.

IMAGE_INSTALL_append = " gpsd gps-utils libgps gpsd-gpsctl"

To check that the recipe builds correctly, you can run the below command.

$ bitbake gpsd

Running gpsd and a test client

Once your linux system has booted up, you can start the gpsd using ANY ONE of the below command variations. The communication port is assumed to be /dev/ttyS1 below.

$ gpsd /dev/ttyS1 /* Using default settings */

OR

$ gpsd /dev/ttyS1 -n /* gpsd will read NMEA data from device even if no client is running */

OR

$ gpsd /dev/ttyS1 -n -N -D 3 /* gpsd will read NMEA data from device  even if no client is running, don't send gpsd into background, debug setting 3 (verbose) */

NOTE: For initial testing, use the third command to ensure that everything is OK before using gpsd as a daemon.

cgps test utility – dummy data shown here!

If running the gpsd as a daemon, you can use the cgps test client to check that everything is OK. Execute the below after starting the gpsd as a daemon.

$ cgps -s

Reading location data using gpsd and libgps functions

We are now ready to start reading meaningful data from our GPS device like latitude, longitude, number of satellites, etc.

Below is a sample source code that can be used to read data from the GPS device.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <gps.h>
#include <math.h>

#define SERVER_NAME         "localhost"
#define SERVER_PORT         "2947"

struct gps_data_t           g_gpsdata;
int                         ret;

int main(void) {
    
    // 1) Try GPS open
    ret = gps_open(SERVER_NAME,SERVER_PORT,&g_gpsdata);
    if(ret != 0) {
        printf("[GPS] Can't open...bye!\r\n");
        return -1;
    }

    // 2) Enable the JSON stream - we enable the watch as well
    (void)gps_stream(&g_gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);

    // 3) Wait for data from GPSD
    while (gps_waiting(&g_gpsdata, 5000000)) {
        sleep(2);
        if (-1 == gps_read(&g_gpsdata, NULL, 0)) {
            printf("Read error!! Exiting...\r\n");
            break;
        }
        else {
            if(g_gpsdata.status == STATUS_FIX) {
                if(isfinite(g_gpsdata.fix.latitude) && isfinite(g_gpsdata.fix.longitude)) {
                    printf("[GPS DATA] Latitude, Longitude, Used satellites, Mode = %lf, %lf, %d, %d\r\n" , g_gpsdata.fix.latitude, g_gpsdata.fix.longitude,g_gpsdata.satellites_used,g_gpsdata.fix.mode);        
                }
                else {
                    printf(".");
                }
            }
            else {
                printf("Waiting for fix...\r\n");
            }
        }
    }

    // Close gracefully...
    (void)gps_stream(&g_gpsdata, WATCH_DISABLE, NULL);
    (void)gps_close(&g_gpsdata);
    return 0;
}

Conclusion

In this post, we talked about geo-location, GNSS, popular GNSS systems. Later, we also talk about using geo-location sensors in microcontroller as well as linux-based systems.

The sample example above has been tested on Raspberry Pi, Ubuntu PC as well as the Jupiter Nano SBC.

See you in the next post!

2 thoughts on “A Beginner’s Guide To Using gpsd (GPS Devices) In Linux

  1. This updated code may be more appropriate. The “status” field is not what you think it is, and it’s quite often returned as zero. Use the “mode” field instead. Note that the “status” field has now moved as a sub-element of the “fix” structure.

    #include
    #include
    #include
    #include
    #include
    #define SERVER_NAME “localhost”
    #define SERVER_PORT “2947”
    struct gps_data_t g_gpsdata;
    int ret;
    int main(void) {
    // 1) Try GPS open
    ret = gps_open(SERVER_NAME,SERVER_PORT,&g_gpsdata);
    if(ret != 0) {
    printf(“[GPS] Can’t open…bye!\r\n”);
    return -1;
    }
    // 2) Enable the JSON stream – we enable the watch as well
    (void)gps_stream(&g_gpsdata, WATCH_ENABLE | WATCH_JSON, NULL);
    // 3) Wait for data from GPSD
    while (gps_waiting(&g_gpsdata, 5000000)) {
    sleep(2);
    if (-1 == gps_read(&g_gpsdata, NULL, 0)) {
    printf(“Read error!! Exiting…\r\n”);
    break;
    }
    else {
    if(g_gpsdata.fix.mode == MODE_2D || g_gpsdata.fix.mode == MODE_3D) {
    if(isfinite(g_gpsdata.fix.latitude) && isfinite(g_gpsdata.fix.longitude)) {
    printf(“[GPS DATA] Latitude, Longitude, Used satellites, Mode, Status = %lf, %lf, %d, %d, %d\r\n” , g_gpsdata.fix.latitude, g_gpsdata.fix.longitude,g_gpsdata.satellites_used,g_gpsdata.fix.mode,g_gpsdata.fix.status);
    }
    else {
    printf(“.”);
    }
    }
    else {
    printf(“Waiting for fix…\r\n”);
    }
    }
    }
    // Close gracefully…
    (void)gps_stream(&g_gpsdata, WATCH_DISABLE, NULL);
    (void)gps_close(&g_gpsdata);
    return 0;
    }

Leave a Reply