Making your iPhone look like a NMEA GPS receiver with gpsd and WiFi or Bluetooth

TRY IT OUT: If you wish to test the front-end interface on your mobile device, click here and tap 'Find' (not 'Enable'). This is handy if you want to check how well your location service is working, and to get raw lat/lon values! Please note: this will not allow you to send the position updates to your computer - this is purely for your testing only.

UPDATE 2: I have enhanced the web interface further so it supports time-outs and the ability to clear the update backlog that is accumulated following a timeout (see new screenshot below). Thank you to Ryan K for additional testing.
Unfortunately there appears to be a bug that is manifest under Linux: if gpsd is run with no clients connected and updates are sent from the phone, the script will lock up when attempting to send updates to gpsd after an arbitrary period of time. In the short term, this can be fixed by connected to gpsd and sending it a command (e.g. the 'watch' instruction). If a client is connected to it (which should be the case in nearly all circumstances) this doesn't seem to happen. This behaviour has not been seen under Windows.
Preliminary tests on Mac OS X are reported to be successful. If installing gpsd via a package manager, be sure to disable auto-startup (via init.d scripts and/or hotplug, etc) otherwise the gpsd instance launched by the Python script will not be able to bind to the normal listener port and you will be left wondering why you're not getting any data (as you'll be connected to the wrong gpsd instance).

UPDATE 1: I have uploaded a new version of the package (new Python and HTML). The new features include:

  • asynchronous updates (no more locking up Safari)
  • batching of updates (which should solve the Bluetooth problem described below), and
  • filtering of duplicate updates (you will see an asterisk * next to the 'Acc' value, as in the screenshot).

Many thanks to 'gabe' for testing! Also, please be aware that the HTTP server opens files from the current working directory of the Python process. If you're seeing 404s then your current directory is probably not the same as where the script is (you can check the current directory when it is printed out upon starting the Python script).


I was suprised to find that seemingly none of the dedicated GPS iPhone apps could stream your current location to a computer. Therefore I knocked up this simple solution, which uses a Python script to wrap up gpsd and make it think it's connected to a real GPS receiver that outputs NMEA sentences. The receiver is of course the iPhone, which uses Javascript (to retrieve location) and basic AJAX (to send the results to the Python script) all running in Safari. The page is served from Python (running on a computer) via WiFi or a tethered connection (Bluetooth or cable). Please note that Python and gpsd are not running on the iPhone - only the web browser is. This is what you would see in Safari once the page has been loaded from the Python web server:

Tapping 'Find' will send your position to gpsd. Tapping 'Enable' will start the one-second timer and continuously repeat the process, thereby streaming your position to the computer.

The Python script launches gpsd and configures it to listen on a pseudo-terminal for NMEA sentences. It also launches the basic HTTP server that sends the above HTML page to Safari on a GET request, and sends the incoming location data (an AJAX POST) as a properly formatted NMEA sentence to the pseduo-terminal.

I have compiled gpsd for Cygwin, so at the command prompt I see:

$ ./iPhone-gpsd.py
GPSd running
Added /dev/tty0
HTTP Server running...
iPhone - - [27/Jan/2010 12:50:23] "GET / HTTP/1.1" 200 -
iPhone - - [27/Jan/2010 12:50:33] "POST / HTTP/1.1" 200 -
$GPRMC,125030.31,V,1234.567890,S,01234.567890,E,,,270110,,,N*5C

(The latitude and longitude values are not real in this example.)

The Python script tries to emulate the (in-)valid fix flag in the $GPRMC sentence by checking whether the accuracy of the position report from the iPhone is below some threshold (by default it is 100). This can be changed in the Python script on line 290: if (fAcc < 100). If you're not getting any position data from gpsd, remember to check the accuracy of the incoming data.

If you wish to use a custom web server port, simply edit the Python code toward the bottom of the script. Doing so can avoid the need to run Python with administrative privileges (necessary for privileged ports, such as 80).

If you need to compile gpsd under Cygwin, make sure you install gcc, python and automake packages using the Cygwin installer (you may also need libpthread and others - watch the output of the configure script to be sure). Run 'autogen.sh' (the configure script) to generate the Makefiles, then run 'make', and finally 'make install' to finish the job.

A word of caution: Safari's timer does not tick when the phone is locked, so make sure that you temporarily disble your phone's Auto-lock while you run the page. Also, turn down the brightness to conserve battery power if you are operating wirelessly.

Another note: WiFi provides the best performance. If you want to be mobile and don't want to take your access point and power supply with you, then Internet Tethering is the other option. You will need to install a working tethering profile, which will let you connect using the white cable (this gives your phone power as well) or via Bluetooth (great for maximum mobility). The question then becomes: what is the address of the webserver to put into Safari? As iPhones support the Bluetooth PAN profile, they contain and inbuilt DHCP server that hands out addresses to connected clients. Easily enough, you just need to check what IP has been allocated to your computer (in the range 192.168.20.2 and upward). There is one drawback of using tethering though: Safari/the phone spends a long time trying to do the POST, so the update interval is extended to about five seconds. With a bit of extra hackery, position updates could be buffered and sent in a bundle, however this requires any client of gpsd to also buffer whatever data it is geotagging and then align each sample with the incoming gpsd data based on each position report's time value.

AttachmentSize
iPhone-gpsd.zip5.88 KB

