~vishvananda/nova/network-refactor

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/protocols/gps/rockwell.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
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
 
 
5
"""Rockwell Semiconductor Zodiac Serial Protocol
 
6
Coded from official protocol specs (Order No. GPS-25, 09/24/1996, Revision 11)
 
7
 
 
8
Maintainer: Bob Ippolito
 
9
 
 
10
The following Rockwell Zodiac messages are currently understood::
 
11
    EARTHA\\r\\n (a hack to "turn on" a DeLorme Earthmate)
 
12
    1000 (Geodesic Position Status Output)
 
13
    1002 (Channel Summary)
 
14
    1003 (Visible Satellites)
 
15
    1011 (Receiver ID)
 
16
 
 
17
The following Rockwell Zodiac messages require implementation::
 
18
    None really, the others aren't quite so useful and require bidirectional communication w/ the device
 
19
 
 
20
Other desired features::
 
21
    - Compatability with the DeLorme Tripmate and other devices with this chipset (?)
 
22
"""
 
23
 
 
24
import struct, operator, math
 
25
from twisted.internet import protocol
 
26
from twisted.python import log
 
27
 
 
28
DEBUG = 1
 
29
 
 
30
class ZodiacParseError(ValueError):
 
31
  pass
 
32
 
 
33
class Zodiac(protocol.Protocol):
 
34
  dispatch = {
 
35
    # Output Messages (* means they get sent by the receiver by default periodically)
 
36
    1000: 'fix',          # *Geodesic Position Status Output
 
37
    1001: 'ecef',         # ECEF Position Status Output
 
38
    1002: 'channels',     # *Channel Summary
 
39
    1003: 'satellites',   # *Visible Satellites
 
40
    1005: 'dgps',         # Differential GPS Status
 
41
    1007: 'channelmeas',  # Channel Measurement
 
42
    1011: 'id',           # *Receiver ID
 
43
    1012: 'usersettings', # User-Settings Output
 
44
    1100: 'testresults',  # Built-In Test Results
 
45
    1102: 'meastimemark', # Measurement Time Mark
 
46
    1108: 'utctimemark',  # UTC Time Mark Pulse Output
 
47
    1130: 'serial',       # Serial Port Communication Parameters In Use
 
48
    1135: 'eepromupdate', # EEPROM Update
 
49
    1136: 'eepromstatus', # EEPROM Status
 
50
  }
 
51
  # these aren't used for anything yet, just sitting here for reference
 
52
  messages = {
 
53
    # Input Messages
 
54
    'fix':      1200,     # Geodesic Position and Velocity Initialization
 
55
    'udatum':   1210,     # User-Defined Datum Definition
 
56
    'mdatum':   1211,     # Map Datum Select
 
57
    'smask':    1212,     # Satellite Elevation Mask Control
 
58
    'sselect':  1213,     # Satellite Candidate Select
 
59
    'dgpsc':    1214,     # Differential GPS Control
 
60
    'startc':   1216,     # Cold Start Control
 
61
    'svalid':   1217,     # Solution Validity Control
 
62
    'antenna':  1218,     # Antenna Type Select
 
63
    'altinput': 1219,     # User-Entered Altitude Input
 
64
    'appctl':   1220,     # Application Platform Control
 
65
    'navcfg':   1221,     # Nav Configuration
 
66
    'test':     1300,     # Perform Built-In Test Command
 
67
    'restart':  1303,     # Restart Command
 
68
    'serial':   1330,     # Serial Port Communications Parameters
 
69
    'msgctl':   1331,     # Message Protocol Control
 
70
    'dgpsd':    1351,     # Raw DGPS RTCM SC-104 Data
 
71
  }  
 
72
  MAX_LENGTH = 296
 
73
  allow_earthmate_hack = 1
 
74
  recvd = ""
 
75
  
 
76
  def dataReceived(self, recd):
 
77
    self.recvd = self.recvd + recd
 
78
    while len(self.recvd) >= 10:
 
79
 
 
80
      # hack for DeLorme EarthMate
 
81
      if self.recvd[:8] == 'EARTHA\r\n':
 
82
        if self.allow_earthmate_hack:
 
83
          self.allow_earthmate_hack = 0
 
84
          self.transport.write('EARTHA\r\n')
 
85
        self.recvd = self.recvd[8:]
 
86
        continue
 
87
      
 
88
      if self.recvd[0:2] != '\xFF\x81':
 
89
        if DEBUG:
 
90
          raise ZodiacParseError('Invalid Sync %r' % self.recvd)
 
91
        else:
 
92
          raise ZodiacParseError
 
93
      sync, msg_id, length, acknak, checksum = struct.unpack('<HHHHh', self.recvd[:10])
 
94
      
 
95
      # verify checksum
 
96
      cksum = -(reduce(operator.add, (sync, msg_id, length, acknak)) & 0xFFFF)
 
97
      cksum, = struct.unpack('<h', struct.pack('<h', cksum))
 
98
      if cksum != checksum:
 
99
        if DEBUG:
 
