RSS feed

Linkedin Profile

Tags:
programming
seattle
things that bug me
wall art

Posts by month: 10/08 (2)
08/08 (1)
06/08 (2)
05/08 (1)
03/08 (3)
02/08 (1)
01/08 (2)
12/07 (2)
11/07 (1)
07/07 (1)
05/07 (2)
02/07 (1)
01/07 (1)
12/06 (1)
11/06 (1)
10/06 (1)
08/06 (1)
07/06 (1)
06/06 (2)
05/06 (1)
04/06 (2)
02/06 (1)
01/06 (2)
12/05 (3)
11/05 (2)
09/05 (5)
08/05 (5)
07/05 (7)
06/05 (3)
05/05 (6)
04/05 (8)
03/05 (7)
02/05 (7)
01/05 (6)
12/04 (2)
11/04 (3)
10/04 (5)
09/04 (3)
08/04 (5)
07/04 (5)
06/04 (4)
05/04 (4)
04/04 (9)
03/04 (4)
02/04 (3)
01/04 (5)
12/03 (1)
11/03 (14)
10/03 (8)


GPS Code
2008-06-25

As promised, here is source code for reading GPS data from the Streets and Trips USB GPS on OS X. This is the most C code I have written in a while, so don't go putting it into production!

Here's how it works:

nmea.h/.c - Structs representing nmea data, and code for reading data from the serial port, and parsing this out of a comma separated string. I have restricted myself to only parsing bits of the gga sentence, but it would be straightforward to extend this to other data and sentences.

serial.h/.c - Code for opening and closing the serial port.

main.c - A small main method that reads the nmea data and prints the lat, long and altitude to the console.

If you want to try it out, don't forget to install the serial port driver as described in my previous post. Have fun with it!

Tags: programming


GPS on Leopard
2008-06-09

A while back I bought Microsoft Streets and Trips 2007 from the company store, and got a nice, small USB GPS with it. I messed around programming the GPS on Windows (post) and hooked it into my blog, but then I moved over to a Mac running Leopard as my daily machine and lost my neat GPS recording capability. No more! On Sunday I hacked together some code to pull data from the GPS on OS X.

nmea print out.

I found a few posts from people trying to do the same thing, so I am going to outline the steps involved now, and show some code in a later post.

1. USB to Serial - The GPS device uses the RS232 serial standard over USB. This won't appear as a serial device without the right driver, so download the BF-810 USB to Serial adapter from here and install it.

2. Vendor and Product ID - The driver installed in step 1 needs some tweaks before it will work, and to make those tweaks you need to know two numbers: The vendor and product IDs. Plug in the GPS and fire up IORegistryExplorer, and then look under IOUSB group (command-6) for the GPS. It will be the entry with USB Vendor Name = Prolific Technology Inc. If you are lazy like me you can also find it by yanking the GPS out of the machine and watching it go red in the explorer. Note that the values here are in hex, so it is a great opportunity to dust off your hex to decimal skills.

3. Edit kext - Plug the vendor and product IDs for your device into the kernel extension Info.plist file /System/Library/Extensions/ProlificUsbSerial.kext/Contents/Info.plist. This is just a plain old xml file, so sudo fire up your editor of choice and replace whatever values are there under idProduct and idVendor with your own.

4. Reload kext - With our product and vendor IDs in place, it is now time to reload the driver. First up, run "sudo kextunload -b com.prolific.driver.PL2303". It is ok if this unload command fails. Now run "sudo kextload -b com.prolific.driver.PL2303". Provided the device is plugged in, you should now be able to see a device file called cu.usbserial in the /dev directory on your file system (ls /dev/cu*).

5. Time to code - Apple has a useful serial programming in C tutorial here. The demo accesses a modem, but the code only needs a few tweaks to pull NMEA data off the GPS. I started by stealing swathes of the demo and hacking on them to re-target it at the GPS. The important changes are:

