Getting GPSd to work with Python and Threading

I bought a few Raspberry Pis with great ambition of doing something awesome with them. Beyond showing my co-workers how cool I was for having them, the Raspberry Pis  pretty much just sat on my desk collecting dust. A month or two after getting them I realized that the Pis would fit nicely into our RallyRecorder project as a GPS tracker/recorder. Previously we had this running on a netbook with a USB GPS receiver using GPSd, a combination of command-line PHP and shell scripts, and MySQL. With the reduced specs on the Raspberry Pi (700 MHz, 256 MB RAM), I wanted to go with something with a little less overhead. Wisely or not, I chose Python and SQLite. Not having worked with either of the before, it should also provide a nice challenge.

After I started looking around the web for info about getting GPSd working with Python, I quickly found that any information was out of date, out of version, or just didn\’t compile. It didn\’t help that I started learning Python last night. Hey, go big or go home, right?

First we\’ll quickly go through how to get all of the software prereqs. This is written for the Raspberry Pi but should work on most Linux distros too.

Required Hardware

  • Raspberry Pi with Debian Wheezy installed (or something running Linux)
  • GPSd compatible GPS Receiver (I used this one)

Getting the Software

If you have a USB GPS receiver, don\’t plug it in yet.

From the command-line, enter the following command to install Python, GPSd, and the Python modules to bring them together.

sudo apt-get install python gpsd gpsd-clients

If you have a USB receiver, you can plug it in now. For Bluetooth and serial receivers, you\’re on your own for this step. Plugging in the USB receiver should start GPSd automatically. To make sure that GPSd is playing nice, you can open cgps to see what data it is receiving.

cgps

After your GPS receiver has a satellite fix, it should look something like this:

Reading GPS data with Python

Now that GPSd is communicating successfully with your receiver and you have the right software installed, we can read in the GPS data through Python. The code below uses threading to get every GPS update (otherwise the buffer fills up) and when requested (lines 31-54) fetches the most recent set of data. You can replace lines 31-54 with whatever you want to do with the data. I plan on storing unique points in a SQLite database (maybe in a follow-up post).

#! /usr/bin/python
# Written by Dan Mandle http://dan.mandle.me September 2012
# License: GPL 2.0

import os
from gps import *
from time import *
import time
import threading

gpsd = None #seting the global variable

os.system('clear') #clear the terminal (optional)

