I was looking to add GPS to my Raspberry Pi (specifically the Clockwork uConsole) and found this USB GPS Receiver with 2m Extension Cable on the PiHut website.
The unit has a USB interface, a 2 meter long cable, and the GPS receiver itself is magnetic and so can attach to lots of different surfaces. Plus, since it was sold on the PiHut website I was quite confident that it would be easy to get working with the Raspberry Pi. As it happens, it was easy to get working, I just needed to install a couple of extra tools to make sure it was producing the correct data.

In this article we'll look at what happens when you plug in the GPS receiver, and then look at a couple of packages you can use to verify that the GPS receiver is working correctly. This assumes that you are running the latest version of Raspberry Pi OS.
Plug In The GPS Receiver
When you plug the GPS device into your system it will will create a file (or resource) called /dev/ttyAMC0. This might be at the location /dev/ttyACM1, it largely depends on your system and setup but it should exist around that location.
You should be able to stream the resource using cat, type the following command.
cat /dev/ttyAMC0
This should be producing information that looks a little like this.
$GPVTG,,T,,M,0.177,N,0.328,K,A*2B
$GPGGA,21011.00,510.33664,N,0021.33939,W,1,04,3.99,113.8,M,48.4,M,,*48
$GPGSA,A,3,09,11,17,19,,,,,,,,,5.78,3.99,4.18*01
$GPGSV,2,1,06,04,,,32,09,54,198,33,11,20,307,27,12,02,315,17*42
$GPGSV,2,2,06,17,23,215,25,19,37,242,35*77
$GPGLL,510.33664,N,0021.33939,W,201011.00,A,A*79
If you see this stream of data then everything is working correctly and you can start reading from the GPS unit. You should be able to run this command without needing to use sudo, if that's not the case then you need to add your user to the "dialout" group.
sudo adduser [your username] dialout
You should now be able to access the resource.
GPS Daemon
In order to make use of this data we need to install a daemon called GPSD.
GPSD allows programs to connect to the GPS receiver without having to directly access the GPS hardware itself. This means that instead of decoding the data from the GPS devices installed, programs can instead access a JSON feed that contains the GPS information in a more readable format.
To install GPSD run the following.
sudo apt install gpsd
Open up the configuration file at /etc/default/gpsd and look for the option called GPSD_OPTIONS. This points to the location of the GPS resource, which will be the file that we looked at before.
Add the filename to the GPSD_OPTIONS setting.
GPSD_OPTIONS="/dev/ttyACM0"
Once you have changed this setting you can either restart the daemon using the following command.
sudo service gpsd restart
Or, you can restart the Raspberry Pi, which will do the same.
You can test that everything is working by connecting to port 2947 via telnet.
telnet localhost 2947
Trying ::1...
Connected to localhost.
Escape character is '^]'.
{"class":"VERSION","release":"3.22","rev":"3.22","proto_major":3,"proto_minor":14}
You can then use the command "?POLL;" to get data from the GPS daemon
?POLL;
{"class":"POLL","time":"2025-04-12T21:23:23.767Z","active":0,"tpv":[],"gst":[],"sky":[]}
The command WATCH can also be used to activate the device if you want to then receive data from it.
?WATCH={"enable":true,"raw":true};
{"class":"DEVICES","devices":[{"class":"DEVICE","path":"/dev/ttyACM1","driver":"u-blox","subtype":"SW 1.00 (59842),HW 00070000","subtype1":"PROTVER 14.00,GPS;SBAS;GLO;QZSS","activated":"2025-04-12T21:25:04.062Z","flags":1,"native":1,"bps":9600,"parity":"N","stopbits":1,"cycle":1.00,"mincycle":0.02}]}
{"class":"WATCH","enable":true,"json":false,"nmea":false,"raw":0,"scaled":false,"timing":false,"split24":false,"pps":false}
This just shows the raw data being streamed out of the service. Let's read that data using some clients.
Reading GPS
To read the GPS data from GPSD we can install another package called gpsd-clients. This is actually a meta package that contains a number of different programs that allow us to test the GPS receiver to ensure it is doing the right thing.
Use apt to install gpsd-clients.
sudo apt install gpsd-clients
This will add a number of different programs, so let's look at a few.
xgps
The xgps application provides a GUI interface for the GPS data and can be started using the following command.
xgps
This will open up a window that looks a bit like this.