5.1 Discovery - There is no need to discover the device dynamically, just hard code the device path and get rid of a couple of hundred lines of code right away:

	char* gpsPath = "/dev/cu.usbserial"; 
	fileDescriptor = OpenSerialPort(gpsPath);

5.2 Config - The device is configured using the termios structure, and you'll need this to get the baud, parity, stop bits and word size right:

	cfsetspeed(&options, B4800); 
	options.c_cflag &= ~PARENB;
	options.c_cflag &= ~CSTOPB;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8;

5.3 Reading data and NMEA parsing - Data isn't read back from the device in a predictable sequence, so you will need to scan the input as it is read and work out when the sequence starts. Luckily the NMEA data structure is simple to parse: every line starts with a $, holds some comma separated values, and ends with a checksum. Parse away!

Tags: programming


KML
2008-03-12

I have done a little GPS hacking in the last year and am part-time interested in location, so as part of my Ruby blog re-write I implemented a KML feed. This lets me tag my blog posts with location coordinates. If this excites you then read on! If you aren't sure if you should be excited or not, then reading this introduction to KML will help you decide.

You can see my feed here here. Now check out this map overlay here. Sweet!

Here are my three step guide to rolling your own KML feed in Rails. I did this as part of my own blogging software, but these steps are generally applicable.

First up, get some location data. I did this by putting an extra string field onto my blog entry. Then fill this field with data of the format "{longitude}, {latitude}".

Next, write some view code to spit out data as KML. My .rhtml looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.1">
	<Document>
<% for thing in @things 
	if thing and 
		thing.location and 
		thing.location.length > 0 then
%>
		<Placemark>
			<name><%=thing.title -%></name>
			<description>
			<![CDATA[
				<%= thing.content%>
			]]>
			</description>
			<Point>
				<coordinates>
					<%= thing.location -%>
				</coordinates>
			</Point>
		</Placemark>
<%	end 
end %>
	</Document>
</kml>

Lastly, set the HTTP content type header like so:

response.headers["type"] = "application/vnd.google-earth.kml-xml"

Easy!

Tags: programming


Rails 2
2007-12-05

I started playing around with Ruby on Rails in late June. I know this because I recorded the date June 30th at the top of my notes titled "Ruby stuff". The "Ruby stuff" page contains the following:

Starting the web server:
	ruby script/server

Creating a new controller:
	ruby script/generate controller controller_name

To run ruby in console mode, run
	ruby script/console {production|development}
	Development is the default if no argument is offered

That's it. I like taking notes when I use some new language or technology so I can come back months later and work out how the heck I ever got whatever-ing to work. In this case, I stopped taking notes after starting console mode. So Ruby on Rails is very easy to program: script/generate creates files ready to be filled up with Ruby; MVC is baked in and the M bit is automated down to the database; Rails provides convenient methods for accessing the model called finders that let me create a type Thing with a name property and then call Thing.find_by_name(…) . And because this is just recreational programming I don't care about perf, I don't even need to know the database is there, or what that dynamic method cost me.

So I went off and wrote some new blog software, and even put in some new features: tags, multiple users, a kml feed (which I will post about later), and an RSS migrator. This was precipitated, btw, by Dominic quitting his .Net hosting.

Back to my notes. The next page is title "Setting up rails app on dreamhost". There is no actual blood, sweat or tear stains on this page, but only because I wiped the monitor down. The page contains the following:

set up the rails app with the command "rails directoryname"
sync the app from svn
change the home directory to /public, wait 10 mins
stand on one foot
edit the fcgi file
kill any dispatch.fcgi processes
stand on the other foot
put the app into production by editing the config file
kill any dispatch.fcgi processes
…

OK so it wasn't quite like that. But it was close. And it took a long time. Apparently Dominic has capistrano deployments working. I guess I need to look into those!

Tags: programming


Webcam
2007-05-30

I bought a cheap web cam at the supermarket on the weekend. After plugging it in I wondered how hard it would be to program. Particularly, I wondered if I could programmatically take pictures with the web cam and send the pictures up to my weblog along with the GPS information that already goes there. I figure so long as I have a database of everywhere I have been, I may as well have a database of what those places look like. And it was a long weekend, and it seemed like a good project for a long weekend...

