~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/protocols/gps/nmea.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2004-06-21 22:01:11 UTC
  • mto: (2.2.3 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20040621220111-vkf909euqnyrp3nr
Tags: upstream-1.3.0
ImportĀ upstreamĀ versionĀ 1.3.0

Show diffs side-by-side

added added

removed removed

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