100
          raise ZodiacParseError('Invalid Header Checksum %r != %r %r' % (checksum, cksum, self.recvd[:8]))
 
101
        else:
 
102
          raise ZodiacParseError
 
103
      
 
104
      # length was in words, now it's bytes
 
105
      length = length * 2
 
106
 
 
107
      # do we need more data ?
 
108
      neededBytes = 10
 
109
      if length:
 
110
        neededBytes += length + 2
 
111
      if len(self.recvd) < neededBytes:
 
112
        break
 
113
      
 
114
      if neededBytes > self.MAX_LENGTH:
 
115
        raise ZodiacParseError("Invalid Header??")
 
116
 
 
117
      # empty messages pass empty strings
 
118
      message = ''
 
119
 
 
120
      # does this message have data ?
 
121
      if length:
 
122
        message, checksum = self.recvd[10:10+length], struct.unpack('<h', self.recvd[10+length:neededBytes])[0]
 
123
        cksum = 0x10000 - (reduce(operator.add, struct.unpack('<%dH' % (length/2), message)) & 0xFFFF)
 
124
        cksum, = struct.unpack('<h', struct.pack('<h', cksum))
 
125
        if cksum != checksum:
 
126
          if DEBUG:
 
127
            log.dmsg('msg_id = %r length = %r' % (msg_id, length), debug=True)
 
128
            raise ZodiacParseError('Invalid Data Checksum %r != %r %r' % (checksum, cksum, message))
 
129
          else:
 
130
            raise ZodiacParseError
 
131
      
 
132
      # discard used buffer, dispatch message
 
133
      self.recvd = self.recvd[neededBytes:]
 
134
      self.receivedMessage(msg_id, message, acknak)
 
135
  
 
136
  def receivedMessage(self, msg_id, message, acknak):
 
137
    dispatch = self.dispatch.get(msg_id, None)
 
138
    if not dispatch:
 
139
      raise ZodiacParseError('Unknown msg_id = %r' % msg_id)
 
140
    handler = getattr(self, 'handle_%s' % dispatch, None)
 
141
    decoder = getattr(self, 'decode_%s' % dispatch, None)
 
142
    if not (handler and decoder):
 
143
      # missing handler or decoder
 
144
      #if DEBUG:
 
145
      #  log.msg('MISSING HANDLER/DECODER PAIR FOR: %r' % (dispatch,), debug=True)
 
146
      return
 
147
    decoded = decoder(message)
 
148
    return handler(*decoded)
 
149
  
 
150
  def decode_fix(self, message):
 
151
    assert len(message) == 98, "Geodesic Position Status Output should be 55 words total (98 byte message)"
 
152
    (ticks, msgseq, satseq, navstatus, navtype, nmeasure, polar, gpswk, gpses, gpsns, utcdy, utcmo, utcyr, utchr, utcmn, utcsc, utcns, latitude, longitude, height, geoidalsep, speed, course, magvar, climb, mapdatum, exhposerr, exvposerr, extimeerr, exphvelerr, clkbias, clkbiasdev, clkdrift, clkdriftdev) = struct.unpack('<LhhHHHHHLLHHHHHHLlllhLHhhHLLLHllll', message)
 
153
 
 
154
    # there's a lot of shit in here.. 
 
155
    # I'll just snag the important stuff and spit it out like my NMEA decoder
 
156
    utc = (utchr * 3600.0) + (utcmn * 60.0) + utcsc + (float(utcns) * 0.000000001)
 
157
    
 
158
    log.msg('utchr, utcmn, utcsc, utcns = ' + repr((utchr, utcmn, utcsc, utcns)), debug=True)
 
159
    
 
160
    latitude = float(latitude)   * 0.00000180 / math.pi
 
161
    longitude = float(longitude) * 0.00000180 / math.pi
 
162
    posfix = not (navstatus & 0x001c)
 
163
    satellites = nmeasure
 
164
    hdop = float(exhposerr) * 0.01
 
165
    altitude = float(height) * 0.01, 'M'
 
166
    geoid = float(geoidalsep) * 0.01, 'M'
 
167
    dgps = None
 
168
    return (
 
169
      # seconds since 00:00 UTC
 
170
      utc,                 
 
171
      # latitude (degrees)
 
172
      latitude,
 
173
      # longitude (degrees)
 
174
      longitude,
 
175
      # position fix status (invalid = False, valid = True)
 
176
      posfix,
 
177
      # number of satellites [measurements] used for fix 0 <= satellites <= 12 
 
178
      satellites,
 
179
      # horizontal dilution of precision
 
180
      hdop,
 
181
      # (altitude according to WGS-84 ellipsoid, units (always 'M' for meters)) 
 
182
      altitude,
 
183
      # (geoid separation according to WGS-84 ellipsoid, units (always 'M' for meters))
 
184
      geoid,
 
185
      # None, for compatability w/ NMEA code
 
186
      dgps,
 
187
    )
 
188
 
 
189
  def decode_id(self, message):
 
190
    assert len(message) == 106, "Receiver ID Message should be 59 words total (106 byte message)"
 