The easiest way I found to automatically capture images on Windows is to use the API in avicap.dll. capGetDriverDescriptionA lets me enumerate attached devices and return a device name, which I can then pass to capCreateCaptureWindowA to creates a capture window, which I can then hook up to the web camera using a series of windows messages (sample code here and here. Not elegant (why do I need to create a window to capture some video?), but it works.

web cam photo

That's the view from my couch, btw. For the moment I'm storing these in a DB via a SOAP service, but I wonder if there is a nice GPS + photos + google maps mashup in the offing...

Tags: programming


Location
2007-05-07

A while back I posted details of a little GPS program I built. The program is a Windows service that periodically takes my location (altitude, latitude and longitude) from a USB GPS device, and next time I am online reverse geo-codes the location and posts the result to my weblog via a web service.

After a while I noticed the location information was a little bit off. My initial reaction was that some sort of conspiracy was afoot, since Microsoft Street and Trips was getting the location perfectly well. Of course, it turned out to be user error. The NMEA data coming out of the device looks like this:

$GPRMC,215238.000,A,4737.2885,N,12219.4668,W,0.19,350.69,270107,,,A*74

I had taken this to be 47.37 etc degrees. It is actually 47 degrees and 37.2885 minutes. Solved!

Tags: programming


GPS
2007-01-28

Last week, in a bout of gadget lust, I bought Streets & Trips 2007, which includes a little USB GPS locator.

GPS device

Not long after plugging it in I started to wonder if it was possible to pull GPS data directly off the device myself (rather than using Street and Trips), and so a hacking project was born. I decided to get my GPS location, somehow reverse geo-code it and publish my location to my weblog using a web service. Since my GPS device will only sometimes be plugged in, I decided to write a service that would pull my GPS location every few minutes and save it to disk. Likewise since my laptop will not always be connected to the internet, the same service would periodically try the geo-coding and publishing steps.

Getting data from the device was remarkably simple. A few internet searches revealed that GPS devices use the NMEA protocol, and that the device can be treated as a serial port, allowing me to start pulling data with a few lines of C# code like this:

	port = new SerialPort();
	port.PortName = "COM4";
	port.BaudRate = 4800;
	port.Parity = Parity.None;
	port.StopBits = StopBits.One;
	port.DataBits = 8;
	port.Open(); 
	int bufferSize = 256;
	byte[] data = new byte[bufferSize];
	port.Read(data, 0, bufferSize);

The NMEA data that is read into the buffer is an asci-encoded string formatted like so:

55 $GPGSA,A,3,16,07,06,21,10,18,,,,,,,2.4,1.3,2.0*39 $GPRMC,215238.000,A,4737.2885,N,12219.4668,W,0.19,350.69,270107,,,A*74 $GPGGA,215239.000,4737.2884,N,12219.4668,W,1,06,1.3,95.1,M,-17.3,M,,0000*55 $GPGSA,A,3,16,07,06,21,10,18,,,,,,,2.4,1.3,2.0*39 $

The GPRMC and GPGGA sections both contain latitude (47.37 N) and longitude (122.19 W), and the GPGGA also has altitude (95.1 M). Rather than worry about parsing this myself, I purloined some NMEA parsing code from this excellent sample by Scott Hanselman on MSDN's Coding4Fun.

With my coordinates retrieved, my attention now turned to reverse geo-coding them to find a place name. I had hoped that local.live.com would have an API, but I settled for the REST service provided by www.geonames.org. It accepts latitude and longitude as part of a HTTP get request like http://ws.geonames.org/findNearbyPlaceName?lat=47.3&lng=9, and returns location data as XML:

Location data

Now with coordinates and a place name, all that remained was to push this up to a SOAP service on my own blog that saves the location and time to a local database, and then add the most recent entry to the sidebar of my blog. And now you know where I am.

Tags: programming

Back to weblog