class GpsPoller(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
global gpsd #bring it in scope
gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info
self.current_value = None
self.running = True #setting the thread running to true

def run(self):
global gpsd
while gpsp.running:
gpsd.next() #this will continue to loop and grab EACH set of gpsd info to clear the buffer

if __name__ == '__main__':
gpsp = GpsPoller() # create the thread
try:
gpsp.start() # start it up
while True:
#It may take a second or two to get good data
#print gpsd.fix.latitude,', ',gpsd.fix.longitude,' Time: ',gpsd.utc

os.system('clear')

print
print ' GPS reading\'
print '----------------------------------------\'
print 'latitude ' , gpsd.fix.latitude
print 'longitude ' , gpsd.fix.longitude
print 'time utc ' , gpsd.utc,' + ', gpsd.fix.time
print 'altitude (m)' , gpsd.fix.altitude
print 'eps ' , gpsd.fix.eps
print 'epx ' , gpsd.fix.epx
print 'epv ' , gpsd.fix.epv
print 'ept ' , gpsd.fix.ept
print 'speed (m/s) ' , gpsd.fix.speed
print 'climb ' , gpsd.fix.climb
print 'track ' , gpsd.fix.track
print 'mode ' , gpsd.fix.mode
print
print 'sats ' , gpsd.satellites

time.sleep(5) #set to whatever

except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
print "\nKilling Thread..."
gpsp.running = False
gpsp.join() # wait for the thread to finish what it's doing
print "Done.\nExiting."

[py]#! /usr/bin/python
# Written by Dan Mandle http://dan.mandle.me September 2012
# License: GPL 2.0

import os
from gps import *
from time import *
import time
import threading

gpsd = None #seting the global variable

os.system(\’clear\’) #clear the terminal (optional)

class GpsPoller(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
global gpsd #bring it in scope
gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info
self.current_value = None
self.running = True #setting the thread running to true

def run(self):
global gpsd
while gpsp.running:
gpsd.next() #this will continue to loop and grab EACH set of gpsd info to clear the buffer

if __name__ == \’__main__\’:
gpsp = GpsPoller() # create the thread
try:
gpsp.start() # start it up
while True:
#It may take a second or two to get good data
#print gpsd.fix.latitude,\’, \’,gpsd.fix.longitude,\’ Time: \’,gpsd.utc

os.system(\’clear\’)

print
print \’ GPS reading\’
print \’—————————————-\’
print \’latitude \’ , gpsd.fix.latitude
print \’longitude \’ , gpsd.fix.longitude
print \’time utc \’ , gpsd.utc,\’ + \’, gpsd.fix.time
print \’altitude (m)\’ , gpsd.fix.altitude
print \’eps \’ , gpsd.fix.eps
print \’epx \’ , gpsd.fix.epx
print \’epv \’ , gpsd.fix.epv
print \’ept \’ , gpsd.fix.ept
print \’speed (m/s) \’ , gpsd.fix.speed
print \’climb \’ , gpsd.fix.climb
print \’track \’ , gpsd.fix.track
print \’mode \’ , gpsd.fix.mode
print
print \’sats \’ , gpsd.satellites

time.sleep(5) #set to whatever

except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
print “\\nKilling Thread…”
gpsp.running = False
gpsp.join() # wait for the thread to finish what it\’s doing
print “Done.\\nExiting.”
[/py]

To use this code, copy/paste all of it in to a text editor of you choosing and save it as gpsdData.py. You can then run it with the following command:

[bash]python gpsdData.py[/bash]

I am by no means an expert in well… anything. But this does work with GPSd 3.6, Python 2.7.3, and Debian Wheezy. I am all ears with ways to make this better and more efficient. Please let me know in the comments.

Some resources that helped me put this all together:

Next post →

68 Comments

  1. Get the hardware then make the plan. Learn a language for a project. I love everything about this!

  2. Thanks for this!

  3. Excellent! Thanks!!

  4. I have the exact same environment as you , but I get this error:

    Traceback (most recent call last):
    File “gps.py”, line 29, in
    gpsp = GpsPoller() # create the thread
    File “gps.py”, line 19, in __init__
    gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info
    NameError: global name ‘gps’ is not defined

    • Dan Mandle

      Is it possible that you don’t have the python GPS libraries installed?

      • Jose Vilela

        Hi Dan!
        I just discovered this helpful blog and I’d like to ask you something. I have been trying to run your code to pull data from the GPS module but I always get “0.0” and “nan”. I am using Python 2.7.9, the Raspberry Pi 2 Model B, OS Raspbian “Jessie” and the Adafruit GPS module. I don’t know what’s going on, maybe you can give me more details about the hardware/software that you used to run your code. I would try to reproduce the same scenario here in order to get the code up and running. I am doing a project and getting data from the GPS is my start point. I look forward to hearing from you soon. Hugs. Jose Vilela.

    • Make sure you haven’t named your file “gps.py” otherwise python can’t import the gps.py library,

      You can also copy the gps.py library to the directory where you have your script to make sure it can be imported.

  5. Sorcier_FXK

    Thank’s a lot for your snippet, realy usefull & interesting 🙂

  6. flazer

    nice! thx!

  7. Thanks for your post – it helped me get a gpsd output in python. It made me realise that gps.fix doesn’t hold any useful data until you have called gps.next() enough times to read in some helpful status lines from gpsd.

    Also, I managed to get it working with a QStarz bluetooth gps. I used “sudo apt-get install bluetooth bluez-utils –no-install-recommends” to install a minimal set of bluetooth drivers (no GUI support), then used the instructions at http://www.catb.org/gpsd/bt.html to set up the RFCOMM port and configure gpsd to use it from startup.

    The only catch I had was getting my bluetooth dongle working. To start with I could detect the USB dongle, but not bring up the interface with hcitool. I had to add “dwc_otg.speed=1” to my /boot/cmdline.txt file. That downgrades the speed of the USB bus from USB 2.0 to USB 1.1 – it seems there is a kernel bug with USB support. While changing the bus speed fixed the problem for me, other people report it causes problems with ethernet and other USB devices.

  8. Nice code Dan!! Worked first time. I’d like to add to your code some things I wanted gpxlogger to do. See the whole project thread here: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=45&t=49852&p=389004#p389004 (raspberry forum, “GPS not working well” thread). As always, will be leaving your authorship on the attribution line at the top.

  9. Thanks Great Code!
    The Adafruit example didn’t work for me

  10. Thanks a lot for your post!!! 🙂 It helped me a lot, I finally get the longitude and latitude data from the GPS in python. At first I also got the same error message (“global name ‘gps’ is not defined”). It turned out I had another program in the same folder called gps.py, after I renamed it, the code worked perfectly.
    I am using a Garmin GPS18xUSB, no other code addressing the GPS device directly worked for me.

  11. Thanks a lot for this, I used your code as the basis for creating my own GPSController class.

    http://www.stuffaboutcode.com/2013/09/raspberry-pi-gps-setup-and-python.html

    • Dan Mandle

      Thanks! I’m glad we can share! I’m ‘borrowing’ your code from your OBD-II reader for the Pi to create a car logger! Been busy, so I haven’t been able to work on it, but I’ll let you know when it’s ready! Also let me know if you’d be interested in collaborating!

  12. Anthony VZ

    Why is there self.current_value = None still in this snippet?
    Leftover from http://stackoverflow.com/questions/6146131/python-gps-module-reading-latest-gps-data ?

  13. Thanks alot, i’m going to use your code for a High altitude balloon project.

  14. maximran

    I have a question, i want to use python for my project, and i already install the gpsd and already copy your coding. However, when i try to run it on idle3, it say there are no module name gps. (error at line 5). What should i do?

    • Dan Mandle

      Unfortunately this is the only project I’ve done with Python, so I can’t help you. Hopefully someone who sees this and can help!

      • maximran

        Owh i see. In this project, you are using idle or idle3?

      • maximran

        Ok, i get the problem. I am using the python 3 (idle3= python 3) and in python 3 there are no modules name GPS.
        now i am using idle (python) , but i encountered a different problem… it related to something with ” non-ASCII character ‘\xc2’ ”
        do you know anything about it?

        • Kolumbus42

          Hello, there is no gps module for python3. But i solved the problem using memory mapped files (shared memory) between a gps requester working with python2 and the gps module and a thread which reads the current coordinates from the shared memory with python3. Both have a class design for reuse.

  15. Works like a charm! Thanks!

  16. Dude, you are my hero ! 🙂
    Since the info on Python and gpsd from PerryGeo (just google it) is so outdated, it did not work at all.
    Thanks to your little code example, now finally everything works exactly as expected. Fantastic !

  17. hello
    your script is working good for me
    how can i simply log this data on a CSV file

    i have tried different thing but i am not good in programing
    must be simple , but didn t sucess

    thank you , for all your answer

    • Gabriela

      before time.sleep(5)
      you have to put this;
      with open(‘gpsdData.csv’, ‘a+’) as csvfile:
      writer = csv.writer(csvfile)
      writer.writerow([gpsd.utc, gpsd.fix.latitude, gpsd.fix.longitude])

      and all data will be saved in a csv file.

  18. Glidux

    Hi, is there a simple way to tanslate results from gpsdData.py to kml (for Google earth use) ?

  19. Sukotruco

    Hi , this post has been really helpfull…
    I´m really interesting in send the datas to google map in a client pc…
    Thanks

  20. Thank you! This post is awesome. The code actually works for me without any modification. I now need to have this data streamed on an actual web page…

  21. Bloody useful, Thanks

  22. Thanks a lot for the code! It’s a great piece of code for figuring out threading in python as well as how to interface with gpsd. It worked pretty much right out of the box with a USB puck on my raspberry B+. One thing, i also ran into the “global name ‘gps’ is not defined” at first. Turns out you cannot call the code gps-py, rather name it gpsmon.py or something.

  23. hi all

    i am having a problem with latitude and longitude readings.
    It seems to only appear 0.0 for both readings.
    any help?

    • Dan Mandle

      Are you getting readings with cgps? If not, it’s likely that your GPS unit doesn’t have a GPS fix yet.

  24. Joe Perch

    Dan,
    Thank you for this. I found you from a link on the Adafruit website. I am completely new to linux, Raspberry Pi and Python (a triple threat). But this just worked. Better than that, I think I know how to get the information I want from the daemon.

    My backup plan was to use Pyserial to read the UART directly and just parse out what I wanted from the NMEA sentences. Not pretty. But I think I have some code already for that. I thin this will be way more elegant.

    Now all I have to do is learn enough Python to write my application. 🙂

    Thanks again,
    Joe

    • Dan Mandle

      If Python isn’t your thing, there are also libraries for at least PHP and Node.JS. Note: Node is pretty slow on the RPi.

  25. Anyone else have trouble with this code at the .join() line? it hangs the whole thing and I have to use a fairly serious kill -9 to get rid of it all. Any help or ideas are appreciated, I have used this code verbatim as well as with debugging code, it must be beyond my skills. The non-thread equivalent seems to work fine(just took the same steps in a simple loop to prove that the process works), but I need to always grab the newest values.

    Thank you,

  26. Tanks! Great Code! This helped me a lot!

  27. Worked Great! Just don’t save it as gps.py – that creates big problems…

  28. Ted Dunn

    Mr.Mandle,
    Thank you. Your gpsd thread works fine.
    I volunteer at a small tourist railway. I built a gps activated system to play sound effect when we run Halloween trains. It was built around a Parallax chip. I chose this because I am fluent in programming in basic.
    The system work well but there were two problems. 1- I was out of memory in the parallax chip. And 2- The GPS receiver was not weather proof.I found the BU353 and decided to learn Python. I am using your program and learning Python on a Raspberry Pi at the same time. When using the Parallax chip, the receiver sent the following data via the RS232 port: $GPRMC,195928.177,A,3547.7690,N,07904.4575,W,0.17,150.49,040814,,,A*71
    I expected the same data from the BU353. Do you where I can find information on the GPS formats for data?
    I did have a problem when the data format on the BU353 suddenly changed for some unknown reason.
    I am using pygame to play the audio tracks and it sometimes stops working and I have to reboot the Raspberry Pi.

  29. Awesome work and works well!!!
    Thank you for putting this up – I just have a couple of questions though..

    Do you know if there is a way to get a ‘Lock’ and a ‘Valid Data’ status out of either GPSD or the data coming into Python?

    I would like to make a little breakout box that connects to a RaspberryPi’s GPI/O header that when GPSD has a lock it brings on an LED (and turns off when it looses a fix) and when there is data received it will flash another LED.

    I am not too fussed on accuracy of the LED’s PPS – its more of an indication that the unit has powered up correctly, locked on to satellites and is getting valid data.

    I just cant seem to find anything on line for this, and would be really appreciative of any assistance you can render.

    • Dan Mandle

      Glad it’s working well for you. I assume you mean a satellite lock? If you look at the print statements in the Python code, there are a few ways to determine that. If you have lat/lon data that’s >0, you have a fix, for instance. You can also see the number of satellites you’re using through gpsd.satellites. As for “data received”, it’s actually going to be coming in faster than you’re going to be able to handle it, which is why we’re using threading. If you really what it to flash for each read, you can add that function near gpsd.next(), but I suggest you have it flash as you process the data instead (inside the main loop).

  30. Hello again Mr. Mandle,

    As I said back in July I am using your GPS code to play Halloween sound effects. It works fine. I have it installed and running on 2 systems. I just tried to install the gps software on another Raspberry Pi, but the: sudo apt-get install python gpsd gpsd-clients procedure does not work. When I launch cgps -s, it times out. I found that the /lib/udev/gpsd.hotplug file does not exist and copying it from another source doesn’t thelp.

    Any suggestions??

    Regards, Ted Dunn

  31. Nelson Farrier

    Thanks so much for sharing this a while back. I’ve been successful getting this to track our school’s laptop carts so teachers can find them. Unfortunately, I’m having trouble getting them to stay working with the Raspberry Pi Zero. It works like a charm w/ Raspberry Pi models A and B, but it often doesn’t getting signals often with the PI Zero. I’m using the same gps modules and made sure they work w/ the A & B.

    Suggestions?

  32. Bruce

    Using same Pi model and cgps is working ok as long as Jupiter T has been initialized first using TAC32. I need to install backup battery to save config in Jupiter so that doesn’t happen. I am trying to copy the Python code you have shown but every time I paste it the line numbers and code are separated and I can’t get them back together without
    editing and adding the line numbers. This happens with notepad and wordpad even using the Pi web and copying then pasting into Python editor same happens. Is there a way you could provide a way to copy the code to download? Oh and not sure your still looking at this page.
    Thanks,
    Bruce

  33. Hi Dan,

    Thanks for posting this, but I just wanted to point out one thing — this code’s not thread-safe.

    You’re accessing the “gpsd” variable from multiple threads without any synchronization, so technically, every once in a great while, you’ll get a mish-mash of two subsequent GPS readings (e.g. latitude from time A with longitude from time B).

  34. hblanken

    Awesome – worked out of the box! Make sure to have the ublox gpsd enabled like here: https://www.raspberrypi.org/forums/viewtopic.php?t=53644

  35. GREAT, worked a treat on OrangePi, after having prob with cat reading UART but not GPSD found solution
    here: https://www.raspberrypi.org/forums/viewtopic.php?t=63799
    and using answer by Screspo altered default/gpsd to point to serial input

  36. Hi Dan,

    Thanks for your code, it is a great base to do everything with python and gpsd.

    I put it to work in a Rpi Zero W, last version of Raspian (Stretch) without touching anything of your code.

    Cheers!

  37. Paul F

    Hi Dan, thanks a ton for this code. If I wanted to see the data at 10hz rate, what would I need to modify? I assume the time.sleep (5) line would modify this. It seems like after running your program the GPS is updating very very slowly.

    • Dan Mandle

      Your GPS receiver would need to support 10hz. Read the rest of the post to see how the code works. If there’s data available, it’ll populate.

  38. This is incredible to find a solution that only needed to be migrated to Python3 by updating the print statements. It is perfect for helping log values from gpsd. Thank you.

    • ataylor

      Could you possibly share the updated print statements for Python3?

  39. ataylor

    Is there a way to have this print the information to a file? I’m very new to coding and can do a few basic things but this exceeds the little knowledge I have. Thank you in advance!

  40. Dear Dan, only one word: Amazing post! Amazing job! after 7 days surfing everywhere, I found here all I need to run my Raspi+U-blox 8 USB GPS dongle. Thank You very much! 🙂

  41. Kevin Shumaker

    Truly excellent. I’m doing an Adafruit little 4 line display and they anly seem to support python3. Took me a few minutes to update your code from “print ‘data'” to “print(‘data’) and have it do everything I needed it to do. Using a PiZero, generic USB GPS, (shows as ttyACM0), the Adafruit display, and a nice little power pack. I appreciate the effort you took to get this out there.

Leave a Reply

Your email address will not be published. Required fields are marked *