Tuesday, 31 July 2012

using a USB stick GPS receiver with the raspberry pi

when i found my old wifi stick for use with my raspi i also found an ancient USB GPS receiver, so i thought i'd give it a try in the pi and see if i could do anything useful with it.

i gave it a try in my laptop first just to make sure it actually worked. when i first plugged it in it showed up with an exclamation in device manager. a quick check of the vendor id etc showed it to be a prolific pl-2303 providing the USB/serial interface so i downloaded the latest drivers from the prolific site.

that worked and it showed up in device manager as a COM port. a quick test with PuTTY confirmed that the device was spewing out a healthy amount of GPS data. (set PuTTY to serial mode, 4800 bps, 8 data bits, 1 stop bit, no parity...those settings are fairly standard for NMEA compliant gps devices, which presumably mine is. i actually have no idea what make/model it is as there are no identifying marks on it; it's blue with a red led, has a USB logo and "USB G.P.S" written on it. a quick search found this... which looks exactly the same and i will assume is what mine is.)



so, i then plugged it into the pi, which promptly stopped dead in its tracks. i was connecting through a powered usb hub but i guess if the gps stick is of poor quality (i seem to remember it didn't cost very much) then it might have a large inrush current that causes a sag in the 5V supply, possibly making the usb hub and/or wifi stick go undervoltage at the same time or something. i tried turning it off and on again with it plugged in to see if it would boot with it connected or not and it showed up promisingly in dmesg.

[   13.316931] USB Serial support registered for generic
[   13.407359] usbcore: registered new interface driver usbserial_generic
[   13.541442] usbserial: USB Serial Driver core
[   13.726884] USB Serial support registered for pl2303
[   13.990896] pl2303 1-1.2.4:1.0: pl2303 converter detected
[   14.197014] usb 1-1.2.4: pl2303 converter now attached to ttyUSB0

unplugging and replugging caused it to hang again... maybe i'll investigate the cause of that later.

to check if it worked or not i issued the following to install something to connect to the gps stick with
sudo apt-get install minicom
then
sudo minicom -s
to change settings. (in my case, i had to change the device to be ttyUSB0 and change the port speed.)
then i exited and ran minicom with no sudo/options; it was successful and showed GPS data coming out of it again.




i decided it was about time i used the pi for one of its intended purposes; python. to install pySerial you can issue
sudo apt-get install python-serial
or
sudo apt-get install python3-serial
depending on which version you're using (i'm using 2.7.3rc2).
i whipped up a quick script to read 15 lines from the gps stick and print them out. sometimes the first line contains rubbish. the gps stick appears to always be outputting data whether or not the port is open so i guess it depends where in its output process you catch it. to workaround this i simply read 16 lines and just discard the first one so that subsequent readline()s provide useful data. this may or may not be the right thing to do (i know very little about serial communications, so there's probably some sort of flow control that could correct this properly, feel free to comment and enlighten me :)).

my script can be found here. i also know very little about python so my code may not be the most elegant or efficient way of going about what i'm trying to do. again, please feel free to suggest improvements.

if all goes well then you should see output that looks something like this:
pi@raspberrypi ~/usbstick/python/gps_stick $ python gps.py
$GPGSA,A,3,01,14,22,03,28,11,19,,,,,,1.9,1.1,1.5*3B

$GPRMC,211741.000,A,5129.1056,N,00235.2244,W,0.00,,300712,,,A*6C

$GPGGA,211742.000,5129.1056,N,00235.2244,W,1,07,1.1,84.7,M,49.0,M,,0000*75

$GPGSA,A,3,01,14,22,03,28,11,19,,,,,,1.9,1.1,1.5*3B

$GPGSV,3,1,12,11,87,190,28,01,69,286,24,32,56,197,26,19,44,150,24*72

$GPGSV,3,2,12,14,32,068,33,28,31,293,27,20,29,220,,22,19,054,22*74

$GPGSV,3,3,12,17,12,312,16,03,11,151,24,09,03,004,,27,03,351,*77

$GPRMC,211742.000,A,5129.1056,N,00235.2244,W,0.00,,300712,,,A*6F

$GPGGA,211743.000,5129.1056,N,00235.2244,W,1,07,1.1,84.7,M,49.0,M,,0000*74

$GPGSA,A,3,01,14,22,03,28,11,19,,,,,,1.9,1.1,1.5*3B

$GPRMC,211743.000,A,5129.1056,N,00235.2244,W,0.00,,300712,,,A*6E

$GPGGA,211744.000,5129.1056,N,00235.2244,W,1,07,1.1,84.7,M,49.0,M,,0000*73

$GPGSA,A,3,01,14,22,03,28,11,19,,,,,,1.9,1.1,1.5*3B

$GPRMC,211744.000,A,5129.1056,N,00235.2244,W,0.00,,300712,,,A*69

in order to decode the NMEA sentences the format is explained nicely on this web page.

i modified my code to decode the satellite information sentences and one of the fix sentences. it also outputs a url that opens google maps at the decoded lat and long. the final script can be found here. the script has no validation of any sentence checksums, and my python skills are not great, so i would strongly suggest not using this script "as-is" in any sort of production environment, it is intended purely as a proof of concept.

sample output (NB. output was modified to mask real location, but you get the idea) :
pi@raspberrypi ~/usbstick/python/gps_stick $ python gps.py
receiving data from device

= Satellites in view =
Number of satellites in view: 12
*** Satellite PRN 19. SNR 24. Elev 74. Azi 293. ***
*** Satellite PRN 03. SNR 20. Elev 68. Azi 138. ***
*** Satellite PRN 06. SNR 30. Elev 54. Azi 124. ***
*** Satellite PRN 22. SNR 17. Elev 54. Azi 101. ***
*** Satellite PRN 11. SNR 20. Elev 33. Azi 259. ***
*** Satellite PRN 18. SNR 21. Elev 29. Azi 052. ***
*** Satellite PRN 16. SNR 18. Elev 23. Azi 177. ***
*** Satellite PRN 01. SNR 19. Elev 17. Azi 248. ***
*** Satellite PRN 08. SNR 27. Elev 16. Azi 307. ***
*** Satellite PRN 07. SNR 23. Elev 09. Azi 268. ***
*** Satellite PRN 15. SNR 16. Elev 05. Azi 020. ***
*** Satellite PRN 14. SNR 00. Elev 01. Azi 117. ***

= Fix data =
Time of Fix                          : 19:07:52 UTC
Latitude                             : 51 Degrees 28.1376' N
Longitude                            : 002 Degrees 34.1730' W
Fix Quality                          : GOOD
Number of satellites being tracked   : 09
Horizontal DOP, lower = better       : 1.0
Altitude above mean sea level        : 52.0 meters
Height of geoid above WGS84 ellips.  : 49.0 meters
http://maps.google.com?q=51.4856266667,-2.56955

= End =
pi@raspberrypi ~/usbstick/python/gps_stick $

9 comments:

  1. Hey Tom,

    I'm currently working on a program that would receive GPS data using the RasPi's UART serial communication. Can you send me a copy of your python code? I did not see any links to the website that explained what the GPS signal meant. I think the idea is the same I would just have to change where the data is coming from.

    ReplyDelete
  2. Hi Daniel,

    My apologies, the 'theme' i had selected for my blog had basically the same properties for the links as normal text so it was very difficult to actually see them. I have changed to something a bit more obvious now :)

    As I said in the post my python skills are nothing amazing but hopefully it shouldn't be too difficult for you to adapt it to look elsewhere for the input.

    My script is here: http://pastebin.com/Kqn2RhZe
    The NMEA string explanation is here: http://www.gpsinformation.org/dale/nmea.htm

    ReplyDelete
  3. Do I have to install anything to import Serial like you did in the beginning of your code?

    ReplyDelete
  4. Yeah, you need to install pySerial if you don't already have it.
    For python 2.7:
    sudo apt-get install python-serial

    For python 3:
    sudo apt-get install python3-serial

    ReplyDelete
  5. Tom,

    I think I accidentally messed up your code that's on pastebin by accidentally cutting it out instead of copying

    ReplyDelete
  6. Also Tom,

    Where you were changing your minicom settings, did you just type USB0 as the serial device or dev/ttyUSB0?

    ReplyDelete
  7. Hallo, great post!!!! But what is the serial port Number in your python code??

    ReplyDelete
    Replies
    1. Hi,

      The serial port parameters are defined in the initialise function.

      In this case, Port.port = '/dev/ttyUSB0'

      If you're in a different operating system then it could be called something else.

      Delete