191
    ticks, msgseq, channels, software_version, software_date, options_list, reserved = struct.unpack('<Lh20s20s20s20s20s', message)
 
192
    channels, software_version, software_date, options_list = map(lambda s: s.split('\0')[0], (channels, software_version, software_date, options_list))
 
193
    software_version = float(software_version)
 
194
    channels = int(channels) # 0-12 .. but ALWAYS 12, so we ignore.
 
195
    options_list = int(options_list[:4], 16) # only two bitflags, others are reserved
 
196
    minimize_rom = (options_list & 0x01) > 0
 
197
    minimize_ram = (options_list & 0x02) > 0
 
198
    # (version info), (options info)
 
199
    return ((software_version, software_date), (minimize_rom, minimize_ram))
 
200
 
 
201
  def decode_channels(self, message):
 
202
    assert len(message) == 90, "Channel Summary Message should be 51 words total (90 byte message)"
 
203
    ticks, msgseq, satseq, gpswk, gpsws, gpsns = struct.unpack('<LhhHLL', message[:18])
 
204
    channels = []
 
205
    message = message[18:]
 
206
    for i in range(12):
 
207
      flags, prn, cno = struct.unpack('<HHH', message[6 * i:6 * (i + 1)])
 
208
      # measurement used, ephemeris available, measurement valid, dgps corrections available
 
209
      flags = (flags & 0x01, flags & 0x02, flags & 0x04, flags & 0x08)
 
210
      channels.append((flags, prn, cno))
 
211
    # ((flags, satellite PRN, C/No in dbHz)) for 12 channels
 
212
    # satellite message sequence number
 
213
    # gps week number, gps seconds in week (??), gps nanoseconds from Epoch
 
214
    return (tuple(channels),) #, satseq, (gpswk, gpsws, gpsns))
 
215
 
 
216
  def decode_satellites(self, message):
 
217
    assert len(message) == 90, "Visible Satellites Message should be 51 words total (90 byte message)"
 
218
    ticks, msgseq, gdop, pdop, hdop, vdop, tdop, numsatellites = struct.unpack('<LhhhhhhH', message[:18])
 
219
    gdop, pdop, hdop, vdop, tdop = map(lambda n: float(n) * 0.01, (gdop, pdop, hdop, vdop, tdop))
 
220
    satellites = []
 
221
    message = message[18:]
 
222
    for i in range(numsatellites):
 
223
      prn, azi, elev = struct.unpack('<Hhh', message[6 * i:6 * (i + 1)])
 
224
      azi, elev = map(lambda n: (float(n) * 0.0180 / math.pi), (azi, elev))
 
225
      satellites.push((prn, azi, elev))
 
226
    # ((PRN [0, 32], azimuth +=[0.0, 180.0] deg, elevation +-[0.0, 90.0] deg)) satellite info (0-12)
 
227
    # (geometric, position, horizontal, vertical, time) dilution of precision 
 
228
    return (tuple(satellites), (gdop, pdop, hdop, vdop, tdop))
 
229
 
 
230
  def decode_dgps(self, message):
 
231
    assert len(message) == 38, "Differential GPS Status Message should be 25 words total (38 byte message)"
 
232
    raise NotImplementedError
 
233
 
 
234
  def decode_ecef(self, message):
 
235
    assert len(message) == 96, "ECEF Position Status Output Message should be 54 words total (96 byte message)"
 
236
    raise NotImplementedError
 
237
 
 
238
  def decode_channelmeas(self, message):
 
239
    assert len(message) == 296, "Channel Measurement Message should be 154 words total (296 byte message)"
 
240
    raise NotImplementedError
 
241
 
 
242
  def decode_usersettings(self, message):
 
243
    assert len(message) == 32, "User-Settings Output Message should be 22 words total (32 byte message)"
 
244
    raise NotImplementedError
 
245
 
 
246
  def decode_testresults(self, message):
 
247
    assert len(message) == 28, "Built-In Test Results Message should be 20 words total (28 byte message)"
 
248
    raise NotImplementedError
 
249
 
 
250
  def decode_meastimemark(self, message):
 
251
    assert len(message) == 494, "Measurement Time Mark Message should be 253 words total (494 byte message)"
 
252
    raise NotImplementedError
 
253
 
 
254
  def decode_utctimemark(self, message):
 
255
    assert len(message) == 28, "UTC Time Mark Pulse Output Message should be 20 words total (28 byte message)"
 
256
    raise NotImplementedError
 
257
 
 
258
  def decode_serial(self, message):
 
259
    assert len(message) == 30, "Serial Port Communication Paramaters In Use Message should be 21 words total (30 byte message)"
 
260
    raise NotImplementedError
 
261
 
 
262
  def decode_eepromupdate(self, message):
 
263
    assert len(message) == 8, "EEPROM Update Message should be 10 words total (8 byte message)"
 
264
    raise NotImplementedError
 
265
 
 
266
  def decode_eepromstatus(self, message):
 
267
    assert len(message) == 24, "EEPROM Status Message should be 18 words total (24 byte message)"
 
268
    raise NotImplementedError