| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.test.test_nmea -*- | |
| 2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 3 # See LICENSE for details. | |
| 4 | |
| 5 """NMEA 0183 implementation | |
| 6 | |
| 7 Maintainer: U{Bob Ippolito<mailto:bob@redivi.com>} | |
| 8 | |
| 9 The following NMEA 0183 sentences are currently understood:: | |
| 10 GPGGA (fix) | |
| 11 GPGLL (position) | |
| 12 GPRMC (position and time) | |
| 13 GPGSA (active satellites) | |
| 14 | |
| 15 The following NMEA 0183 sentences require implementation:: | |
| 16 None really, the others aren't generally useful or implemented in most devic
es anyhow | |
| 17 | |
| 18 Other desired features:: | |
| 19 - A NMEA 0183 producer to emulate GPS devices (?) | |
| 20 """ | |
| 21 | |
| 22 import operator | |
| 23 from twisted.protocols import basic | |
| 24 | |
| 25 POSFIX_INVALID, POSFIX_SPS, POSFIX_DGPS, POSFIX_PPS = 0, 1, 2, 3 | |
| 26 MODE_AUTO, MODE_FORCED = 'A', 'M' | |
| 27 MODE_NOFIX, MODE_2D, MODE_3D = 1, 2, 3 | |
| 28 | |
| 29 class InvalidSentence(Exception): | |
| 30 pass | |
| 31 | |
| 32 class InvalidChecksum(Exception): | |
| 33 pass | |
| 34 | |
| 35 class NMEAReceiver(basic.LineReceiver): | |
| 36 """This parses most common NMEA-0183 messages, presumably from a serial GPS
device at 4800 bps | |
| 37 """ | |
| 38 delimiter = '\r\n' | |
| 39 dispatch = { | |
| 40 'GPGGA': 'fix', | |
| 41 'GPGLL': 'position', | |
| 42 'GPGSA': 'activesatellites', | |
| 43 'GPRMC': 'positiontime', | |
| 44 'GPGSV': 'viewsatellites', # not implemented | |
| 45 'GPVTG': 'course', # not implemented | |
| 46 'GPALM': 'almanac', # not implemented | |
| 47 'GPGRS': 'range', # not implemented | |
| 48 'GPGST': 'noise', # not implemented | |
| 49 'GPMSS': 'beacon', # not implemented | |
| 50 'GPZDA': 'time', # not implemented | |
| 51 } | |
| 52 # generally you may miss the beginning of the first message | |
| 53 ignore_invalid_sentence = 1 | |
| 54 # checksums shouldn't be invalid | |
| 55 ignore_checksum_mismatch = 0 | |
| 56 # ignore unknown sentence types | |
| 57 ignore_unknown_sentencetypes = 0 | |
| 58 # do we want to even bother checking to see if it's from the 20th century? | |
| 59 convert_dates_before_y2k = 1 | |
| 60 | |
| 61 def lineReceived(self, line): | |
| 62 if not line.startswith('$'): | |
| 63 if self.ignore_invalid_sentence: | |
| 64 return | |
| 65 raise InvalidSentence("%r does not begin with $" % (line,)) | |
| 66 # message is everything between $ and *, checksum is xor of all ASCII va
lues of the message | |
| 67 strmessage, checksum = line[1:].strip().split('*') | |
| 68 message = strmessage.split(',') | |
| 69 sentencetype, message = message[0], message[1:] | |
| 70 dispatch = self.dispatch.get(sentencetype, None) | |
| 71 if (not dispatch) and (not self.ignore_unknown_sentencetypes): | |
| 72 raise InvalidSentence("sentencetype %r" % (sentencetype,)) | |
| 73 if not self.ignore_checksum_mismatch: | |
| 74 checksum, calculated_checksum = int(checksum, 16), reduce(operator.x
or, map(ord, strmessage)) | |
| 75 if checksum != calculated_checksum: | |
| 76 raise InvalidChecksum("Given 0x%02X != 0x%02X" % (checksum, calc
ulated_checksum)) | |
| 77 handler = getattr(self, "handle_%s" % dispatch, None) | |
| 78 decoder = getattr(self, "decode_%s" % dispatch, None) | |
| 79 if not (dispatch and handler and decoder): | |
| 80 # missing dispatch, handler, or decoder | |
| 81 return | |
| 82 # return handler(*decoder(*message)) | |
| 83 try: | |
| 84 decoded = decoder(*message) | |
| 85 except Exception, e: | |
| 86 raise InvalidSentence("%r is not a valid %s (%s) sentence" % (line,
sentencetype, dispatch)) | |
| 87 return handler(*decoded) | |
| 88 | |
| 89 def decode_position(self, latitude, ns, longitude, ew, utc, status): | |
| 90 latitude, longitude = self._decode_latlon(latitude, ns, longitude, ew) | |
| 91 utc = self._decode_utc(utc) | |
| 92 if status == 'A': | |
| 93 status = 1 | |
| 94 else: | |
| 95 status = 0 | |
| 96 return ( | |
| 97 latitude, | |
| 98 longitude, | |
| 99 utc, | |
| 100 status, | |
| 101 ) | |
| 102 | |
| 103 def decode_positiontime(self, utc, status, latitude, ns, longitude, ew, spee
d, course, utcdate, magvar, magdir): | |
| 104 utc = self._decode_utc(utc) | |
| 105 latitude, longitude = self._decode_latlon(latitude, ns, longitude, ew) | |
| 106 if speed != '': | |
| 107 speed = float(speed) | |
| 108 else: | |
| 109 speed = None | |
| 110 if course != '': | |
| 111 course = float(course) | |
| 112 else: | |
| 113 course = None | |
| 114 utcdate = 2000+int(utcdate[4:6]), int(utcdate[2:4]), int(utcdate[0:2]) | |
| 115 if self.convert_dates_before_y2k and utcdate[0] > 2073: | |
| 116 # GPS was invented by the US DoD in 1973, but NMEA uses 2 digit year
. | |
| 117 # Highly unlikely that we'll be using NMEA or this twisted module in
70 years, | |
| 118 # but remotely possible that you'll be using it to play back data fr
om the 20th century. | |
| 119 utcdate = (utcdate[0] - 100, utcdate[1], utcdate[2]) | |
| 120 if magvar != '': | |
| 121 magvar = float(magvar) | |
| 122 if magdir == 'W': | |
| 123 magvar = -magvar | |
| 124 else: | |
| 125 magvar = None | |
| 126 return ( | |
| 127 latitude, | |
| 128 longitude, | |
| 129 speed, | |
| 130 course, | |
| 131 # UTC seconds past utcdate | |
| 132 utc, | |
| 133 # UTC (year, month, day) | |
| 134 utcdate, | |
| 135 # None or magnetic variation in degrees (west is negative) | |
| 136 magvar, | |
| 137 ) | |
| 138 | |
| 139 def _decode_utc(self, utc): | |
| 140 utc_hh, utc_mm, utc_ss = map(float, (utc[:2], utc[2:4], utc[4:])) | |
| 141 return utc_hh * 3600.0 + utc_mm * 60.0 + utc_ss | |
| 142 | |
| 143 def _decode_latlon(self, latitude, ns, longitude, ew): | |
| 144 latitude = float(latitude[:2]) + float(latitude[2:])/60.0 | |
| 145 if ns == 'S': | |
| 146 latitude = -latitude | |
| 147 longitude = float(longitude[:3]) + float(longitude[3:])/60.0 | |
| 148 if ew == 'W': | |
| 149 longitude = -longitude | |
| 150 return (latitude, longitude) | |
| 151 | |
| 152 def decode_activesatellites(self, mode1, mode2, *args): | |
| 153 satellites, (pdop, hdop, vdop) = args[:12], map(float, args[12:]) | |
| 154 satlist = [] | |
| 155 for n in satellites: | |
| 156 if n: | |
| 157 satlist.append(int(n)) | |
| 158 else: | |
| 159 satlist.append(None) | |
| 160 mode = (mode1, int(mode2)) | |
| 161 return ( | |
| 162 # satellite list by channel | |
| 163 tuple(satlist), | |
| 164 # (MODE_AUTO/MODE_FORCED, MODE_NOFIX/MODE_2DFIX/MODE_3DFIX) | |
| 165 mode, | |
| 166 # position dilution of precision | |
| 167 pdop, | |
| 168 # horizontal dilution of precision | |
| 169 hdop, | |
| 170 # vertical dilution of precision | |
| 171 vdop, | |
| 172 ) | |
| 173 | |
| 174 def decode_fix(self, utc, latitude, ns, longitude, ew, posfix, satellites, h
dop, altitude, altitude_units, geoid_separation, geoid_separation_units, dgps_ag
e, dgps_station_id): | |
| 175 latitude, longitude = self._decode_latlon(latitude, ns, longitude, ew) | |
| 176 utc = self._decode_utc(utc) | |
| 177 posfix = int(posfix) | |
| 178 satellites = int(satellites) | |
| 179 hdop = float(hdop) | |
| 180 altitude = (float(altitude), altitude_units) | |
| 181 if geoid_separation != '': | |
| 182 geoid = (float(geoid_separation), geoid_separation_units) | |
| 183 else: | |
| 184 geoid = None | |
| 185 if dgps_age != '': | |
| 186 dgps = (float(dgps_age), dgps_station_id) | |
| 187 else: | |
| 188 dgps = None | |
| 189 return ( | |
| 190 # seconds since 00:00 UTC | |
| 191 utc, | |
| 192 # latitude (degrees) | |
| 193 latitude, | |
| 194 # longitude (degrees) | |
| 195 longitude, | |
| 196 # position fix status (POSFIX_INVALID, POSFIX_SPS, POSFIX_DGPS, POSF
IX_PPS) | |
| 197 posfix, | |
| 198 # number of satellites used for fix 0 <= satellites <= 12 | |
| 199 satellites, | |
| 200 # horizontal dilution of precision | |
| 201 hdop, | |
| 202 # None or (altitude according to WGS-84 ellipsoid, units (typically
'M' for meters)) | |
| 203 altitude, | |
| 204 # None or (geoid separation according to WGS-84 ellipsoid, units (ty
pically 'M' for meters)) | |
| 205 geoid, | |
| 206 # (age of dgps data in seconds, dgps station id) | |
| 207 dgps, | |
| 208 ) | |
| OLD | NEW |