00001
00002 import serial
00003 import struct
00004
00005 import sys, getopt, time, glob
00006
00007 from mtdef import MID, OutputMode, OutputSettings, MTException, Baudrates, XDIGroup, getName, getMIDName
00008
00009
00010 verbose = False
00011
00012
00013
00014
00015
00016 class MTDevice(object):
00017 """XSens MT device communication object."""
00018
00019 def __init__(self, port, baudrate=115200, timeout=0.001, autoconf=True,
00020 config_mode=False):
00021 """Open device."""
00022
00023 self.device = serial.Serial(port, baudrate, timeout=timeout,
00024 writeTimeout=timeout)
00025 self.device.flushInput()
00026 self.device.flushOutput()
00027
00028 self.timeout = 100*timeout
00029 if autoconf:
00030 self.auto_config()
00031 else:
00032
00033 self.mode = None
00034
00035 self.settings = None
00036
00037 self.length = None
00038
00039 self.header = None
00040 if config_mode:
00041 self.GoToConfig()
00042
00043
00044
00045
00046
00047
00048 def write_msg(self, mid, data=[]):
00049 """Low-level message sending function."""
00050 length = len(data)
00051 if length>254:
00052 lendat = [0xFF, 0xFF&length, 0xFF&(length>>8)]
00053 else:
00054 lendat = [length]
00055 packet = [0xFA, 0xFF, mid] + lendat + list(data)
00056 packet.append(0xFF&(-(sum(packet[1:]))))
00057 msg = struct.pack('%dB'%len(packet), *packet)
00058 start = time.time()
00059 while (time.time()-start)<self.timeout and self.device.read():
00060
00061 pass
00062 self.device.write(msg)
00063 if verbose:
00064 print "MT: Write message id 0x%02X (%s) with %d data bytes: [%s]"%(mid, getMIDName(mid), length,
00065 ' '.join("%02X"% v for v in data))
00066
00067
00068
00069 def read_data_msg(self, buf=bytearray()):
00070 """Low-level MTData receiving function.
00071 Take advantage of known message length."""
00072 start = time.time()
00073 if self.length>254:
00074 totlength = 7 + self.length
00075 else:
00076 totlength = 5 + self.length
00077 while (time.time()-start)<self.timeout:
00078 while len(buf)<totlength:
00079 buf.extend(self.device.read(totlength-len(buf)))
00080 preamble_ind = buf.find(self.header)
00081 if preamble_ind==-1:
00082
00083
00084 del buf[:-3]
00085 continue
00086 elif preamble_ind:
00087
00088
00089 del buf[:preamble_ind]
00090
00091 while len(buf)<totlength:
00092 buf.extend(self.device.read(totlength-len(buf)))
00093 if 0xFF&sum(buf[1:]):
00094
00095
00096 del buf[:buf.find(self.header)-2]
00097 continue
00098 data = str(buf[-self.length-1:-1])
00099 del buf[:]
00100 return data
00101 else:
00102 raise MTException("could not find MTData message.")
00103
00104
00105 def read_msg(self):
00106 """Low-level message receiving function."""
00107 start = time.time()
00108 while (time.time()-start)<self.timeout:
00109 new_start = time.time()
00110
00111
00112 def waitfor(size=1):
00113 while self.device.inWaiting() < size:
00114 if time.time()-new_start >= self.timeout:
00115 raise MTException("timeout waiting for message.")
00116
00117 c = self.device.read()
00118 while (not c) and ((time.time()-new_start)<self.timeout):
00119 c = self.device.read()
00120 if not c:
00121 raise MTException("timeout waiting for message.")
00122 if ord(c)<>0xFA:
00123 continue
00124
00125 waitfor(3)
00126 if ord(self.device.read())<>0xFF:
00127 continue
00128
00129
00130 mid, length = struct.unpack('!BB', self.device.read(2))
00131 if length==255:
00132 waitfor(2)
00133 length, = struct.unpack('!H', self.device.read(2))
00134
00135
00136 waitfor(length+1)
00137 buf = self.device.read(length+1)
00138 while (len(buf)<length+1) and ((time.time()-start)<self.timeout):
00139 buf+= self.device.read(length+1-len(buf))
00140 if (len(buf)<length+1):
00141 continue
00142 checksum = ord(buf[-1])
00143 data = struct.unpack('!%dB'%length, buf[:-1])
00144 if mid == MID.Error:
00145 sys.stderr.write("MT error 0x%02X: %s."%(data[0],
00146 MID.ErrorCodes[data[0]]))
00147 if verbose:
00148 print "MT: Got message id 0x%02X (%s) with %d data bytes: [%s]"%(mid, getMIDName(mid), length,
00149 ' '.join("%02X"% v for v in data))
00150 if 0xFF&sum(data, 0xFF+mid+length+checksum):
00151 sys.stderr.write("invalid checksum; discarding data and "\
00152 "waiting for next message.\n")
00153 continue
00154 return (mid, buf[:-1])
00155 else:
00156 raise MTException("could not find message.")
00157
00158
00159 def write_ack(self, mid, data=[]):
00160 """Send a message a read confirmation."""
00161 self.write_msg(mid, data)
00162 for tries in range(100):
00163 mid_ack, data_ack = self.read_msg()
00164 if mid_ack==(mid+1):
00165 break
00166 else:
00167 raise MTException("Ack (0x%X) expected, MID 0x%X received instead"\
00168 " (after 100 tries)."%(mid+1, mid_ack))
00169 return data_ack
00170
00171
00172
00173
00174
00175
00176
00177 def Reset(self):
00178 """Reset MT device."""
00179 self.write_ack(MID.Reset)
00180
00181
00182
00183 def GoToConfig(self):
00184 """Place MT device in configuration mode."""
00185 self.write_ack(MID.GoToConfig)
00186
00187
00188
00189 def GoToMeasurement(self):
00190 """Place MT device in measurement mode."""
00191 self.write_ack(MID.GoToMeasurement)
00192
00193
00194
00195 def RestoreFactoryDefaults(self):
00196 """Restore MT device configuration to factory defaults (soft version).
00197 """
00198 self.GoToConfig()
00199 self.write_ack(MID.RestoreFactoryDef)
00200
00201
00202
00203
00204 def GetOutputMode(self):
00205 """Get current output mode.
00206 Assume the device is in Config state."""
00207 data = self.write_ack(MID.SetOutputMode)
00208 self.mode, = struct.unpack('!H', data)
00209 return self.mode
00210
00211
00212
00213
00214 def SetOutputMode(self, mode):
00215 """Select which information to output.
00216 Assume the device is in Config state."""
00217 H, L = (mode&0xFF00)>>8, mode&0x00FF
00218 self.write_ack(MID.SetOutputMode, (H, L))
00219 self.mode = mode
00220
00221
00222
00223
00224 def GetOutputSettings(self):
00225 """Get current output mode.
00226 Assume the device is in Config state."""
00227 data = self.write_ack(MID.SetOutputSettings)
00228 self.settings, = struct.unpack('!I', data)
00229 return self.settings
00230
00231
00232
00233
00234 def SetOutputSettings(self, settings):
00235 """Select how to output the information.
00236 Assume the device is in Config state."""
00237 HH, HL = (settings&0xFF000000)>>24, (settings&0x00FF0000)>>16
00238 LH, LL = (settings&0x0000FF00)>>8, settings&0x000000FF
00239 self.write_ack(MID.SetOutputSettings, (HH, HL, LH, LL))
00240 self.settings = settings
00241
00242
00243
00244
00245 def SetPeriod(self, period):
00246 """Set the period of sampling.
00247 Assume the device is in Config state."""
00248 H, L = (period&0xFF00)>>8, period&0x00FF
00249 self.write_ack(MID.SetPeriod, (H, L))
00250
00251
00252
00253
00254 def SetOutputSkipFactor(self, skipfactor):
00255 """Set the output skip factor.
00256 Assume the device is in Config state."""
00257 H, L = (skipfactor&0xFF00)>>8, skipfactor&0x00FF
00258 self.write_ack(MID.SetOutputSkipFactor, (H, L))
00259
00260
00261
00262
00263 def ReqDataLength(self):
00264 """Get data length.
00265 Assume the device is in Config state."""
00266 data = self.write_ack(MID.ReqDataLength)
00267 self.length, = struct.unpack('!H', data)
00268 self.header = '\xFA\xFF\x32'+chr(self.length)
00269 return self.length
00270
00271
00272
00273
00274 def ReqConfiguration(self):
00275 """Ask for the current configuration of the MT device.
00276 Assume the device is in Config state."""
00277 config = self.write_ack(MID.ReqConfiguration)
00278 try:
00279 masterID, period, skipfactor, _, _, _, date, time, num, deviceID,\
00280 length, mode, settings =\
00281 struct.unpack('!IHHHHI8s8s32x32xHIHHI8x', config)
00282 except struct.error:
00283 raise MTException("could not parse configuration.")
00284 self.mode = mode
00285 self.settings = settings
00286 self.length = length
00287 self.header = '\xFA\xFF\x32'+chr(length)
00288 conf = {'output-mode': mode,
00289 'output-settings': settings,
00290 'length': length,
00291 'period': period,
00292 'skipfactor': skipfactor,
00293 'Master device ID': masterID,
00294 'date': date,
00295 'time': time,
00296 'number of devices': num,
00297 'device ID': deviceID}
00298 return conf
00299
00300
00301
00302
00303 def SetBaudrate(self, brid):
00304 """Set the baudrate of the device using the baudrate id.
00305 Assume the device is in Config state."""
00306 self.write_ack(MID.SetBaudrate, (brid,))
00307
00308
00309
00310
00311 def ReqAvailableScenarios(self):
00312 """Request the available XKF scenarios on the device.
00313 Assume the device is in Config state."""
00314 scenarios_dat = self.write_ack(MID.ReqAvailableScenarios)
00315 scenarios = []
00316 try:
00317 for i in range(len(scenarios_dat)/22):
00318 scenario_type, version, label =\
00319 struct.unpack('!BB20s', scenarios_dat[22*i:22*(i+1)])
00320 scenarios.append((scenario_type, version, label.strip()))
00321
00322 self.scenarios = scenarios
00323 except struct.error:
00324 raise MTException("could not parse the available XKF scenarios.")
00325 return scenarios
00326
00327
00328
00329
00330 def ReqCurrentScenario(self):
00331 """Request the ID of the currently used XKF scenario.
00332 Assume the device is in Config state."""
00333 data = self.write_ack(MID.SetCurrentScenario)
00334
00335 self.scenario_id, = struct.unpack('!H', data)
00336 try:
00337 scenarios = self.scenarios
00338 except AttributeError:
00339 scenarios = self.ReqAvailableScenarios()
00340 for t, _, label in scenarios:
00341 if t==self.scenario_id:
00342
00343 self.scenario_label = label
00344 break
00345 else:
00346 self.scenario_label = ""
00347 return self.scenario_id, self.scenario_label
00348
00349
00350
00351
00352 def SetCurrentScenario(self, scenario_id):
00353 """Sets the XKF scenario to use.
00354 Assume the device is in Config state."""
00355 self.write_ack(MID.SetCurrentScenario, (0x00, scenario_id&0xFF))
00356
00357
00358
00359
00360
00361
00362 def configure(self, mode, settings, period=None, skipfactor=None):
00363 """Configure the mode and settings of the MT device."""
00364 self.GoToConfig()
00365 self.SetOutputMode(mode)
00366 self.SetOutputSettings(settings)
00367 if period is not None:
00368 self.SetPeriod(period)
00369 if skipfactor is not None:
00370 self.SetOutputSkipFactor(skipfactor)
00371
00372 self.GetOutputMode()
00373 self.GetOutputSettings()
00374 self.ReqDataLength()
00375 self.GoToMeasurement()
00376
00377
00378
00379 def auto_config(self):
00380 """Read configuration from device."""
00381 self.GoToConfig()
00382 self.ReqConfiguration()
00383 self.GoToMeasurement()
00384 return self.mode, self.settings, self.length
00385
00386
00387
00388
00389
00390
00391
00392
00393 def read_measurement(self, mode=None, settings=None):
00394
00395
00396 mid, data = self.read_msg()
00397 if mid==MID.MTData:
00398 return self.parse_MTData(data, mode, settings)
00399 elif mid==MID.MTData2:
00400 return self.parse_MTData2(data)
00401 else:
00402 raise MTException("unknown data message: mid=0x%02X (%s)."% (mid, getMIDName(mid)))
00403
00404
00405 def parse_MTData2(self, data):
00406
00407 def parse_temperature(data_id, content, ffmt):
00408 o = {}
00409 if (data_id&0x00F0) == 0x10:
00410 o['Temp'], = struct.unpack('!'+ffmt, content)
00411 else:
00412 raise MTException("unknown packet: 0x%04X."%data_id)
00413 return o
00414 def parse_timestamp(data_id, content, ffmt):
00415 o = {}
00416 if (data_id&0x00F0) == 0x10:
00417 o['ns'], o['Year'], o['Month'], o['Day'], o['Hour'],\
00418 o['Minute'], o['Second'], o['Flags'] =\
00419 struct.unpack('!LHBBBBBB', content)
00420 elif (data_id&0x00F0) == 0x20:
00421 o['PacketCounter'], = struct.unpack('!H', content)
00422 elif (data_id&0x00F0) == 0x30:
00423 o['TimeOfWeek'], = struct.unpack('!L', content)
00424 elif (data_id&0x00F0) == 0x40:
00425 o['gpsAge'], = struct.unpack('!B', content)
00426 elif (data_id&0x00F0) == 0x50:
00427 o['pressureAge'], = struct.unpack('!B', content)
00428 elif (data_id&0x00F0) == 0x60:
00429 o['SampleTimeFine'], = struct.unpack('!L', content)
00430 elif (data_id&0x00F0) == 0x70:
00431 o['SampleTimeCoarse'], = struct.unpack('!L', content)
00432 elif (data_id&0x00F0) == 0x80:
00433 o['startFrame'], o['endFrame'] = struct.unpack('!HH', content)
00434 else:
00435 raise MTException("unknown packet: 0x%04X."%data_id)
00436 return o
00437 def parse_orientation_data(data_id, content, ffmt):
00438 o = {}
00439 if (data_id&0x00F0) == 0x10:
00440 o['Q0'], o['Q1'], o['Q2'], o['Q3'] = struct.unpack('!'+4*ffmt,
00441 content)
00442 elif (data_id&0x00F0) == 0x20:
00443 o['a'], o['b'], o['c'], o['d'], o['e'], o['f'], o['g'], o['h'],\
00444 o['i'] = struct.unpack('!'+9*ffmt, content)
00445 elif (data_id&0x00F0) == 0x30:
00446 o['Roll'], o['Pitch'], o['Yaw'] = struct.unpack('!'+3*ffmt,
00447 content)
00448 else:
00449 raise MTException("unknown packet: 0x%04X."%data_id)
00450 return o
00451 def parse_pressure(data_id, content, ffmt):
00452 o = {}
00453 if (data_id&0x00F0) == 0x10:
00454
00455 o['Pressure'], = struct.unpack('!L', content)
00456 else:
00457 raise MTException("unknown packet: 0x%04X."%data_id)
00458 return o
00459 def parse_acceleration(data_id, content, ffmt):
00460 o = {}
00461 if (data_id&0x00F0) == 0x10:
00462 o['Delta v.x'], o['Delta v.y'], o['Delta v.z'] = \
00463 struct.unpack('!'+3*ffmt, content)
00464 elif (data_id&0x00F0) == 0x20:
00465 o['accX'], o['accY'], o['accZ'] = \
00466 struct.unpack('!'+3*ffmt, content)
00467 elif (data_id&0x00F0) == 0x30:
00468 o['freeAccX'], o['freeAccY'], o['freeAccZ'] = \
00469 struct.unpack('!'+3*ffmt, content)
00470 else:
00471 raise MTException("unknown packet: 0x%04X."%data_id)
00472 return o
00473 def parse_position(data_id, content, ffmt):
00474 o = {}
00475 if (data_id&0x00F0) == 0x10:
00476 o['altMsl'], = struct.unpack('!'+ffmt, content)
00477 elif (data_id&0x00F0) == 0x20:
00478 o['altEllipsoid'], = struct.unpack('!'+ffmt, content)
00479 elif (data_id&0x00F0) == 0x30:
00480 o['ecefX'], o['ecefY'], o['ecefZ'] = \
00481 struct.unpack('!'+3*ffmt, content)
00482 elif (data_id&0x00F0) == 0x40:
00483 o['lat'], o['lon'] = struct.unpack('!'+2*ffmt, content)
00484 else:
00485 raise MTException("unknown packet: 0x%04X."%data_id)
00486 return o
00487 def parse_angular_velocity(data_id, content, ffmt):
00488 o = {}
00489
00490 if (data_id&0x00F0) == 0x20:
00491 o['gyrX'], o['gyrY'], o['gyrZ'] = \
00492 struct.unpack('!'+3*ffmt, content)
00493 elif (data_id&0x00F0) == 0x30:
00494 o['Delta q0'], o['Delta q1'], o['Delta q2'], o['Delta q3'] = \
00495 struct.unpack('!'+4*ffmt, content)
00496 else:
00497 raise MTException("unknown packet: 0x%04X."%data_id)
00498 return o
00499 def parse_GPS(data_id, content, ffmt):
00500 o = {}
00501 if (data_id&0x00F0) == 0x30:
00502 o['iTOW'], g, p, t, v, h, n, e = \
00503 struct.unpack('!LHHHHHHH', content)
00504 o['gDOP'], o['pDOP'], o['tDOP'], o['vDOP'], o['hDOP'], \
00505 o['nDOP'], o['eDOP'] = 0.01*g, 0.01*p, 0.01*t, \
00506 0.01*v, 0.01*h, 0.01*n, 0.01*e
00507 elif (data_id&0x00F0) == 0x40:
00508 o['iTOW'], o['fTOW'], o['Week'], o['gpsFix'], o['Flags'], \
00509 o['ecefX'], o['ecefY'], o['ecefZ'], o['pAcc'], \
00510 o['ecefVX'], o['ecefVY'], o['ecefVZ'], o['sAcc'], \
00511 o['pDOP'], o['numSV'] = \
00512 struct.unpack('!LlhBBlllLlllLHxBx', content)
00513 elif (data_id&0x00F0) == 0x80:
00514 o['iTOW'], o['tAcc'], o['nano'], o['year'], o['month'], \
00515 o['day'], o['hour'], o['min'], o['sec'], o['valid'] = \
00516 struct.unpack('!LLlHBBBBBB', content)
00517 elif (data_id&0x00F0) == 0xA0:
00518 o['iTOW'], o['numCh'] = struct.unpack('!LBxx', content[:8])
00519 channels = []
00520 ch = {}
00521 for i in range(o['numCh']):
00522 ch['chn'], ch['svid'], ch['flags'], ch['quality'], \
00523 ch['cno'], ch['elev'], ch['azim'], ch['prRes'] = \
00524 struct.unpack('!BBBBBbhl', content[8+12*i:20+12*i])
00525 channels.append(ch)
00526 o['channels'] = channels
00527 else:
00528 raise MTException("unknown packet: 0x%04X."%data_id)
00529 return o
00530 def parse_SCR(data_id, content, ffmt):
00531 o = {}
00532 if (data_id&0x00F0) == 0x10:
00533 o['accX'], o['accY'], o['accZ'], o['gyrX'], o['gyrY'], \
00534 o['gyrZ'], o['magX'], o['magY'], o['magZ'], o['Temp']=\
00535 struct.unpack("!9Hh", content)
00536 elif (data_id&0x00F0) == 0x20:
00537 o['tempGyrX'], o['tempGyrY'], o['tempGyrZ'] = \
00538 struct.unpack("!hhh", content)
00539 else:
00540 raise MTException("unknown packet: 0x%04X."%data_id)
00541 return o
00542 def parse_analog_in(data_id, content, ffmt):
00543 o = {}
00544 if (data_id&0x00F0) == 0x10:
00545 o['analogIn1'], = struct.unpack("!H", content)
00546 elif (data_id&0x00F0) == 0x20:
00547 o['analogIn2'], = struct.unpack("!H", content)
00548 else:
00549 raise MTException("unknown packet: 0x%04X."%data_id)
00550 return o
00551 def parse_magnetic(data_id, content, ffmt):
00552 o = {}
00553 if (data_id&0x00F0) == 0x20:
00554 o['magX'], o['magY'], o['magZ'] = \
00555 struct.unpack("!3"+ffmt, content)
00556 else:
00557 raise MTException("unknown packet: 0x%04X."%data_id)
00558 return o
00559 def parse_velocity(data_id, content, ffmt):
00560 o = {}
00561 if (data_id&0x00F0) == 0x10:
00562 o['velX'], o['velY'], o['velZ'] = \
00563 struct.unpack("!3"+ffmt, content)
00564 else:
00565 raise MTException("unknown packet: 0x%04X."%data_id)
00566 return o
00567 def parse_status(data_id, content, ffmt):
00568 o = {}
00569 if (data_id&0x00F0) == 0x10:
00570 o['StatusByte'], = struct.unpack("!B", content)
00571 elif (data_id&0x00F0) == 0x20:
00572 o['StatusWord'], = struct.unpack("!L", content)
00573 elif (data_id&0x00F0) == 0x40:
00574 o['RSSI'], = struct.unpack("!b", content)
00575 else:
00576 raise MTException("unknown packet: 0x%04X."%data_id)
00577 return o
00578
00579
00580 output = {}
00581 while data:
00582 try:
00583 data_id, size = struct.unpack('!HB', data[:3])
00584 if (data_id&0x0003) == 0x3:
00585 float_format = 'd'
00586 elif (data_id&0x0003) == 0x0:
00587 float_format = 'f'
00588 else:
00589 raise MTException("fixed point precision not supported.")
00590 content = data[3:3+size]
00591 data = data[3+size:]
00592 group = data_id&0xFF00
00593 ffmt = float_format
00594 if group == XDIGroup.Temperature:
00595 output['Temperature'] = parse_temperature(data_id, content, ffmt)
00596 elif group == XDIGroup.Timestamp:
00597 output['Timestamp'] = parse_timestamp(data_id, content, ffmt)
00598 elif group == XDIGroup.OrientationData:
00599 output['Orientation Data'] = parse_orientation_data(data_id, content, ffmt)
00600 elif group == XDIGroup.Pressure:
00601 output['Pressure'] = parse_pressure(data_id, content, ffmt)
00602 elif group == XDIGroup.Acceleration:
00603 output['Acceleration'] = parse_acceleration(data_id, content, ffmt)
00604 elif group == XDIGroup.Position:
00605 output['Position'] = parse_position(data_id, content, ffmt)
00606 elif group == XDIGroup.AngularVelocity:
00607 output['Angular Velocity'] = parse_angular_velocity(data_id, content, ffmt)
00608 elif group == XDIGroup.GPS:
00609 output['GPS'] = parse_GPS(data_id, content, ffmt)
00610 elif group == XDIGroup.SensorComponentReadout:
00611 output['SCR'] = parse_SCR(data_id, content, ffmt)
00612 elif group == XDIGroup.AnalogIn:
00613 output['Analog In'] = parse_analog_in(data_id, content, ffmt)
00614 elif group == XDIGroup.Magnetic:
00615 output['Magnetic'] = parse_magnetic(data_id, content, ffmt)
00616 elif group == XDIGroup.Velocity:
00617 output['Velocity'] = parse_velocity(data_id, content, ffmt)
00618 elif group == XDIGroup.Status:
00619 output['Status'] = parse_status(data_id, content, ffmt)
00620 else:
00621 raise MTException("unknown XDI group: 0x%04X."%group)
00622 except struct.error, e:
00623 raise MTException("couldn't parse MTData2 message.")
00624 return output
00625
00626
00627 def parse_MTData(self, data, mode=None, settings=None):
00628 """Read and parse a measurement packet."""
00629
00630 if mode is None:
00631 mode = self.mode
00632 if settings is None:
00633 settings = self.settings
00634
00635 output = {}
00636 try:
00637
00638 if mode & OutputMode.RAW:
00639 o = {}
00640 o['accX'], o['accY'], o['accZ'], o['gyrX'], o['gyrY'], o['gyrZ'],\
00641 o['magX'], o['magY'], o['magZ'], o['temp'] =\
00642 struct.unpack('!10H', data[:20])
00643 data = data[20:]
00644 output['RAW'] = o
00645
00646 if mode & OutputMode.RAWGPS:
00647 o = {}
00648 o['Press'], o['bPrs'], o['ITOW'], o['LAT'], o['LON'], o['ALT'],\
00649 o['VEL_N'], o['VEL_E'], o['VEL_D'], o['Hacc'], o['Vacc'],\
00650 o['Sacc'], o['bGPS'] = struct.unpack('!HBI6i3IB', data[:44])
00651 data = data[44:]
00652 output['RAWGPS'] = o
00653
00654 if mode & OutputMode.Temp:
00655 temp, = struct.unpack('!f', data[:4])
00656 data = data[4:]
00657 output['Temp'] = temp
00658
00659 if mode & OutputMode.Calib:
00660 o = {}
00661 if not (settings&OutputSettings.CalibMode_GyrMag):
00662 o['accX'], o['accY'], o['accZ'] = struct.unpack('!3f',\
00663 data[:12])
00664 data = data[12:]
00665 if not (settings&OutputSettings.CalibMode_AccMag):
00666 o['gyrX'], o['gyrY'], o['gyrZ'] = struct.unpack('!3f',\
00667 data[:12])
00668 data = data[12:]
00669 if not (settings&OutputSettings.CalibMode_AccGyr):
00670 o['magX'], o['magY'], o['magZ'] = struct.unpack('!3f',\
00671 data[:12])
00672 data = data[12:]
00673 output['Calib'] = o
00674
00675 if mode & OutputMode.Orient:
00676 o = {}
00677 if settings & OutputSettings.OrientMode_Euler:
00678 o['roll'], o['pitch'], o['yaw'] = struct.unpack('!3f', data[:12])
00679 data = data[12:]
00680 elif settings & OutputSettings.OrientMode_Matrix:
00681 a, b, c, d, e, f, g, h, i = struct.unpack('!9f', data[:36])
00682 data = data[36:]
00683 o['matrix'] = ((a, b, c), (d, e, f), (g, h, i))
00684 else:
00685 q0, q1, q2, q3 = struct.unpack('!4f', data[:16])
00686 data = data[16:]
00687 o['quaternion'] = (q0, q1, q2, q3)
00688 output['Orient'] = o
00689
00690 if mode & OutputMode.Auxiliary:
00691 o = {}
00692 if not (settings&OutputSettings.AuxiliaryMode_NoAIN1):
00693 o['Ain_1'], = struct.unpack('!H', data[:2])
00694 data = data[2:]
00695 if not (settings&OutputSettings.AuxiliaryMode_NoAIN2):
00696 o['Ain_2'], = struct.unpack('!H', data[:2])
00697 data = data[2:]
00698 output['Auxiliary'] = o
00699
00700 if mode & OutputMode.Position:
00701 o = {}
00702 o['Lat'], o['Lon'], o['Alt'] = struct.unpack('!3f', data[:12])
00703 data = data[12:]
00704 output['Pos'] = o
00705
00706 if mode & OutputMode.Velocity:
00707 o = {}
00708 o['Vel_X'], o['Vel_Y'], o['Vel_Z'] = struct.unpack('!3f', data[:12])
00709 data = data[12:]
00710 output['Vel'] = o
00711
00712 if mode & OutputMode.Status:
00713 status, = struct.unpack('!B', data[:1])
00714 data = data[1:]
00715 output['Stat'] = status
00716
00717 if settings & OutputSettings.Timestamp_SampleCnt:
00718 TS, = struct.unpack('!H', data[:2])
00719 data = data[2:]
00720 output['Sample'] = TS
00721 except struct.error, e:
00722 raise MTException("could not parse MTData message.")
00723 if data <> '':
00724 raise MTException("could not parse MTData message (too long).")
00725 return output
00726
00727
00728
00729 def ChangeBaudrate(self, baudrate):
00730 """Change the baudrate, reset the device and reopen communication."""
00731 self.GoToConfig()
00732 brid = Baudrates.get_BRID(baudrate)
00733 self.SetBaudrate(brid)
00734 self.Reset()
00735
00736 self.device.baudrate=baudrate
00737
00738 time.sleep(0.01)
00739 self.read_msg()
00740 self.write_msg(0x3f)
00741
00742
00743
00744
00745
00746
00747
00748 def find_devices():
00749 mtdev_list = []
00750 for port in glob.glob("/dev/tty*S*"):
00751 try:
00752 br = find_baudrate(port)
00753 if br:
00754 mtdev_list.append((port, br))
00755 except MTException:
00756 pass
00757 return mtdev_list
00758
00759
00760
00761
00762
00763 def find_baudrate(port):
00764 baudrates = (115200, 460800, 921600, 230400, 57600, 38400, 19200, 9600)
00765 for br in baudrates:
00766 try:
00767 mt = MTDevice(port, br)
00768 except serial.SerialException:
00769 raise MTException("unable to open %s"%port)
00770 try:
00771 mt.GoToConfig()
00772 mt.GoToMeasurement()
00773 return br
00774 except MTException:
00775 pass
00776
00777
00778
00779
00780
00781
00782 def usage():
00783 print """MT device driver.
00784 Usage:
00785 ./mtdevice.py [commands] [opts]
00786
00787 Commands:
00788 -h, --help
00789 Print this help and quit.
00790 -r, --reset
00791 Reset device to factory defaults.
00792 -a, --change-baudrate=NEW_BAUD
00793 Change baudrate from BAUD (see below) to NEW_BAUD.
00794 -c, --configure
00795 Configure the device (needs MODE and SETTINGS arguments below).
00796 -e, --echo
00797 Print MTData. It is the default if no other command is supplied.
00798 -i, --inspect
00799 Print current MT device configuration.
00800 -x, --xkf-scenario=ID
00801 Change the current XKF scenario.
00802
00803
00804 Options:
00805 -d, --device=DEV
00806 Serial interface of the device (default: /dev/ttyUSB0). If 'auto', then
00807 all serial ports are tested at all baudrates and the first
00808 suitable device is used.
00809 -b, --baudrate=BAUD
00810 Baudrate of serial interface (default: 115200). If 0, then all
00811 rates are tried until a suitable one is found.
00812 -m, --output-mode=MODE
00813 Mode of the device selecting the information to output.
00814 This is required for 'configure' command. If it is not present
00815 in 'echo' command, the configuration will be read from the
00816 device.
00817 MODE can be either the mode value in hexadecimal, decimal or
00818 binary form, or a string composed of the following characters
00819 (in any order):
00820 t temperature, [0x0001]
00821 c calibrated data, [0x0002]
00822 o orientation data, [0x0004]
00823 a auxiliary data, [0x0008]
00824 p position data (requires MTi-G), [0x0010]
00825 v velocity data (requires MTi-G), [0x0020]
00826 s status data, [0x0800]
00827 g raw GPS mode (requires MTi-G), [0x1000]
00828 r raw (incompatible with others except raw GPS),
00829 [0x4000]
00830 For example, use "--output-mode=so" to have status and
00831 orientation data.
00832 -s, --output-settings=SETTINGS
00833 Settings of the device.
00834 This is required for 'configure' command. If it is not present
00835 in 'echo' command, the configuration will be read from the
00836 device.
00837 SETTINGS can be either the settings value in hexadecimal,
00838 decimal or binary form, or a string composed of the following
00839 characters (in any order):
00840 t sample count (excludes 'n')
00841 n no sample count (excludes 't')
00842 q orientation in quaternion (excludes 'e' and 'm')
00843 e orientation in Euler angles (excludes 'm' and
00844 'q')
00845 m orientation in matrix (excludes 'q' and 'e')
00846 A acceleration in calibrated data
00847 G rate of turn in calibrated data
00848 M magnetic field in calibrated data
00849 i only analog input 1 (excludes 'j')
00850 j only analog input 2 (excludes 'i')
00851 N North-East-Down instead of default: X North Z up
00852 For example, use "--output-settings=tqMAG" for all calibrated
00853 data, sample counter and orientation in quaternion.
00854 -p, --period=PERIOD
00855 Sampling period in (1/115200) seconds (default: 1152).
00856 Minimum is 225 (1.95 ms, 512 Hz), maximum is 1152
00857 (10.0 ms, 100 Hz).
00858 Note that it is the period at which sampling occurs, not the
00859 period at which messages are sent (see below).
00860 -f, --skip-factor=SKIPFACTOR
00861 Number of samples to skip before sending MTData message
00862 (default: 0).
00863 The frequency at which MTData message is send is:
00864 115200/(PERIOD * (SKIPFACTOR + 1))
00865 If the value is 0xffff, no data is send unless a ReqData request
00866 is made.
00867 """
00868
00869
00870
00871
00872
00873 def main():
00874
00875 shopts = 'hra:ceid:b:m:s:p:f:x:'
00876 lopts = ['help', 'reset', 'change-baudrate=', 'configure', 'echo',
00877 'inspect', 'device=', 'baudrate=', 'output-mode=',
00878 'output-settings=', 'period=', 'skip-factor=', 'xkf-scenario=']
00879 try:
00880 opts, args = getopt.gnu_getopt(sys.argv[1:], shopts, lopts)
00881 except getopt.GetoptError, e:
00882 print e
00883 usage()
00884 return 1
00885
00886 device = '/dev/ttyUSB0'
00887 baudrate = 115200
00888 mode = None
00889 settings = None
00890 period = None
00891 skipfactor = None
00892 new_baudrate = None
00893 new_xkf = None
00894 actions = []
00895
00896 for o, a in opts:
00897 if o in ('-h', '--help'):
00898 usage()
00899 return
00900 if o in ('-r', '--reset'):
00901 actions.append('reset')
00902 if o in ('-a', '--change-baudrate'):
00903 try:
00904 new_baudrate = int(a)
00905 except ValueError:
00906 print "change-baudrate argument must be integer."
00907 return 1
00908 actions.append('change-baudrate')
00909 if o in ('-c', '--configure'):
00910 actions.append('configure')
00911 if o in ('-e', '--echo'):
00912 actions.append('echo')
00913 if o in ('-i', '--inspect'):
00914 actions.append('inspect')
00915 if o in ('-x', '--xkf-scenario'):
00916 try:
00917 new_xkf = int(a)
00918 except ValueError:
00919 print "xkf-scenario argument must be integer."
00920 return 1
00921 actions.append('xkf-scenario')
00922 if o in ('-d', '--device'):
00923 device = a
00924 if o in ('-b', '--baudrate'):
00925 try:
00926 baudrate = int(a)
00927 except ValueError:
00928 print "baudrate argument must be integer."
00929 return 1
00930 if o in ('-m', '--output-mode'):
00931 mode = get_mode(a)
00932 if mode is None:
00933 return 1
00934 if o in ('-s', '--output-settings'):
00935 settings = get_settings(a)
00936 if settings is None:
00937 return 1
00938 if o in ('-p', '--period'):
00939 try:
00940 period = int(a)
00941 except ValueError:
00942 print "period argument must be integer."
00943 return 1
00944 if o in ('-f', '--skip-factor'):
00945 try:
00946 skipfactor = int(a)
00947 except ValueError:
00948 print "skip-factor argument must be integer."
00949 return 1
00950
00951 if len(actions) == 0:
00952 actions.append('echo')
00953 try:
00954 if device=='auto':
00955 devs = find_devices()
00956 if devs:
00957 print "Detected devices:","".join('\n\t%s @ %d'%(d,p) for d,p in
00958 devs)
00959 print "Using %s @ %d"%devs[0]
00960 device, baudrate = devs[0]
00961 else:
00962 print "No suitable device found."
00963 return 1
00964
00965 if not baudrate:
00966 baudrate = find_baudrate(device)
00967 if not baudrate:
00968 print "No suitable baudrate found."
00969 return 1
00970
00971 try:
00972 mt = MTDevice(device, baudrate)
00973 except serial.SerialException:
00974 raise MTException("unable to open %s"%device)
00975
00976 if 'inspect' in actions:
00977 mt.GoToConfig()
00978 print "Device: %s at %d Bd:"%(device, baudrate)
00979 print "General configuration:", mt.ReqConfiguration()
00980 print "Available scenarios:", mt.ReqAvailableScenarios()
00981 print "Current scenario: %s (id: %d)"%mt.ReqCurrentScenario()[::-1]
00982 mt.GoToMeasurement()
00983 if 'change-baudrate' in actions:
00984 print "Changing baudrate from %d to %d:"%(baudrate, new_baudrate),
00985 sys.stdout.flush()
00986 mt.ChangeBaudrate(new_baudrate)
00987 print " Ok"
00988 if 'reset' in actions:
00989 print "Restoring factory defaults",
00990 sys.stdout.flush()
00991 mt.RestoreFactoryDefaults()
00992 print " Ok"
00993 if 'configure' in actions:
00994 if mode is None:
00995 print "output-mode is require to configure the device."
00996 return 1
00997 if settings is None:
00998 print "output-settings is required to configure the device."
00999 return 1
01000 print "Configuring mode and settings",
01001 sys.stdout.flush()
01002 mt.configure(mode, settings, period, skipfactor)
01003 print " Ok"
01004 if 'xkf-scenario' in actions:
01005 print "Changing XKF scenario",
01006 sys.stdout.flush()
01007 mt.GoToConfig()
01008 mt.SetCurrentScenario(new_xkf)
01009 mt.GoToMeasurement()
01010 print "Ok"
01011 if 'echo' in actions:
01012
01013
01014
01015 try:
01016 while True:
01017 print mt.read_measurement(mode, settings)
01018 except KeyboardInterrupt:
01019 pass
01020 except MTException as e:
01021
01022 print e
01023
01024
01025
01026 def get_mode(arg):
01027 """Parse command line output-mode argument."""
01028 try:
01029 mode = int(arg)
01030 return mode
01031 except ValueError:
01032 pass
01033 if arg[0]=='0':
01034 try:
01035 mode = int(arg, 2)
01036 return mode
01037 except ValueError:
01038 pass
01039 try:
01040 mode = int(arg, 16)
01041 return mode
01042 except ValueError:
01043 pass
01044
01045 mode = 0
01046 for c in arg:
01047 if c=='t':
01048 mode |= OutputMode.Temp
01049 elif c=='c':
01050 mode |= OutputMode.Calib
01051 elif c=='o':
01052 mode |= OutputMode.Orient
01053 elif c=='a':
01054 mode |= OutputMode.Auxiliary
01055 elif c=='p':
01056 mode |= OutputMode.Position
01057 elif c=='v':
01058 mode |= OutputMode.Velocity
01059 elif c=='s':
01060 mode |= OutputMode.Status
01061 elif c=='g':
01062 mode |= OutputMode.RAWGPS
01063 elif c=='r':
01064 mode |= OutputMode.RAW
01065 else:
01066 print "Unknown output-mode specifier: '%s'"%c
01067 return
01068 return mode
01069
01070 def get_settings(arg):
01071 """Parse command line output-settings argument."""
01072 try:
01073 settings = int(arg)
01074 return settings
01075 except ValueError:
01076 pass
01077 if arg[0]=='0':
01078 try:
01079 settings = int(arg, 2)
01080 return settings
01081 except ValueError:
01082 pass
01083 try:
01084 settings = int(arg, 16)
01085 return settings
01086 except ValueError:
01087 pass
01088
01089 timestamp = 0
01090 orient_mode = 0
01091 calib_mode = OutputSettings.CalibMode_Mask
01092 NED = 0
01093 for c in arg:
01094 if c=='t':
01095 timestamp = OutputSettings.Timestamp_SampleCnt
01096 elif c=='n':
01097 timestamp = OutputSettings.Timestamp_None
01098 elif c=='q':
01099 orient_mode = OutputSettings.OrientMode_Quaternion
01100 elif c=='e':
01101 orient_mode = OutputSettings.OrientMode_Euler
01102 elif c=='m':
01103 orient_mode = OutputSettings.OrientMode_Matrix
01104 elif c=='A':
01105 calib_mode &= OutputSettings.CalibMode_Acc
01106 elif c=='G':
01107 calib_mode &= OutputSettings.CalibMode_Gyr
01108 elif c=='M':
01109 calib_mode &= OutputSettings.CalibMode_Mag
01110 elif c=='i':
01111 calib_mode &= OutputSettings.AuxiliaryMode_NoAIN2
01112 elif c=='j':
01113 calib_mode &= OutputSettings.AuxiliaryMode_NoAIN1
01114 elif c=='N':
01115 NED = OutputSettings.Coordinates_NED
01116 else:
01117 print "Unknown output-settings specifier: '%s'"%c
01118 return
01119 settings = timestamp|orient_mode|calib_mode|NED
01120 return settings
01121
01122
01123 if __name__=='__main__':
01124 main()
01125