Sunday, November 27, 2011

GPS dongle, gpsd, and Ubuntu 11.10

This post is an update to my original experiment #1 and experiment #2 with a USB GPS dongle two years ago.

Install the hardware: Plug it in, of course.
  • Check the kernel message log to discover the GPS dongle location in the filesystem:
    $ dmesg | grep tty
    [1769190.919411] usb 2-1: pl2303 converter now attached to ttyUSB0
  • Test for signal with the following command:
    $ cat /dev/ttyUSB0 
    5�y�220,N,08754.5061,W,000.0,316.5,271111,,,A*71
    $GPVTG,316.5,T,,M,000.0,N,000.0,K,A*0C
    $GPGGA,173905.000,4259.7220,N,08754.5060,W,1,08,1.0,190.0,M,-33.8,M,,0000*63
    $GPGSA,A,3,12,04,10,23,02,05,17,25,,,,,1.7,1.0,1.4*31
    $GPRMC,173905.000,A,4259.7220,N,08754.5060,W,000.0,316.5,271111,,,A*71
    
    Use CTRL+C to end the signal check.
    If you get a bunch of zero-data, then try moving closer to a window, since GPS signals are line-of-sight. Or try a USB hub or extension cable, to reduce interference from your computer.

Install the gps daemon so you can use gps data.
  • sudo apt-get install gpsd gpsd-clients
  • Normally, udev will start gpsd automatically when a USB GPS device is inserted. If it fails to start after 5-10 seconds, you can start it manually:
    gpsd /dev/ttyUSB0  #You must tell gpsd where to look for the dongle
  • Test the GPS and gpsd output using the gpsmon and cgps terminal programs, or the xgps program (all included in the gpsd-clients package)
  • Capture data for recycling (optional - handy for development). gpsfake will simulate gpsd output
    $cat /dev/ttyUSB0 > path/to/datalog  # Create data file. Use CTRL+C to end the capture.
    $gpsfake path/to/datalog             # Run gpsd simulator (not a daemon - it will occupy the terminal)

Command-line tools to get GPS data
  • Use the gpspipe command to get gpsd data:
    $ gpspipe -w -n 5
    netlib_connectsock() returns socket on fd 3
    {"class":"VERSION","release":"2.95","rev":"2011-07-27T11:20:24","proto_major":3,"proto_minor":3}
    {"class":"DEVICES","devices":[{"class":"DEVICE","path":"/dev/ttyUSB0"}]}
    {"class":"WATCH","enable":true,"json":true,"nmea":false,"raw":0,"scaled":false,"timing":false}
    {"class":"TPV","tag":"RMC","device":"/dev/ttyUSB0","time":1322418390.001,"ept":0.005,"lat":42.995410000,"lon":-87.908430000,"alt":195.400,"epx":11.739,"epy":17.294,"epv":36.800,"track":342.2000,"speed":0.000,"climb":0.000,"eps":34.59,"mode":3}
    {"class":"SKY","tag":"GSV","device":"/dev/ttyUSB0","xdop":0.66,"ydop":1.15,"vdop":1.46,"tdop":1.01,"hdop":1.32,"gdop":2.22,"pdop":1.97,"satellites":[{"PRN":2,"el":77,"az":360,"ss":26,"used":true},{"PRN":10,"el":58,"az":72,"ss":32,"used":true},{"PRN":12,"el":51,"az":247,"ss":34,"used":true},{"PRN":5,"el":45,"az":192,"ss":21,"used":true},{"PRN":4,"el":40,"az":65,"ss":20,"used":true},{"PRN":25,"el":39,"az":292,"ss":21,"used":true},{"PRN":13,"el":15,"az":60,"ss":18,"used":true},{"PRN":24,"el":14,"az":258,"ss":0,"used":false},{"PRN":29,"el":12,"az":304,"ss":23,"used":false},{"PRN":17,"el":8,"az":121,"ss":17,"used":true},{"PRN":23,"el":7,"az":35,"ss":10,"used":false},{"PRN":31,"el":0,"az":334,"ss":0,"used":false}]}
  • Let's refine the command a bit, limit to the first "TPV" (time, position, velocity) sentence:
    $ gpspipe -w -n 5 | grep -m 1 TPV
    netlib_connectsock() returns socket on fd 3
    {"class":"TPV","tag":"RMC","device":"/dev/ttyUSB0","time":1322418481.001,"ept":0.005,"lat":42.995406667,"lon":-87.908428333,"alt":195.300,"epx":10.003,"epy":17.383,"epv":36.800,"track":342.2000,"speed":0.000,"climb":0.000,"eps":34.77,"mode":3}
  • We can use basic shell commands to extract single variables from this sentence:
    tpv=$(gpspipe -w -n 5 | grep -m 1 TPV | cut -d, -f4,6-8,13)
    seconds=$(echo $tpv | cut -d, -f1 | cut -d: -f2)
    latitude=$(echo $tpv | cut -d, -f2 | cut -d: -f2)
    longitude=$(echo $tpv | cut -d, -f3 | cut -d: -f2)
    altitude=$(echo $tpv | cut -d, -f4 | cut -d: -f2)
    speed=$(echo $tpv | cut -d, -f5 | cut -d: -f2)
  • For fun, let's compare GPS time with system time using shell commands:
    date -u +%s   # System utc time in seconds
    gpspipe -w -n 5 | grep -m 1 TPV | cut -d, -f4 | cut -d: -f2   # GPS utc time in seconds
    
    # Let's compare them side-by side
    $ echo System: $(date -u +%s) , GPS: $(gpspipe -w -n 5 | grep -m 1 TPV | cut -d, -f4 | cut -d: -f2) 
    netlib_connectsock() returns socket on fd 3
    System: 1322419689 , GPS: 1322419689.000
    # Our sytem time is within one second of GPS time. That's good!
  • For fun, let's use shell script to post the GPS location to Google Maps:
    The format is simple enough: http://maps.google.com/?ll=40.480381,-92.373047&z=16 will return a valid map. So that's three variables: Latitude, longitude, and zoom Level.
    tpv=$(gpspipe -w -n 5 | grep -m 1 TPV | cut -d, -f4,6-8,13)
    latitude=$(echo $tpv | cut -d, -f2 | cut -d: -f2)
    longitude=$(echo $tpv | cut -d, -f3 | cut -d: -f2)
    zoom=20
    map_url="http://maps.google.com/?ll=${latitude},${longitude}&z=${zoom}"
    firefox $map_url

Python tools to get GPS data
  • Python bindings to gpsd are provided by the python-gps package.
    sudo apt-get install python-gps
  • The python tools have changed a *lot* in two years. The old methods don't work anymore. No Python 3 version is available yet. There's a decent example here, but I couldn't get it to work.

Conversion between Lat/Lon and MGRS/UTM and other systems: gpsd doesn't do this, though apparently the geographiclib-tools package does. And so does this website.

1 comment:

Marco Picanço said...

Thanks for the recipe. I also do some experiments my self with some xexun gpstrackers and Google earth.
Regards,
Marco