~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/protocols/gps/nmea.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

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