Comments

Great stuff

Wonderful as always. Took me a minute to remember where to find the gps.py dependency but a quick apt-get of python-gps downloaded the libraries I needed. Thanks for the time you put into this!

no troughput to gpsd

Hello!

Great work this! I was just trying it to test openCPN, an open source marine charting and navigational application. However, it seems that the script doesn't relay the information to GPSD.. I don't get any feedback except that there's information from the iphone..

Any ideas?

Thanks,
Fabian

RE: no troughput to gpsd

By information from the phone, do you mean NMEA output in the console triggered by the phone? And this is not being picked up by gpsd? Are you sure that the accuracy of the incoming data is sufficient to cause the NMEA sentences to be marked as valid? Otherwise gpsd won't use the information. Also, check out the gpsd diagnostic commands, connect to it manually, and see if it actually thinks there's a device connected via pipe.

RE: no troughput to gpsd

Well, this is the output I get:
Starting GPSD iphone server:
gpsd: can't bind to IPv6 port 2947, Address already in use
gpsd: maybe gpsd is already running!
GPSd running
Added /dev/pts/1
HTTP Server running...
iPhone.local - - [21/Dec/2010 21:40:07] "POST / HTTP/1.1" 200 -

Then, I get the updates:
iPhone.local - - [21/Dec/2010 21:41:28] "POST / HTTP/1.1" 200 -
Received 1 updates:
52.341957442500004,4.970284947500001,121.25553592709136,1292964074267
$GPRMC,214114.26,V,5220.517447,N,458.217097,E,,,211210,,,N*42

(and alike).

When stopping the server:
^C^C received, shutting down server
Removed device
Killed GPSd
Closed HTTP socket

Don't know if I'm doing something wrong, if there's something wrong with my iphone or if the accuracy is wrong.. Anyway, thanks for helping me out :-)

RE: no troughput to gpsd

Thanks for the dump - makes things much easier to diagnose!
The accuracy isn't enough:
"$GPRMC,214114.26,V,5220.517447,N,458.217097,E,,,211210,,,N*42"
The "V" indicates this. If you move outside and get a proper fix, this will turn to "A" and gpsd will start giving you results.
Alternatively you can modify line 292 (the default "100" value) to change the threshold.
Also, have you looked into the "Address already in use" error? Is definitely gpsd bound on IPv4?

Great! Works fine on Mac OS

Great! Works fine on Mac OS X, just had to compile gspd to have the python libraries (in macports there's no gpsd-clients like in debian/ubuntu)

MacPorts - gpsd - Python

there are. They are just in a wrong location: /opt/local/lib/python2.6/site-packages

my jaw just dropped

I can't believe what I'm finding on your site. First, an excellent guide on recovering my apparently dead (R)AID-0

Then, how to use my iPhone as a GPS receiver.

You're the answer to all my queries. Australia should feature you in tourism brochures :)

Webserver

Awesome work.
I'm not a python geek - is there a solution to run this with Apache or IIS Webserver?
Regards

RE: Webserver

Not as yet, although it is certainly feasible to create a pipe between gpsd and either server - would you prefer this for your setup? The Python approach is convenient because it's portable and 'lite'.

Hey has anyone thought of

Hey has anyone thought of just running the bare javascript file (or something of the like) on the iphone itself, then pointing the output to a remote file or something? I would be willing to work on such a thing if i could get a little guidance on it.

Unfortunately not possible

Thanks for your comment 'wasutton3', but since the Javascript must run inside Safari (just like the scenario with any other browser where security is of prime concern) one's options are very limited in terms of how to communicate remotely (i.e. XMLHttpRequest). If legit iPhone app development and distribution was free and open, one could write a proper app to do that and bypass Javascript altogether - remember Javascript is the only scriptable code that runs on the iPhone. The C64 emulator was not accepted for this reason.

thank you!

thank you so much for finally making this!! i've had the same idea for a long time, and periodically search to see if anyone has implemented it, because i don't know a single thing about coding (although in the time i've been waiting i probably could have taught myself). now, i don't have to wait anymore. this is wonderful!

however, i can't get it to work. i managed to sort out the fact that my gpsd binary was in a different directory than the script expected, but now my complete lack of understanding of python or anything else is becoming a problem as i get the error:

File "/Users/abc/Desktop/iPhone-gpsd/iPhone-gpsd-modified_path.py", line 224, in ReadFile
    self.send_error(404, 'File Not Found: %s' % path)
NameError: global name 'path' is not defined

(there's plenty that comes before that but i assume the problem is in that bit)

i've looked on google a bit, but i can't figure this out without actually knowing what i'm doing. care to help?

Bug fix

Thank you testing it out Gabe. Indeed, there was a bug, which I have now fixed. 'path' should have been 'filePath'. Sounds like the script cannot find the requested file (i.e. 404)?

yup, i bet that would do it!

yup, i bet that would do it! you're right about the problem being with calling the html file, becuase there were no issues up until the point the page loads on safari.

404

Hey, would you mind using the contact form to move the discussion to email? Posting is a real pain because there's a bug in the captcha system and (without fiddling) I get a blank page instead of the comment entry form since I'm a registered user - go figure.
Unfortunantely, regarding the bug fix, that doesn't address why in fact the file cannot be found. You need to double check that 'index.html' is in the current directory of the Python process...