Tuesday, February 24, 2009

You Are Here on a Google Map

This post is obsolete and has been superseded


The following python 2.x script shows your current (GPS-enabled) location on a Google Map. A useful learning experience:

#!/usr/bin/env python

"""This is a python 2.5 script that plot's a GPS receiver's location on the
Google Maps website.

In order to work, you need a network connection, an attached GPS receiver, and
the GPS daemon (gpsd).
"""
import os
#import subprocess and SU
import gps
import dbus
import sys


def test( ):
    """ Step 1: Test for the existence of a running gpsd, test for the existence of an open network connection, and test for a firefox process.
    If any fail, give an error message, don't try to recover. FUTURE: Could also use DBus to test for firefox and gpsd."""

    process_list = os.popen('ps -e')    # os.popen is deprecated in favort of subprocess.Popen
    #process_list = SU.Popen(['ps','e'], stdout=SU.PIPE).stdout.read()  
    gpsd_existence_flag = 0
    firefox_existence_flag = 0
    for line in process_list.readlines():
        if line.count('gpsd') > 0: gpsd_existence_flag = 1
        if line.count('firefox') > 0: firefox_existence_flag = 1

    if not gpsd_existence_flag:
        print ("gpsd is not running. Use 'gpsd -b /dev/ttyUSB0' to start it, and then try again.")
        sys.exit()       
    else: print ('Checking...found gpsd')

    if not firefox_existence_flag:
        print ("firefox is not running. Please start it and try again.")
        sys.exit()
    else: print ('Checking...found firefox')

    bus = dbus.SystemBus()
    nm_item = ('org.freedesktop.NetworkManager')  # This string gets used a lot
    nm_path = ('/org/freedesktop/NetworkManager')
    nm_device = ('org.freedesktop.NetworkManager.Device')

    list_of_interface_paths = dbus.Interface(bus.get_object(nm_item, nm_path), nm_device).getDevices()

    found_network_flag = 0
    for interface_path in list_of_interface_paths:
        one_interface = dbus.Interface(bus.get_object(nm_item, interface_path), nm_device)
        if one_interface.getLinkActive():   # True if there is an active network on this interface
            if one_interface.getType() == 2: # 0 unknown, 1 wired, 2 wireless
                print('Checking...found the wireless network') 
                found_network_flag = 1
            elif one_interface.getType() == 1: 
                print('Checking...found the wired network')
                found_network_flag = 1
                
    if found_network_flag: return
    else:
        print ("cannot find a network connection. Please connect and try again.")
        sys.exit()    


def get_position_fix( ):
    """Step 2: Get a position fix from gpsd."""
    session = gps.gps('localhost','2947')  # Open a connection to gpsd
    session.query('p')                     # Get the location fix 
    lat = session.fix.latitude
    lon = session.fix.longitude
    print ('Location is ' + str(lat) + ' latitude and ' + str(lon) + ' longitude.')
    return (lat, lon)


def show_map(lat_lon_tuple):
    """Step 3: Submit the position fix to Google Maps. Note that the parentheses '()' in the URL must be escaped '\' to work.
    Sample URL format: http://maps.google.com/maps?q=37.771008,+-122.41175+(You+can+insert+your+text+here)&iwloc=A&hl=en"""
    url_string = ('http://maps.google.com/maps?q=' + str(lat_lon_tuple[0]) + ',+' + str(lat_lon_tuple[1]) + '+\(You+Are+Here\)&iwloc=A&hl=en')
    os.popen('firefox ' + url_string)
    return

# Run this script as a standalone program
if __name__ == "__main__" :
    test()
    location = get_position_fix()
    show_map(location)

Monday, February 23, 2009

GPS and Xubuntu 8.04

This post is obsolete and has been superseded


I'm experimenting with USB GPS receiver (dongle). It's a Canmore GT-730F that I received in January 2009. Here's what I've learned so far.


Manually getting data using the command line (source):

  1. Check dmesg, the kernel log, to find out where the device has been mounted. In my case, it mounts reliably to /dev/ttyUSB0. If it doesn't mount, try the command sudo modprobe pl2303 to load the correct USB driver.
  2. Set the data output rate to 4800 baud using the stty command: stty 4800 > /dev/ttyUSB0
  3. Read the data stream using the cat command: cat /dev/ttyUSB0
  4. You should see a set of data scroll down the screen. Use CTRL+C to end the test.


The Linux GPS Daemon (gpsd) is the central clearinghouse for receiving GPS data from the receiver, buffering it, and forwarding it to the applications that want it. gpsd has features to broadcast to dbus (system bus), update ntpd, and respond to a multitude of specific queries from clients. References: Project home page, gpsd man page, and a great example program

$sudo apt-get install gpsd gpsd-clients  # Installs the daemon (gpsd) and test set (gpsd-clients) packages
$gpsd -b /dev/ttyUSB0                    # Start gpsd, telling it where to find the receiver
$cgps                                    # Current satellite data - great way to test that the receiver and gpsd are working


gpsfake is a gpsd simulator. It tricks gpsd into reading from a logfile instead of a real GPS device. Very handy for testing without actually using a GPS dongle. It is included with the gpsd package, and has a man page for additional reference. To make a logfile, and then to use gpsfake:

$cat /dev/ttyUSB0 > path/to/testlog  # Create the log file. Use CTRL+C to end the capture.
$gpsfake path/to/testlog             # Run gpsd simulator (not a daemon - it will occupy the terminal)


Python interface to gpsd (python-gps) is a programming tool to build your own gps-aware application.

>>>import gps                             # Load the module
>>>session = gps.gps('localhost','2947')  # Open a connection to gpsd
>>>session.query('o')                     # See man gpsd(8) for the list of commands
>>>print session.fix.latitude             # Query responses are attributes of the session
>>>dir(session)                           # To see the possible responses
>>>del session                            # Close the connection to gpsd


