mtdevice.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 import serial
00003 import struct
00004 
00005 import sys, getopt, time, glob#, traceback
00006 
00007 from mtdef import MID, OutputMode, OutputSettings, MTException, Baudrates, XDIGroup, getName, getMIDName
00008 
00009 # Verbose flag for debugging
00010 verbose = False
00011 
00012 ################################################################
00013 # MTDevice class
00014 ################################################################
00015 ## XSens MT device communication object.
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                 ## serial interface to the device
00023                 self.device = serial.Serial(port, baudrate, timeout=timeout,
00024                                 writeTimeout=timeout)
00025                 self.device.flushInput()        # flush to make sure the port is ready TODO
00026                 self.device.flushOutput()       # flush to make sure the port is ready TODO
00027                 ## timeout for communication
00028                 self.timeout = 100*timeout
00029                 if autoconf:
00030                         self.auto_config()
00031                 else:
00032                         ## mode parameter of the IMU
00033                         self.mode = None
00034                         ## settings parameter of the IMU
00035                         self.settings = None
00036                         ## length of the MTData message
00037                         self.length = None
00038                         ## header of the MTData message
00039                         self.header = None
00040                 if config_mode:
00041                         self.GoToConfig()
00042 
00043         ############################################################
00044         # Low-level communication
00045         ############################################################
00046 
00047         ## Low-level message sending function.
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                         #print ".",
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         ## Low-level MTData receiving function.
00068         # Take advantage of known message length.
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:    # not found
00082                                 # discard unexploitable data
00083                                 #sys.stderr.write("MT: discarding (no preamble).\n")
00084                                 del buf[:-3]
00085                                 continue
00086                         elif preamble_ind:      # found but not at start
00087                                 # discard leading bytes
00088                                 #sys.stderr.write("MT: discarding (before preamble).\n")
00089                                 del buf[:preamble_ind]
00090                                 # complete message for checksum
00091                                 while len(buf)<totlength:
00092                                         buf.extend(self.device.read(totlength-len(buf)))
00093                         if 0xFF&sum(buf[1:]):
00094                                 #sys.stderr.write("MT: invalid checksum; discarding data and "\
00095                                 #               "waiting for next message.\n")
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         ## Low-level message receiving function.
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                         # Makes sure the buffer has 'size' bytes.
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                         # second part of preamble
00125                         waitfor(3)
00126                         if ord(self.device.read())<>0xFF:       # we assume no timeout anymore
00127                                 continue
00128                         # read message id and length of message
00129                         #msg = self.device.read(2)
00130                         mid, length = struct.unpack('!BB', self.device.read(2))
00131                         if length==255: # extended length
00132                                 waitfor(2)
00133                                 length, = struct.unpack('!H', self.device.read(2))
00134                         # read contents and checksum
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         ## Send a message and read confirmation
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         # High-level functions
00175         ############################################################
00176         ## Reset MT device.
00177         def Reset(self):
00178                 """Reset MT device."""
00179                 self.write_ack(MID.Reset)
00180 
00181 
00182         ## Place MT device in configuration mode.
00183         def GoToConfig(self):
00184                 """Place MT device in configuration mode."""
00185                 self.write_ack(MID.GoToConfig)
00186 
00187 
00188         ## Place MT device in measurement mode.
00189         def GoToMeasurement(self):
00190                 """Place MT device in measurement mode."""
00191                 self.write_ack(MID.GoToMeasurement)
00192 
00193 
00194         ## Restore MT device configuration to factory defaults (soft version).
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         ## Get current output mode.
00203         # Assume the device is in Config state.
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         ## Select which information to output.
00213         # Assume the device is in Config state.
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         ## Get current output mode.
00223         # Assume the device is in Config state.
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         ## Select how to output the information.
00233         # Assume the device is in Config state.
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         ## Set the period of sampling.
00244         # Assume the device is in Config state.
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         ## Set the output skip factor.
00253         # Assume the device is in Config state.
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         ## Get data length.
00262         # Assume the device is in Config state.
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         ## Ask for the current configuration of the MT device.
00273         # Assume the device is in Config state.
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         ## Set the baudrate of the device using the baudrate id.
00302         # Assume the device is in Config state.
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         ## Request the available XKF scenarios on the device.
00310         # Assume the device is in Config state.
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                         ## available XKF scenarios
00322                         self.scenarios = scenarios
00323                 except struct.error:
00324                         raise MTException("could not parse the available XKF scenarios.")
00325                 return scenarios
00326 
00327 
00328         ## Request the ID of the currently used XKF scenario.
00329         # Assume the device is in Config state.
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                 ## current XKF id
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                                 ## current XKF label
00343                                 self.scenario_label = label
00344                                 break
00345                 else:
00346                         self.scenario_label = ""
00347                 return self.scenario_id, self.scenario_label
00348 
00349 
00350         ## Sets the XKF scenario to use.
00351         # Assume the device is in Config state.
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         # High-level utility functions
00360         ############################################################
00361         ## Configure the mode and settings of the MT device.
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         ## Read configuration from device.
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                 #self.GoToConfig()
00386                 #mode = self.GetOutputMode()
00387                 #settings = self.GetOutputSettings()
00388                 #length = self.ReqDataLength()
00389                 #self.GoToMeasurement()
00390                 #return mode, settings, length
00391 
00392         ## Read and parse a measurement packet
00393         def read_measurement(self, mode=None, settings=None):
00394                 # getting data
00395                 #data = self.read_data_msg()
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         ## Parse a new MTData2 message
00405         def parse_MTData2(self, data):
00406                 # Functions to parse each type of packet
00407                 def parse_temperature(data_id, content, ffmt):
00408                         o = {}
00409                         if (data_id&0x00F0) == 0x10:    # Temperature
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:    # UTC Time
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:  # Packet Counter
00421                                 o['PacketCounter'], = struct.unpack('!H', content)
00422                         elif (data_id&0x00F0) == 0x30:  # Integer Time of Week
00423                                 o['TimeOfWeek'], = struct.unpack('!L', content)
00424                         elif (data_id&0x00F0) == 0x40:  # GPS Age
00425                                 o['gpsAge'], = struct.unpack('!B', content)
00426                         elif (data_id&0x00F0) == 0x50:  # Pressure Age
00427                                 o['pressureAge'], = struct.unpack('!B', content)
00428                         elif (data_id&0x00F0) == 0x60:  # Sample Time Fine
00429                                 o['SampleTimeFine'], = struct.unpack('!L', content)
00430                         elif (data_id&0x00F0) == 0x70:  # Sample Time Coarse
00431                                 o['SampleTimeCoarse'], = struct.unpack('!L', content)
00432                         elif (data_id&0x00F0) == 0x80:  # Frame Range
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:    # Quaternion
00440                                 o['Q0'], o['Q1'], o['Q2'], o['Q3'] = struct.unpack('!'+4*ffmt,
00441                                                 content)
00442                         elif (data_id&0x00F0) == 0x20:  # Rotation Matrix
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:  # Euler Angles
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:    # Baro pressure
00454                                 # FIXME is it really U4 as in the doc and not a float/double?
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:    # Delta V
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:  # Acceleration
00465                                 o['accX'], o['accY'], o['accZ'] = \
00466                                                 struct.unpack('!'+3*ffmt, content)
00467                         elif (data_id&0x00F0) == 0x30:  # Free Acceleration
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:    # Altitude MSL
00476                                 o['altMsl'], = struct.unpack('!'+ffmt, content)
00477                         elif (data_id&0x00F0) == 0x20:  # Altitude Ellipsoid
00478                                 o['altEllipsoid'], = struct.unpack('!'+ffmt, content)
00479                         elif (data_id&0x00F0) == 0x30:  # Position ECEF
00480                                 o['ecefX'], o['ecefY'], o['ecefZ'] = \
00481                                                 struct.unpack('!'+3*ffmt, content)
00482                         elif (data_id&0x00F0) == 0x40:  # LatLon
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                         # FIXME is it really 802y and 803y as in the doc?
00490                         if (data_id&0x00F0) == 0x20:    # Rate of Turn
00491                                 o['gyrX'], o['gyrY'], o['gyrZ'] = \
00492                                                 struct.unpack('!'+3*ffmt, content)
00493                         elif (data_id&0x00F0) == 0x30:  # Delta Q
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:    # DOP
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:  # SOL
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:  # Time UTC
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:  # SV Info
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:    # ACC+GYR+MAG+Temperature
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:  # Gyro Temperature
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:    # Analog In 1
00545                                 o['analogIn1'], = struct.unpack("!H", content)
00546                         elif (data_id&0x00F0) == 0x20:  # Analog In 2
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:    # Magnetic Field
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:    # Velocity XYZ
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:    # Status Byte
00570                                 o['StatusByte'], = struct.unpack("!B", content)
00571                         elif (data_id&0x00F0) == 0x20:  # Status Word
00572                                 o['StatusWord'], = struct.unpack("!L", content)
00573                         elif (data_id&0x00F0) == 0x40:  # RSSI
00574                                 o['RSSI'], = struct.unpack("!b", content)
00575                         else:
00576                                 raise MTException("unknown packet: 0x%04X."%data_id)
00577                         return o
00578 
00579                 # data object
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         ## Parse a legacy MTData message
00627         def parse_MTData(self, data, mode=None, settings=None):
00628                 """Read and parse a measurement packet."""
00629                 # getting mode
00630                 if mode is None:
00631                         mode = self.mode
00632                 if settings is None:
00633                         settings = self.settings
00634                 # data object
00635                 output = {}
00636                 try:
00637                         # raw IMU first
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                         # raw GPS second
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                         # temperature
00654                         if mode & OutputMode.Temp:
00655                                 temp, = struct.unpack('!f', data[:4])
00656                                 data = data[4:]
00657                                 output['Temp'] = temp
00658                         # calibrated data
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                         # orientation
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: # OutputSettings.OrientMode_Quaternion:
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                         # auxiliary
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                         # position
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                         # velocity
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                         # status
00712                         if mode & OutputMode.Status:
00713                                 status, = struct.unpack('!B', data[:1])
00714                                 data = data[1:]
00715                                 output['Stat'] = status
00716                         # sample counter
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         ## Change the baudrate, reset the device and reopen communication.
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                 #self.device.flush()
00736                 self.device.baudrate=baudrate
00737                 #self.device.flush()
00738                 time.sleep(0.01)
00739                 self.read_msg()
00740                 self.write_msg(0x3f)
00741 
00742 
00743 
00744 
00745 ################################################################
00746 # Auto detect port
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 # Auto detect baudrate
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 # Documentation for stand alone usage
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 # Main function
00872 ################################################################
00873 def main():
00874         # parse command line
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         # default values
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         # filling in arguments
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         # if nothing else: echo
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                 # find baudrate
00965                 if not baudrate:
00966                         baudrate = find_baudrate(device)
00967                 if not baudrate:
00968                         print "No suitable baudrate found."
00969                         return 1
00970                 # open device
00971                 try:
00972                         mt = MTDevice(device, baudrate)
00973                 except serial.SerialException:
00974                         raise MTException("unable to open %s"%device)
00975                 # execute actions
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"             # should we test it was actually ok?
00988                 if 'reset' in actions:
00989                         print "Restoring factory defaults",
00990                         sys.stdout.flush()
00991                         mt.RestoreFactoryDefaults()
00992                         print " Ok"             # should we test it was actually 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"             # should we test it was actually 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 #                       if (mode is None) or (settings is None):
01013 #                               mode, settings, length = mt.auto_config()
01014 #                               print mode, settings, length
01015                         try:
01016                                 while True:
01017                                         print mt.read_measurement(mode, settings)
01018                         except KeyboardInterrupt:
01019                                 pass
01020         except MTException as e:
01021                 #traceback.print_tb(sys.exc_info()[2])
01022                 print e
01023 
01024 
01025 
01026 def get_mode(arg):
01027         """Parse command line output-mode argument."""
01028         try:    # decimal
01029                 mode = int(arg)
01030                 return mode
01031         except ValueError:
01032                 pass
01033         if arg[0]=='0':
01034                 try:    # binary
01035                         mode = int(arg, 2)
01036                         return mode
01037                 except ValueError:
01038                         pass
01039                 try:    # hexadecimal
01040                         mode = int(arg, 16)
01041                         return mode
01042                 except ValueError:
01043                         pass
01044         # string mode specification
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:    # decimal
01073                 settings = int(arg)
01074                 return settings
01075         except ValueError:
01076                 pass
01077         if arg[0]=='0':
01078                 try:    # binary
01079                         settings = int(arg, 2)
01080                         return settings
01081                 except ValueError:
01082                         pass
01083                 try:    # hexadecimal
01084                         settings = int(arg, 16)
01085                         return settings
01086                 except ValueError:
01087                         pass
01088         # strings settings specification
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 


ethzasl_xsens_driver
Author(s):
autogenerated on Sun Oct 5 2014 23:52:44