3 Provides several GPS interfaces and means to create them.
5 These GPS interfaces are all iterable and return data points.
8 # Copyright (C) 2008 Laurens Van Houtven <lvh at laurensvh.be>
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # NMEA parsing and data point generation
30 # Serial port interfaces
31 import ceserial
.ceserial
as ceserial
37 logging
.debug("'gps' module not found, gpsd support disabled...")
40 # Default CE ports: try 8 and 4 first, those are usually the defaults for BT
41 # and raw serial GPSes, respectively. Also, yes, WinCE ports end in ':'.
42 DEFAULT_CE_PORTS
= ['COM8:', 'COM4:', 'COM1:', 'COM2:', 'COM3:', 'COM5:']
43 # If we're not running on CE, let pyserial figure it out.
44 DEFAULT_GENERIC_PORTS
= range(8)
46 DEFAULT_GPSD_HOSTNAME
= 'localhost'
47 DEFAULT_GPSD_PORT
= 2947
49 def connect(port
= None,
52 sentence_filename
= None):
53 """Tries to connect to a GPS device.
55 Calling this without any arguments tries to automagically detect everything.
57 The platform is guessed based on the output of ``os.name``. On WinCE (aka
58 Windows Mobile), it tries ceserial. In almost all other cases, it tries
59 pyserial. Previously, ``sys.platform`` was used, but modern versions of
60 PythonCE return ``win32``, just like Python on Win2k/XP/Vista would.
62 When this autodetection failed (and no serial port was opened), the client
63 complains loudly, and we return a fake GPS with replayed sentences. Please
64 note that the fact that this did not happen does not imply success -- just
65 because you can open a port doesn't mean there's anything on it.
67 The port usually refers to a serial port. Depending on the platform, this
68 either looks a bit like "COM0" (Win32), "COM0:" (WinCE), or "/dev/ttyS0"
71 gpsd support requires explicitly setting the platform to ``'gpsd'``. The
72 port then refers to a TCP port on which gpsd is listening (the default is
73 is 2947). The hostname argument is the hostname on which gpsd is listening.
75 The hostname argument is not used except for gpsd. This is because serial
76 ports can generally only be accessed locally. Remote serial port access,
77 although possible, is not supported, unless of course whatever it is you're
78 using manages to disguise the remote port as a local one sufficiently well.
80 logging
.debug('Trying to connect to a GPS (platform: %s)...' % platform
)
82 if platform
== 'fake':
83 return _fake_nmea_gps(sentence_filename
)
85 if platform
== 'gpsd':
86 return connect_to_gpsd(host
, port
)
89 # sys.platform on recent versions of PythonCE is 'win32', just like on
90 # normal Windows boxes. os.name returns 'ce' on WinCE.
91 platform
= os
.name
.lower()
92 logging
.debug("Detected platform %s..." % platform
)
94 serial_port
= connect_to_serial(port
, platform
)
96 if serial_port
is not None:
97 return nmeaGPS(serial_port
)
99 raise RuntimeError, "Host doesn't seem to have any serial ports?!"
101 def connect_to_serial(port
= None, platform
= 'ce'):
102 """Attempts to connect to a serial GPS device. Returns a serial port.
104 When forced to autodetect, the first serial port that can be opened will be
105 picked. This might not be the wrong device -- the only way to fix that is
106 to make sure the default ports are *likely* to be GPSes.
108 Please note that a port might (usually: will) open even though there is no
109 device connected to it.
112 logging
.debug("Attempting to connect to a GPS device...")
115 logging
.debug('Using common defaults for WinCE...')
116 potential_ports
= DEFAULT_CE_PORTS
118 logging
.debug("Using common defaults for pyserial...")
119 potential_ports
= DEFAULT_GENERIC_PORTS
121 logging
.debug("Port specified: %s." % port
)
123 if platform
== 'ce' and port
[-1] is not ':':
124 # WinCE does not agree with 99% of its users on how to name ports
125 port
= ''.join([port
, ':'])
127 potential_ports
= [port
]
129 for portname
in potential_ports
:
130 port
= _try_serial_port(portname
, platform
)
134 logging
.error("Exhausted potential serial ports...")
137 def _try_serial_port(port
, platform
):
138 """ Tries to connect to a GPS device on a serial given port. """
140 logging
.debug("Attempting to open serial port '%s'" % port
)
142 gps
= ceserial
.CESerial(port
= port
)
144 gps
= serial
.Serial(port
)
149 logging
.info("Couldn't open serial port '%s'." % port
)
152 logging
.debug("Opening serial port %s successful..." )
153 gps
.sentence_type
= 'nmea' # Assume NMEA.
157 def _fake_nmea_gps(sentence_filename
= None):
158 """Returns a fake GPS device (NMEA sentence replayer)."""
159 if sentence_filename
is None:
160 ownpath
= os
.path
.dirname(os
.path
.abspath(__file__
))
161 filename
= os
.path
.join(ownpath
, 'sampletrip')
163 filename
= os
.path
.abspath(sentence_filename
)
165 filehandle
= open(filename
, 'r')
167 logging
.debug("Returning a fake GPS...")
168 return nmeaGPS(filehandle
)
170 def connect_to_gpsd(host
= None, port
= None):
171 """ Connects to a gpsd daemon running on the specified hostname and port. """
173 return gpsdGPS(host
, port
)
175 class nmeaGPS(object):
177 An interface for any source of NMEA sentences.
179 The first argument must be an iterable object that returns NMEA sentences.
180 This can be a list (for a fake GPS), a serial device (a real GPS)...
182 # TODO:Implement sentence logging in nmeaGPS
183 def __init__(self
, sentence_iterable
):
184 self
.sentences
= sentence_iterable
187 for sentence
in self
.sentences
:
188 yield deltas
.Point(nmea
.parse(sentence
))
191 """ Fake close() method."""
194 class gpsdGPS(object):
196 An interface to the gpsd_ daemon.
198 .. _gpsd: http://gpsd.berlios.de/
200 def __init__(self
, host
= None, port
= None):
202 self
.port
= DEFAULT_GPSD_PORT
204 self
.host
= DEFAULT_GPSD_HOSTNAME
206 self
.session
= gps
.gps(host
, port
)
209 """ Gets the next data point from the gpsd daemon. """
211 self
.session
.query('adyos')
214 'latitude': float(self
.session
.fix
.latitude
),
215 'longitude': float(self
.session
.fix
.longitude
),
216 'altitude': float(self
.session
.altitude
),
217 'velocity': float(self
.session
.speed
),
218 'timestamp': int(self
.session
.time
),
221 yield deltas
.Point(data
)