In this case, it seems that I need a periodic session.query('p'), which just gives lat/lon and timestamp.


Time might be an issue, since the system and the GPS may think the time is different. To see if it's an issue, compare them using the python script below. In my tests, they vary from 0.08 to 1.3 seconds apart, not enough to worry about. GPS timestamps use GMT, not localtime.

#!/usr/bin/env python
import calendar, time, gps
system_time = calendar.timegm(time.gmtime())  # System time (in seconds)
session = gps.gps('localhost','2947')         # Open a connection to gpsd
session.query('p')                            # See man gpsd(8) for the list of commands
gps_time = session.timings.c_recv_time        # GPS time (in seconds)
print ('The time difference is ' + str(system_time - gps_time) + ' seconds.')


MGRS (military map coordinates) conversion to/from latitude and longitude is not currently available in Ubuntu...that I can find. The dongle documentation doesn't mention MGRS at all. An online converter is available. The proj package looks promising, but I haven't figured it out yet. Perhaps Lat/Lon -> UTM -> MGRS?


DBus access appears to be undocumented...but there are tantalizing hints on Google that examples are out there. I can listen to dBus using cgps to make traffic, then dbus-monitor --system to see it.


The best storage format for tracklogs, routes, and waypoints seems to be GPX format, since it's easy to understand and cgpxlogger, included with gpsd, will create an XML track in GPX 1.1 format. Google's KML is more powerful, but also much more complex. GPSbabel universal data translator is a command-line application that translates one file type to another, and does convert GPX <-> KML.

$cgpxlogger -i 30 > path/to/logfile         # Save data every 30 seconds to an XML file
$gpsbabel -i gpx -f path/to/gpx_file -x duplicate -o kml -F path/to/kml_file
$#gpsbabel [options] -i INTYPE -f INFILE -x FILTER -o OUTTYPE -F OUTFILE


GPSdrive navigation system looks cool, but I couldn't get maps to load, so it's utility was limited. However, it seems that online plotting of tracklogs, routes, and waypoints is possible on Google Maps (and Yahoo Maps, and others). One example is the cool GPS Visualizer.


Gypsy is an alternative daemon, but not currently in Debian or Ubuntu, so I haven't tried it. Last release 0.6 in March 2008.


GPSman device manager is an app I know nothing about. I couldn't get it to work, so I removed it. The dongle seems small enough and simple enough that it may not need to be 'managed' at all.

Friday, February 20, 2009

Using Python to reformat the xml within .odt files

A quick python 2,x script to copy, unzip, and uniformly reformat the XML of an .odt file. It adds indentations and line breaks. Useful to debug my invoicing script, which muddles with the xml files, by making the files diff-able and easier to read and easier to search.

#!/usr/bin/env python
import os
import xml.etree.ElementTree as ET
odt_path_and_file = 'path/to/file.odt'

# This function was copied from http://effbot.org/zone/element-lib.htm
def indent(elem, level=0):
    i = "\n" + level*"  "
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for elem in elem:
            indent(elem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i

odt_filename = odt_path_and_file.split('/')[-1]
folder_name = ('Desktop/' + odt_path_and_file.split('/')[-1].rstrip('.odt'))
os.popen('rm -r ' + folder_name) #Delete any old working files
os.popen('mkdir ' + folder_name)
os.popen('cp ' + odt_path_and_file + ' ' + folder_name)
os.popen('unzip ' + folder_name + '/' + odt_filename + ' -d ' + folder_name)
reply = os.popen('ls ' + folder_name)
file_list = [filename.rstrip('\n') for filename in reply.readlines() if filename.count('.xml') > 0]
for file in file_list:
    print ('Parsing ' + folder_name + '/' + file)
    tree = ET.parse(folder_name + '/' + file)
    indent(tree.getroot())
    tree.write(folder_name + '/' + file)
    print ('Completed ' + file)

Using Python to compare .odt files

A quick python script to copy, unzip, and reformat an .odt file. It adds indentations and line breaks. Useful to debug my invoicing script, which muddles with the xml files, by making the files diff-able and easier to read and easier to search.

#!/usr/bin/env python
import os
import xml.etree.ElementTree as ET
odt_path_and_file = 'path/to/file.odt'

# This function was copied from http://effbot.org/zone/element-lib.htm
def indent(elem, level=0):
    i = "\n" + level*"  "
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "  "
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for elem in elem:
            indent(elem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i

odt_filename = odt_path_and_file.split('/')[-1]
folder_name = ('Desktop/' + odt_path_and_file.split('/')[-1].rstrip('.odt'))
os.popen('rm -r ' + folder_name) #Delete any old working files
os.popen('mkdir ' + folder_name)
os.popen('cp ' + odt_path_and_file + ' ' + folder_name)
os.popen('unzip ' + folder_name + '/' + odt_filename + ' -d ' + folder_name)
reply = os.popen('ls ' + folder_name)
file_list = [filename.rstrip('\n') for filename in reply.readlines() if filename.count('.xml') > 0]
for file in file_list:
    print ('Parsing ' + folder_name + '/' + file)
    tree = ET.parse(folder_name + '/' + file)
    indent(tree.getroot())
    tree.write(folder_name + '/' + file)
    print ('Completed ' + file)

Tuesday, February 10, 2009

Bean on Mac OS 10.5

Our old copy of Appleworks from a previous Mac is sputtering and becoming unreliable in 10.5, so I'm looking for a replacement. OpenOffice and AbiWord are cluttered and unpleasant (spouse dislikes them), but Bean seems pleasant enough to fit our needs. Happily, it reads and writes simple documents to .rtf, .doc, and .odt.