This shows the satellites that are within view of the GPS receiver and what information they are presenting to the system.
Note, there is more to this application than what you can see here, it's just that my Raspberry Pi cuts off the lower half that contains the actual GPS data. The window can't be resized either, so unless you have a screen big enough this tool is more for confirmation that you are getting a signal.
In the screenshot about you can see the JSON feed at the bottom, which is where the GPS data is being read from.
cgps
The cgps program is a command line tool that reads the GPS signal and produces a lot of information about your location, speed, and altitude.
This program can be run using the following.
cgps
The output will look a little like this.
┌───────────────────────────────────────────┐┌──────────────────Seen 13/Used 9┐
│ Time: 2025-04-06T09:51:42.000Z (18)││GNSS PRN Elev Azim SNR Use│
│ Latitude: 53.40513167 N ││GP 4 4 17.0 317.0 22.0 Y │
│ Longitude: 2.36171800 W ││GP 5 5 10.0 82.0 29.0 Y │
│ Alt (HAE, MSL): 86.000, 37.400 m ││GP 16 16 20.0 285.0 39.0 Y │
│ Speed: 0.03 km/h ││GP 18 18 33.0 160.0 42.0 Y │
│ Track (true, var): n/a deg ││GP 20 20 15.0 46.0 27.0 Y │
│ Climb: 18.00 m/min ││GP 25 25 30.0 111.0 43.0 Y │
│ Status: 3D FIX (18 secs) ││GP 26 26 48.0 287.0 43.0 Y │
│ Long Err (XDOP, EPX): 0.64, +/- 9.5 m ││GP 28 28 50.0 202.0 43.0 Y │
│ Lat Err (YDOP, EPY): 0.98, +/- 14.7 m ││GP 31 31 60.0 247.0 40.0 Y │
│ Alt Err (VDOP, EPV): 2.26, +/- 52.0 m ││GP 9 9 6.0 346.0 0.0 N │
│ 2D Err (HDOP, CEP): 1.18, +/- 22.4 m ││GP 11 11 5.0 28.0 25.0 N │
│ 3D Err (PDOP, SEP): 2.55, +/- 48.5 m ││GP 29 29 66.0 72.0 0.0 N │
│ Time Err (TDOP): 1.15 ││SB127 40 10.0 117.0 39.0 N │
│ Geo Err (GDOP): 2.76 ││ │
│ Speed Err (EPS): +/- 106 km/h ││ │
│ Track Err (EPD): n/a ││ │
│ Time offset: 0.078947500 s ││ │
│ Grid Square: IO83tj67 ││ │
└───────────────────────────────────────────┘└─────────────────────────────────┘
.0000,"altMSL":37.4000,"alt":37.4000,"epx":9.526,"epy":14.748,"epv":51.980,"magv
ar":-1.2,"speed":0.009,"climb":0.300,"eps":29.50,"epc":103.96,"geoidSep":48.600,
"eph":22.420,"sep":48.450}
The left hand side of the screen shows our GPS data, and the right hand side of the screen shows the translated satellite data that we are receiving from the GPS.
You'll need to leave the GPS running for a few minutes to gather all of the information it needs before you see a longitude and latitude. If you can see satellite data on the right hand side then this is a good indication that things are working.
All of the JSON data from the gpsd daemon can be seen running across the bottom of the screen. Use the "-s" flag to hide this if you don't want to see the raw stream of data.
gpsmon
The gpsmon program is another command line application that can convert GPS data of different types into coordiante data.
To run gpsmon just enter the following into the command line
gpsmon
This will produce the following sort of output.
/dev/ttyACM1 u-blox>
┌──────────────────────────┐┌─────────────────────────────────────────────────┐
│Ch PRN Az El S/N Flag U ││ECEF Pos: +3807383.361m -157035.49m +5097615.98m │
│ 0 4 320 16 34 070d Y ││ECEF Vel: -0.01m/s -0.00m/s +0.02m/s │
│ 1 5 84 8 36 070d Y ││ │
│ 2 9 270 4 0 0104 ││LTP Pos: 53.405161535° -2.361825054° 79.60m │
│ 3 11 154 8 33 070d Y ││LTP Vel: 0.00m/s 0.0° 0.00m/s │
│ 4 12 250 1 0 040d ││ │
│ 5 16 70 17 31 070d Y ││Time: 0 09:43:16.00 │
│ 6 18 119 29 29 040d Y ││Time GPS: 2361+ 34996.000 Day: 0 │
│ 7 20 175 15 27 040d Y ││ │
│ 8 26 271 33 31 070d Y ││Est Pos Err 5.36m Est Vel Err 0.00m/s │
│ 9 28 83 44 36 040d Y ││PRNs: 11 PDOP: 1.6 Fix 0x03 Flags 0xdd │
│10 31 198 54 22 040d Y │└─────────────────── NAV_SOL ─────────────────────┘
│11 120 196 70 27 040d Y │┌─────────────────────────────────────────────────┐
│12 124 151 28 35 070d Y ││DOP [H] 0.9 [V] 1.4 [P] 1.6 [T] 0.8 [G] 1.8 │
│13 126 147 25 0 0110 │└─────────────────── NAV_DOP ─────────────────────┘
│14 193 0 -91 0 0110 │┌─────────────────────────────────────────────────┐
│15 194 0 -91 0 0110 ││TOFF: 0.073476959 PPS: N/A │
└────── NAV_SVINFO ────────┘└─────────────────────────────────────────────────┘
00000000e726
(26) b56201041200200ff1502b600a4004f0089005a004a00330056ab
(26) b5620120100020ff150278890500390910030700000c90c
This shows the satellite information on the left hand side, and the decoded GPS information on the right hand side.
Interestingly, we can force gpsmon to read data from the GPS device in NMEA format, which is a different type of GPS data used by maritime GPS systems. To do this, we use the --nmea flag.
gpsmon --nmea
This produces output that looks like this.
/dev/ttyACM1 NMEA0183>
┌──────────────────────────────────────────────────────────────────────────────┐
│Time: 2025-04-06T09:54:43.000Z Lat: 53 24.307510' N Lon: 2 21.702710' W │
└───────────────────────────────── Cooked TPV ─────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ GPRMC GPVTG GPGGA GPGSA GPGSV GPGLL │
└───────────────────────────────── Sentences ──────────────────────────────────┘
┌───────────────────────┌─────────────────────────┌────────────────────────────┐
│ SVID PRN Az El SN HU│Time: 095443.00 │Time: 095443.00 │
│GP 4 4 316 17 32 Y│Latitude: 5324.30751 N │Latitude: 5324.30751 │
│GP 5 5 81 11 29 Y│Longitude: 00221.70271 W │Longitude: 00221.70271 │
│GP 16 16 285 22 35 Y│Speed: 0.238 │Altitude: 29.8 │
│GP 18 18 159 34 42 Y│Course: │Quality: 2 Sats: 10 │
│GP 20 20 45 15 32 Y│Status: A FAA:D │HDOP: 0.96 │
│GP 25 25 112 28 39 Y│MagVar: │Geoid: 48.6 │
│GP 26 26 287 49 44 Y└───────── RMC ───────────└─────────── GGA ────────────┘
│GP 28 28 201 48 37 Y┌─────────────────────────┌────────────────────────────┐
│GP 29 29 71 65 27 Y│Mode: A3 Sats: 4 5 16 + │UTC: RMS: │
│GP 31 31 244 59 44 Y│DOP H=0.96 V=1.56 P=1.84 │MAJ: MIN: │
│GP 9 9 345 6 0 N│TOFF: 0.078758244 │ORI: LAT: │
│GP 11 11 28 4 21 N│PPS: N/A │LON: ALT: │
└───v──── GSV ──────────└────── GSA + PPS ────────└─────────── GST ────────────┘
(52) $GPGLL,5324.30751,N,00221.70271,W,095443.00,A,D*73
This shows our longitude and latitude coordinates in the top of the output, with the satellite data below.
Using GPS Data
Now that we have verified that you can read GPS data we can now start building programs around this information. The first port of call might be to use telnet and port 2947, but the GPS daemon library comes with a number of functions that we can use to read GPS data.
To do this we need to install the libgps-dev so that we can get access to the appropriate headers.
sudo apt install libgps-dev
Next we need to write some code that will read from the GPS data using the GPS functions. I found some code from a couple of years ago that needed some modifications to get working, but does read the GPS coordinates and prints them out.
#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... exiting.\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 = %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;
}
Save this file as "gps.c" and run the following to compile the file, the important bit is to inject the GPS library into the compiler using the "-lgps" flag.
gcc gps.c -o gps -lgps
You should now have a program called "gps" that you can run to read out the GPS data from the GPS Daemon. This produces the following output.
/gps
Waiting for fix...
Waiting for fix...
Waiting for fix...
[GPS DATA] Latitude, Longitude, Used satellites, Mode = 53.405161, -2.361825, 0, 3
[GPS DATA] Latitude, Longitude, Used satellites, Mode = 53.405161, -2.361825, 7, 3
[GPS DATA] Latitude, Longitude, Used satellites, Mode = 53.405161, -2.361825, 7, 3
[GPS DATA] Latitude, Longitude, Used satellites, Mode = 53.405161, -2.361825, 7, 3
[GPS DATA] Latitude, Longitude, Used satellites, Mode = 53.405161, -2.361825, 7, 3
Now that we have a mechanism to read GPS data we can do things like write it to a file to create a time based output of our movements. I can't take credit for this code, but you can see how easy it is to plug GPS data into programs you write.
Conclusion
Reading data from a GPS dongle on Linux is quite easy. Once you have verified that you are receiving data through gpsd then you can write a number of programs that wrap around port 2947 to read GPS data. If you don't use the gpsd library then you will have to decode the data it contains into GPS coordinates. Using some of the above code I was able to track a walk around the UK countryside using the Clockwork uConsole.
The GPS dongle has a built in battery that allows it to keep the last recorded data for a few minutes, which means that even if you turn off the system you can get results quickly by polling for results.
Note that having the correct time is extremely important to finding accurate GPS data. If you are getting strange results then make sure the time of your system is set correctly.
Note that none of the locations you see here are my home location. They were all generated whilst out and about in the countryside of the UK.
Add new comment