Package killerbee :: Package openear :: Package gps :: Module gps'
[hide private]
[frames] | no frames]

Source Code for Module killerbee.openear.gps.gps'

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # This file is Copyright (c) 2010 by the GPSD project 
  5  # BSD terms apply: see the file COPYING in the distribution root for details. 
  6  # 
  7  # gps.py -- Python interface to GPSD. 
  8  # 
  9  # This interface has a lot of historical cruft in it related to old 
 10  # protocol, and was modeled on the C interface. It won't be thrown 
 11  # away, but it's likely to be deprecated in favor of something more 
 12  # Pythonic. 
 13  # 
 14  # The JSON parts of this (which will be reused by any new interface) 
 15  # now live in a different module. 
 16  # 
 17  import time 
 18  from client import * 
 19   
 20  NaN = float('nan') 
21 -def isnan(x): return str(x) == 'nan'
22 23 # Don't hand-hack this list, it's generated. 24 ONLINE_SET = 0x00000001 25 TIME_SET = 0x00000002 26 TIMERR_SET = 0x00000004 27 LATLON_SET = 0x00000008 28 ALTITUDE_SET = 0x00000010 29 SPEED_SET = 0x00000020 30 TRACK_SET = 0x00000040 31 CLIMB_SET = 0x00000080 32 STATUS_SET = 0x00000100 33 MODE_SET = 0x00000200 34 DOP_SET = 0x00000400 35 VERSION_SET = 0x00000800 36 HERR_SET = 0x00001000 37 VERR_SET = 0x00002000 38 ATTITUDE_SET = 0x00004000 39 POLICY_SET = 0x00008000 40 SATELLITE_SET = 0x00010000 41 RAW_SET = 0x00020000 42 USED_SET = 0x00040000 43 SPEEDERR_SET = 0x00080000 44 TRACKERR_SET = 0x00100000 45 CLIMBERR_SET = 0x00200000 46 DEVICE_SET = 0x00400000 47 DEVICELIST_SET = 0x00800000 48 DEVICEID_SET = 0x01000000 49 ERROR_SET = 0x02000000 50 RTCM2_SET = 0x04000000 51 RTCM3_SET = 0x08000000 52 AIS_SET = 0x10000000 53 PACKET_SET = 0x20000000 54 AUXDATA_SET = 0x80000000 55 UNION_SET = (RTCM2_SET|RTCM3_SET|AIS_SET|VERSION_SET|DEVICELIST_SET|ERROR_SET) 56 57 STATUS_NO_FIX = 0 58 STATUS_FIX = 1 59 STATUS_DGPS_FIX = 2 60 MODE_NO_FIX = 1 61 MODE_2D = 2 62 MODE_3D = 3 63 MAXCHANNELS = 20 64 SIGNAL_STRENGTH_UNKNOWN = NaN 65 66 WATCH_NEWSTYLE = 0x00080 67 WATCH_OLDSTYLE = 0x10000 68
69 -class gpsfix:
70 - def __init__(self):
71 self.mode = MODE_NO_FIX 72 self.time = NaN 73 self.ept = NaN 74 self.latitude = self.longitude = 0.0 75 self.epx = NaN 76 self.epy = NaN 77 self.altitude = NaN # Meters 78 self.epv = NaN 79 self.track = NaN # Degrees from true north 80 self.speed = NaN # Knots 81 self.climb = NaN # Meters per second 82 self.epd = NaN 83 self.eps = NaN 84 self.epc = NaN
85
86 -class gpsdata:
87 "Position, track, velocity and status information returned by a GPS." 88
89 - class satellite:
90 - def __init__(self, PRN, elevation, azimuth, ss, used=None):
91 self.PRN = PRN 92 self.elevation = elevation 93 self.azimuth = azimuth 94 self.ss = ss 95 self.used = used
96 - def __repr__(self):
97 return "PRN: %3d E: %3d Az: %3d Ss: %3d Used: %s" % ( 98 self.PRN, self.elevation, self.azimuth, self.ss, "ny"[self.used] 99 )
100
101 - def __init__(self):
102 # Initialize all data members 103 self.online = 0 # NZ if GPS on, zero if not 104 105 self.valid = 0 106 self.fix = gpsfix() 107 108 self.status = STATUS_NO_FIX 109 self.utc = "" 110 111 self.satellites_used = 0 # Satellites used in last fix 112 self.xdop = self.ydop = self.vdop = self.tdop = 0 113 self.pdop = self.hdop = self.gdop = 0.0 114 115 self.epe = 0.0 116 117 self.satellites = [] # satellite objects in view 118 119 self.gps_id = None 120 self.driver_mode = 0 121 self.baudrate = 0 122 self.stopbits = 0 123 self.cycle = 0 124 self.mincycle = 0 125 self.device = None 126 self.devices = [] 127 128 self.version = None 129 self.timings = None
130
131 - def __repr__(self):
132 st = "Time: %s (%s)\n" % (self.utc, self.fix.time) 133 st += "Lat/Lon: %f %f\n" % (self.fix.latitude, self.fix.longitude) 134 if isnan(self.fix.altitude): 135 st += "Altitude: ?\n" 136 else: 137 st += "Altitude: %f\n" % (self.fix.altitude) 138 if isnan(self.fix.speed): 139 st += "Speed: ?\n" 140 else: 141 st += "Speed: %f\n" % (self.fix.speed) 142 if isnan(self.fix.track): 143 st += "Track: ?\n" 144 else: 145 st += "Track: %f\n" % (self.fix.track) 146 st += "Status: STATUS_%s\n" % ("NO_FIX", "FIX", "DGPS_FIX")[self.status] 147 st += "Mode: MODE_%s\n" % ("ZERO", "NO_FIX", "2D", "3D")[self.fix.mode] 148 st += "Quality: %d p=%2.2f h=%2.2f v=%2.2f t=%2.2f g=%2.2f\n" % \ 149 (self.satellites_used, self.pdop, self.hdop, self.vdop, self.tdop, self.gdop) 150 st += "Y: %s satellites in view:\n" % len(self.satellites) 151 for sat in self.satellites: 152 st += " %r\n" % sat 153 return st
154
155 -class gps(gpsdata, gpsjson):
156 "Client interface to a running gpsd instance."
157 - def __init__(self, host="127.0.0.1", port=GPSD_PORT, verbose=0, mode=0):
158 gpscommon.__init__(self, host, port, verbose) 159 gpsdata.__init__(self) 160 self.raw_hook = None 161 self.newstyle = False 162 if mode: 163 self.stream(mode)
164
165 - def set_raw_hook(self, hook):
166 self.raw_hook = hook
167
168 - def __oldstyle_unpack(self, buf):
169 # unpack a daemon response into the gps instance members 170 self.fix.time = 0.0 171 fields = buf.strip().split(",") 172 if fields[0] == "GPSD": 173 for field in fields[1:]: 174 if not field or field[1] != '=': 175 continue 176 cmd = field[0].upper() 177 data = field[2:] 178 if data[0] == "?": 179 continue 180 if cmd == 'F': 181 self.device = data 182 elif cmd == 'I': 183 self.gps_id = data 184 elif cmd == 'O': 185 fields = data.split() 186 if fields[0] == '?': 187 self.fix.mode = MODE_NO_FIX 188 else: 189 def default(i, vbit=0, cnv=float): 190 if fields[i] == '?': 191 return NaN 192 else: 193 try: 194 value = cnv(fields[i]) 195 except ValueError: 196 return NaN 197 self.valid |= vbit 198 return value
199 # clear all valid bits that might be set again below 200 self.valid &= ~( 201 TIME_SET | TIMERR_SET | LATLON_SET | ALTITUDE_SET | 202 HERR_SET | VERR_SET | TRACK_SET | SPEED_SET | 203 CLIMB_SET | SPEEDERR_SET | CLIMBERR_SET | MODE_SET 204 ) 205 self.utc = fields[1] 206 self.fix.time = default(1, TIME_SET) 207 if not isnan(self.fix.time): 208 self.utc = isotime(self.fix.time) 209 self.fix.ept = default(2, TIMERR_SET) 210 self.fix.latitude = default(3, LATLON_SET) 211 self.fix.longitude = default(4) 212 self.fix.altitude = default(5, ALTITUDE_SET) 213 self.fix.epx = self.epy = default(6, HERR_SET) 214 self.fix.epv = default(7, VERR_SET) 215 self.fix.track = default(8, TRACK_SET) 216 self.fix.speed = default(9, SPEED_SET) 217 self.fix.climb = default(10, CLIMB_SET) 218 self.fix.epd = default(11) 219 self.fix.eps = default(12, SPEEDERR_SET) 220 self.fix.epc = default(13, CLIMBERR_SET) 221 if len(fields) > 14: 222 self.fix.mode = default(14, MODE_SET, int) 223 else: 224 if self.valid & ALTITUDE_SET: 225 self.fix.mode = MODE_2D 226 else: 227 self.fix.mode = MODE_3D 228 self.valid |= MODE_SET 229 elif cmd == 'X': 230 self.online = float(data) 231 self.valid |= ONLINE_SET 232 elif cmd == 'Y': 233 satellites = data.split(":") 234 prefix = satellites.pop(0).split() 235 d1 = int(prefix.pop()) 236 newsats = [] 237 for i in range(d1): 238 newsats.append(gps.satellite(*map(int, satellites[i].split()))) 239 self.satellites = newsats 240 self.valid |= SATELLITE_SET
241
242 - def __oldstyle_shim(self):
243 # The rest is backwards compatibility for the old interface 244 def default(k, dflt, vbit=0): 245 if k not in self.data.keys(): 246 return dflt 247 else: 248 self.valid |= vbit 249 return self.data[k]
250 if self.data.get("class") == "VERSION": 251 self.version = self.data 252 elif self.data.get("class") == "DEVICE": 253 self.valid = ONLINE_SET | DEVICE_SET 254 self.path = self.data["path"] 255 self.activated = default("activated", None) 256 driver = default("driver", None, DEVICEID_SET) 257 subtype = default("subtype", None, DEVICEID_SET) 258 self.gps_id = driver 259 if subtype: 260 self.gps_id += " " + subtype 261 self.driver_mode = default("native", 0) 262 self.baudrate = default("bps", 0) 263 self.serialmode = default("serialmode", "8N1") 264 self.cycle = default("cycle", NaN) 265 self.mincycle = default("mincycle", NaN) 266 elif self.data.get("class") == "TPV": 267 self.valid = ONLINE_SET 268 self.fix.time = default("time", NaN, TIME_SET) 269 self.fix.ept = default("ept", NaN, TIMERR_SET) 270 self.fix.latitude = default("lat", NaN, LATLON_SET) 271 self.fix.longitude = default("lon", NaN) 272 self.fix.altitude = default("alt", NaN, ALTITUDE_SET) 273 self.fix.epx = default("epx", NaN, HERR_SET) 274 self.fix.epy = default("epy", NaN, HERR_SET) 275 self.fix.epv = default("epv", NaN, VERR_SET) 276 self.fix.track = default("track", NaN, TRACK_SET) 277 self.fix.speed = default("speed", NaN, SPEED_SET) 278 self.fix.climb = default("climb", NaN, CLIMB_SET) 279 self.fix.epd = default("epd", NaN) 280 self.fix.eps = default("eps", NaN, SPEEDERR_SET) 281 self.fix.epc = default("epc", NaN, CLIMBERR_SET) 282 self.fix.mode = default("mode", 0, MODE_SET) 283 elif self.data.get("class") == "SKY": 284 for attrp in "xyvhpg": 285 setattr(self, attrp+"dop", default(attrp+"dop", NaN, DOP_SET)) 286 if "satellites" in self.data.keys(): 287 self.satellites = [] 288 for sat in self.data['satellites']: 289 self.satellites.append(gps.satellite(PRN=sat['PRN'], elevation=sat['el'], azimuth=sat['az'], ss=sat['ss'], used=sat['used'])) 290 self.satellites_used = 0 291 for sat in self.satellites: 292 if sat.used: 293 self.satellites_used += 1 294 self.valid = ONLINE_SET | SATELLITE_SET 295 elif self.data.get("class") == "TIMING": 296 self.data["c_recv"] = self.received 297 self.data["c_decode"] = time.time() 298 self.timings = self.data 299
300 - def poll(self):
301 "Read and interpret data from the daemon." 302 status = gpscommon.read(self) 303 if status <= 0: 304 return status 305 if self.raw_hook: 306 self.raw_hook(self.response); 307 if self.response.startswith("{") and self.response.endswith("}\r\n"): 308 self.json_unpack(self.response) 309 self.__oldstyle_shim() 310 self.newstyle = True 311 self.valid |= PACKET_SET 312 elif self.response.startswith("GPSD"): 313 self.__oldstyle_unpack(self.response) 314 self.valid |= PACKET_SET 315 return 0
316
317 - def next(self):
318 if self.poll() == -1: 319 raise StopIteration 320 if hasattr(self, "data"): 321 return self.data 322 else: 323 return self.response
324
325 - def stream(self, flags=0, outfile=None):
326 "Ask gpsd to stream reports at your client." 327 if (flags & (WATCH_JSON|WATCH_OLDSTYLE|WATCH_NMEA|WATCH_RAW)) == 0: 328 # If we're looking at a daemon that speaks JSON, this 329 # should have been set when we saw the initial VERSION 330 # response. Note, however, that this requires at 331 # least one poll() before stream() is called 332 if self.newstyle or flags & WATCH_NEWSTYLE: 333 flags |= WATCH_JSON 334 else: 335 flags |= WATCH_OLDSTYLE 336 if flags & WATCH_OLDSTYLE: 337 if flags & WATCH_DISABLE: 338 arg = "w-" 339 if flags & WATCH_NMEA: 340 arg += 'r-' 341 return self.send(arg) 342 else: # flags & WATCH_ENABLE: 343 arg = 'w+' 344 if self.raw_hook or (flags & WATCH_NMEA): 345 arg += 'r+' 346 return self.send(arg) 347 else: # flags & WATCH_NEWSTYLE: 348 gpsjson.stream(self, flags)
349 350 if __name__ == '__main__': 351 import readline, getopt, sys 352 (options, arguments) = getopt.getopt(sys.argv[1:], "v") 353 streaming = False 354 verbose = False 355 for (switch, val) in options: 356 if switch == '-v': 357 verbose = True 358 if len(arguments) > 2: 359 print 'Usage: gps.py [-v] [host [port]]' 360 sys.exit(1) 361 362 opts = { "verbose" : verbose } 363 if len(arguments) > 0: 364 opts["host"] = arguments[0] 365 if len(arguments) > 1: 366 opts["port"] = arguments[1] 367 368 session = gps(**opts) 369 session.set_raw_hook(lambda s: sys.stdout.write(s.strip() + "\n")) 370 session.stream(WATCH_ENABLE|WATCH_NEWSTYLE) 371 for report in session: 372 print report 373 374 # gps.py ends here 375