text: """
  Name: u6.py
  Desc: Defines the U6 class, which makes working with a U6 much easier. All of
        the low-level functions for the U6 are implemented as functions of the U6
        class. There are also a handful additional functions which improve upon
        the interface provided by the low-level functions.
  
  To learn about the low-level functions, please see Section 5.2 of the U6 User's
  Guide:
  
  http://labjack.com/support/u6/users-guide/5.2
  """
  import collections
  import sys
  import warnings
  
  try:
      import ConfigParser
  except ImportError:  # Python 3
      import configparser as ConfigParser
  
  from struct import pack, unpack
  
  from LabJackPython import (
      Device,
      deviceCount,
      LabJackException,
      LowlevelErrorException,
      lowlevelErrorToString,
      MAX_USB_PACKET_LENGTH,
      setChecksum8,
      toDouble,
      )
  
  
  def openAllU6():
      """
      A helpful function which will open all the connected U6s. Returns a
      dictionary where the keys are the serialNumber, and the value is the device
      object.
      """
      returnDict = dict()
  
      for i in range(deviceCount(6)):
          d = U6(firstFound = False, devNumber = i+1)
          returnDict[str(d.serialNumber)] = d
  
      return returnDict
  
  def dumpPacket(buffer):
      """
      Name: dumpPacket(buffer)
      Args: byte array
      Desc: Returns hex value of all bytes in the buffer
      """
      return repr([ hex(x) for x in buffer ])
  
  def getBit(n, bit):
      """
      Name: getBit(n, bit)
      Args: n, the original integer you want the bit of
            bit, the index of the bit you want
      Desc: Returns the bit at position "bit" of integer "n"
  
      >>> n = 5
      >>> bit = 2
      >>> getBit(n, bit)
      1
      >>> bit = 0
      >>> getBit(n, bit)
      1
  
      """
      return int(bool((int(n) & (1 << bit)) >> bit))
  
  def toBitList(inbyte):
      """
      Name: toBitList(inbyte)
      Args: a byte
      Desc: Converts a byte into list for access to individual bits
      
      >>> inbyte = 5
      >>> toBitList(inbyte)
      [1, 0, 1, 0, 0, 0, 0, 0]
      
      """
      return [ getBit(inbyte, b) for b in range(8) ]
  
  def dictAsString(d):
      """Helper function that returns a string representation of a dictionary"""
      s = "{"
      for key, val in sorted(d.items()):
          s += "%s: %s, " % (key, val)
      s = s.rstrip(", ")  # Nuke the trailing comma
      s += "}"
      return s
  
  
  class CalibrationInfo(object):
      """ A class to hold the calibration info for a U6 """
      def __init__(self):
          # A flag to tell difference between nominal and actual values.
          self.nominal = True
      
          # Positive Channel calibration
          self.ain10vSlope = 3.1580578 * (10 ** -4)
          self.ain10vOffset = -10.5869565220
          self.ain1vSlope = 3.1580578 * (10 ** -5)
          self.ain1vOffset = -1.05869565220
          self.ain100mvSlope = 3.1580578 * (10 ** -6)
          self.ain100mvOffset = -0.105869565220
          self.ain10mvSlope = 3.1580578 * (10 ** -7)
          self.ain10mvOffset = -0.0105869565220
  
          self.ainSlope = [self.ain10vSlope, self.ain1vSlope, self.ain100mvSlope, self.ain10mvSlope]
          self.ainOffset = [self.ain10vOffset, self.ain1vOffset, self.ain100mvOffset, self.ain10mvOffset]
  
          # Negative Channel calibration
          self.ain10vNegSlope = -3.15805800 * (10 ** -4)
          self.ain10vCenter = 33523.0
          self.ain1vNegSlope = -3.15805800 * (10 ** -5)
          self.ain1vCenter = 33523.0
          self.ain100mvNegSlope = -3.15805800 * (10 ** -6)
          self.ain100mvCenter = 33523.0
          self.ain10mvNegSlope = -3.15805800 * (10 ** -7)
          self.ain10mvCenter = 33523.0
          
          self.ainNegSlope = [self.ain10vNegSlope, self.ain1vNegSlope, self.ain100mvNegSlope, self.ain10mvNegSlope]
          self.ainCenter = [self.ain10vCenter, self.ain1vCenter, self.ain100mvCenter, self.ain10mvCenter]
          
          # Miscellaneous
          self.dac0Slope = 13200.0
          self.dac0Offset = 0
          self.dac1Slope = 13200.0
          self.dac1Offset = 0
          
          self.dacSlope = [self.dac0Slope, self.dac1Slope]
          self.dacOffset = [self.dac0Offset, self.dac1Offset]
  
          self.currentOutput0 = 0.0000100000
          self.currentOutput1 = 0.0002000000
          
          self.temperatureSlope = -92.379
          self.temperatureOffset = 465.129
          
          # Hi-Res ADC stuff
          # Positive Channel calibration
          self.proAin10vSlope = 3.1580578 * (10 ** -4)
          self.proAin10vOffset = -10.5869565220
          self.proAin1vSlope = 3.1580578 * (10 ** -5)
          self.proAin1vOffset = -1.05869565220
          self.proAin100mvSlope = 3.1580578 * (10 ** -6)
          self.proAin100mvOffset = -0.105869565220
          self.proAin10mvSlope = 3.1580578 * (10 ** -7)
          self.proAin10mvOffset = -0.0105869565220
          
          self.proAinSlope = [self.proAin10vSlope, self.proAin1vSlope, self.proAin100mvSlope, self.proAin10mvSlope]
          self.proAinOffset = [self.proAin10vOffset, self.proAin1vOffset, self.proAin100mvOffset, self.proAin10mvOffset]
          
          # Negative Channel calibration
          self.proAin10vNegSlope = -3.15805800 * (10 ** -4)
          self.proAin10vCenter = 33523.0
          self.proAin1vNegSlope = -3.15805800 * (10 ** -5)
          self.proAin1vCenter = 33523.0
          self.proAin100mvNegSlope = -3.15805800 * (10 ** -6)
          self.proAin100mvCenter = 33523.0
          self.proAin10mvNegSlope = -3.15805800 * (10 ** -7)
          self.proAin10mvCenter = 33523.0
          
          self.proAinNegSlope = [self.proAin10vNegSlope, self.proAin1vNegSlope, self.proAin100mvNegSlope, self.proAin10mvNegSlope]
          self.proAinCenter = [self.proAin10vCenter, self.proAin1vCenter, self.proAin100mvCenter, self.proAin10mvCenter]
  
  
      def __str__(self):
          return str(self.__dict__)
  
  
  class U6(Device):
      """
      U6 Class for all U6 specific low-level commands.
      
      Example:
      >>> import u6
      >>> d = u6.U6()
      >>> print(d.configU6())
      {'SerialNumber': 320032102, ... , 'FirmwareVersion': '1.26'}
      """
      def __init__(self, debug = False, autoOpen = True, **kargs):
          """
          Name: U6.__init__(self, debug = False, autoOpen = True, **kargs)
          Args: debug, Do you want debug information?
                autoOpen, If true, then the constructor will call open for you
                **kargs, The arguments to be passed to open.
          Desc: Your basic constructor.
          """
          Device.__init__(self, None, devType = 6)
          self.firmwareVersion = 0
          self.bootloaderVersion = 0
          self.hardwareVersion = 0
          self.productId = 0
          self.fioDirection = [None] * 8
          self.fioState = [None] * 8
          self.eioDirection = [None] * 8
          self.eioState = [None] * 8
          self.cioDirection = [None] * 8
          self.cioState = [None] * 8
          self.dac1Enable = 0
          self.dac0 = 0
          self.dac1 = 0
          self.calInfo = CalibrationInfo()
          self.deviceName = 'U6'
          self.isPro = False
          self.debug = debug
  
          if autoOpen:
              self.open(**kargs)
  
      def open(self, localId = None, firstFound = True, serial = None, devNumber = None, handleOnly = False, LJSocket = None):
          """
          Name: U6.open(localId = None, firstFound = True, devNumber = None,
                        handleOnly = False, LJSocket = None)
          Args: firstFound, If True, use the first found U6
                serial, open a U6 with the given serial number
                localId, open a U6 with the given local id.
                devNumber, open a U6 with the given devNumber
                handleOnly, if True, LabJackPython will only open a handle
                LJSocket, set to "<ip>:<port>" to connect to LJSocket
          Desc: Opens a U6 for reading and writing.
          
          >>> myU6 = u6.U6(autoOpen = False)
          >>> myU6.open()
          """
          Device.open(self, 6, firstFound = firstFound, serial = serial, localId = localId, devNumber = devNumber, handleOnly = handleOnly, LJSocket = LJSocket )
  
      def configU6(self, LocalID = None):
          """
          Name: U6.configU6(LocalID = None)
          Args: LocalID, if set, will write the new value to U6
          Desc: Writes the Local ID, and reads some hardware information.
  
          >>> myU6 = u6.U6()
          >>> myU6.configU6()
          {'BootloaderVersion': '6.15',
           'FirmwareVersion': '0.88',
           'HardwareVersion': '2.0',
           'LocalID': 1,
           'ProductID': 6,
           'SerialNumber': 360005087,
           'VersionInfo': 4}
          """
          command = [ 0 ] * 26
  
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x0A
          command[3] = 0x08
          #command[4]  = Checksum16 (LSB)
          #command[5]  = Checksum16 (MSB)
  
          if LocalID is not None:
              command[6] = (1 << 3)
              command[8] = LocalID
              
          #command[7] = Reserved
  
          #command[9-25] = Reserved 
          try:
              result = self._writeRead(command, 38, [0xF8, 0x10, 0x08])
          except LabJackException:
              e = sys.exc_info()[1]
              if e.errorCode is 4:
                  print("NOTE: ConfigU6 returned an error of 4. This probably means you are using U6 with a *really old* firmware. Please upgrade your U6's firmware as soon as possible.")
                  result = self._writeRead(command, 38, [0xF8, 0x10, 0x08], checkBytes = False)
              else:
                  raise e
  
          self.firmwareVersion = "%s.%02d" % (result[10], result[9])
          self.bootloaderVersion = "%s.%02d" % (result[12], result[11]) 
          self.hardwareVersion = "%s.%02d" % (result[14], result[13])
          self.serialNumber = unpack("<I", pack(">BBBB", *result[15:19]))[0]
          self.productId = unpack("<H", pack(">BB", *result[19:21]))[0]
          self.localId = result[21]
          self.versionInfo = result[37]
          self.deviceName = 'U6'
          self.isPro = False
          if self.versionInfo == 12:
              self.deviceName = 'U6-Pro'
              self.isPro = True
  
          return {'FirmwareVersion': self.firmwareVersion, 'BootloaderVersion': self.bootloaderVersion, 'HardwareVersion': self.hardwareVersion, 'SerialNumber': self.serialNumber, 'ProductID': self.productId, 'LocalID': self.localId, 'VersionInfo': self.versionInfo, 'DeviceName': self.deviceName}
  
      def configIO(self, NumberTimersEnabled = None, EnableCounter1 = None, EnableCounter0 = None, TimerCounterPinOffset = None, EnableUART = None):
          """
          Name: U6.configIO(NumberTimersEnabled = None, EnableCounter1 = None,
                            EnableCounter0 = None, TimerCounterPinOffset = None)
          Args: NumberTimersEnabled, Number of timers to enable
                EnableCounter1, Set to True to enable counter 1, F to disable
                EnableCounter0, Set to True to enable counter 0, F to disable
                TimerCounterPinOffset, where should the timers/counters start
  
                if all args are None, command just reads.
  
          Desc: Writes and reads the current IO configuration.
  
          >>> myU6 = u6.U6()
          >>> myU6.configIO()
          {'Counter0Enabled': False,
           'Counter1Enabled': False,
           'NumberTimersEnabled': 0,
           'TimerCounterPinOffset': 0}
          """
          command = [ 0 ] * 16
  
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x05
          command[3] = 0x0B
          #command[4]  = Checksum16 (LSB)
          #command[5]  = Checksum16 (MSB)
  
          if NumberTimersEnabled is not None:
              command[6] = 1
              command[7] = NumberTimersEnabled
  
          if EnableCounter0 is not None:
              command[6] = 1
  
              if EnableCounter0:
                  command[8] = 1
  
          if EnableCounter1 is not None:
              command[6] = 1
  
              if EnableCounter1:
                  command[8] |= (1 << 1)
  
          if TimerCounterPinOffset is not None:
              command[6] = 1
              command[9] = TimerCounterPinOffset
  
          if EnableUART is not None:
              command[6] |= 1
  
              if EnableUART:
                  command[6] |= (1 << 5)
  
          result = self._writeRead(command, 16, [0xf8, 0x05, 0x0B])
  
          return { 'NumberTimersEnabled' : result[8], 'Counter0Enabled' : bool(result[9] & 1), 'Counter1Enabled' : bool( (result[9] >> 1) & 1), 'TimerCounterPinOffset' : result[10] }
  
      def configTimerClock(self, TimerClockBase = None, TimerClockDivisor = None):
          """
          Name: U6.configTimerClock(TimerClockBase = None,
                                    TimerClockDivisor = None)
          Args: TimerClockBase, which timer base to use
                TimerClockDivisor, set the divisor
                
                if all args are None, command just reads.
                Also, you cannot set the divisor without setting the base.
                
          Desc: Writes and read the timer clock configuration.
          
          >>> myU6 = u6.U6()
          >>> myU6.configTimerClock()
          {'TimerClockDivisor': 256, 'TimerClockBase': 2}
          """
          command = [ 0 ] * 10
          
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x02
          command[3] = 0x0A
          #command[4]  = Checksum16 (LSB)
          #command[5]  = Checksum16 (MSB)
          #command[6]  = Reserved
          #command[7]  = Reserved
          
          if TimerClockBase is not None:
              command[8] = (1 << 7)
              command[8] |= TimerClockBase & 7
          
          if TimerClockDivisor is not None:
              command[9] = TimerClockDivisor
              
          result = self._writeRead(command, 10, [0xF8, 0x2, 0x0A])
          
          divisor = result[9]
          if divisor == 0:
              divisor = 256
          return { 'TimerClockBase' : (result[8] & 7), 'TimerClockDivisor' : divisor }
  
      def _buildBuffer(self, sendBuffer, readLen, commandlist):
          for cmd in commandlist:
              if isinstance(cmd, FeedbackCommand):
                  sendBuffer += cmd.cmdBytes
                  readLen += cmd.readLen
              elif isinstance(cmd, list):
                  sendBuffer, readLen = self._buildBuffer(sendBuffer, readLen, cmd)
          return (sendBuffer, readLen)
                  
      def _buildFeedbackResults(self, rcvBuffer, commandlist, results, i):
          for cmd in commandlist:
              if isinstance(cmd, FeedbackCommand):
                  results.append(cmd.handle(rcvBuffer[i:i+cmd.readLen]))
                  i += cmd.readLen
              elif isinstance(cmd, list):
                  self._buildFeedbackResults(rcvBuffer, cmd, results, i)
          return results
  
      def getFeedback(self, *commandlist):
          """
          Name: U6.getFeedback(commandlist)
          Args: the FeedbackCommands to run
          Desc: Forms the commandlist into a packet, sends it to the U6, and reads
                the response.
          
          >>> myU6 = U6()
          >>> ledCommand = u6.LED(False)
          >>> internalTempCommand = u6.AIN(30, 31, True)
          >>> myU6.getFeedback(ledCommand, internalTempCommand)
          [None, 23200]
  
          OR if you like the list version better:
          
          >>> myU6 = U6()
          >>> ledCommand = u6.LED(False)
          >>> internalTempCommand = u6.AIN(30, 31, True)
          >>> commandList = [ ledCommand, internalTempCommand ]
          >>> myU6.getFeedback(commandList)
          [None, 23200]
          """
          sendBuffer = [0] * 7
          sendBuffer[1] = 0xF8
          readLen = 9
          sendBuffer, readLen = self._buildBuffer(sendBuffer, readLen, commandlist)
  
          if len(sendBuffer) % 2:
              sendBuffer += [0]
          sendBuffer[2] = len(sendBuffer) // 2 - 3
          
          if readLen % 2:
              readLen += 1
              
          if len(sendBuffer) > MAX_USB_PACKET_LENGTH:
              raise LabJackException("ERROR: The feedback command you are attempting to send is bigger than 64 bytes ( %s bytes ). Break your commands up into separate calls to getFeedback()." % len(sendBuffer))
          
          if readLen > MAX_USB_PACKET_LENGTH:
              raise LabJackException("ERROR: The feedback command you are attempting to send would yield a response that is greater than 64 bytes ( %s bytes ). Break your commands up into separate calls to getFeedback()." % readLen)
          
          rcvBuffer = self._writeRead(sendBuffer, readLen, [], checkBytes = False, stream = False, checksum = True)
          
          # Check the response for errors
          try:
              self._checkCommandBytes(rcvBuffer, [0xF8])
          
              if rcvBuffer[3] != 0x00:
                  raise LabJackException("Got incorrect command bytes")
          except LowlevelErrorException:
              if isinstance(commandlist[0], list):
                  culprit = commandlist[0][ (rcvBuffer[7] - 1) ]
              else:
                  culprit = commandlist[ (rcvBuffer[7] -1) ]
              
              raise LowlevelErrorException("\nThis Command\n    %s\nreturned an error:\n    %s" %  ( culprit, lowlevelErrorToString(rcvBuffer[6]) ) )
          
          results = []
          i = 9
          return self._buildFeedbackResults(rcvBuffer, commandlist, results, i)
  
      def readMem(self, BlockNum, ReadCal=False):
          """
          Name: U6.readMem(BlockNum, ReadCal=False)
          Args: BlockNum, which block to read
                ReadCal, set to True to read the calibration data
          Desc: Reads 1 block (32 bytes) from the non-volatile user or 
                calibration memory. Please read section 5.2.6 of the user's
                guide before you do something you may regret.
          
          >>> myU6 = U6()
          >>> myU6.readMem(0)
          [ < userdata stored in block 0 > ]
          
          NOTE: Do not call this function while streaming.
          """
          command = [ 0 ] * 8
          
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x01
          command[3] = 0x2A
          if ReadCal:
              command[3] = 0x2D
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          command[6] = 0x00
          command[7] = BlockNum
          
          result = self._writeRead(command, 40, [ 0xF8, 0x11, command[3] ])
          
          return result[8:]
      
      def readCal(self, BlockNum):
          return self.readMem(BlockNum, ReadCal = True)
          
      def writeMem(self, BlockNum, Data, WriteCal=False):
          """
          Name: U6.writeMem(BlockNum, Data, WriteCal=False)
          Args: BlockNum, which block to write
                Data, a list of bytes to write
                WriteCal, set to True to write calibration.
          Desc: Writes 1 block (32 bytes) from the non-volatile user or 
                calibration memory. Please read section 5.2.7 of the user's
                guide before you do something you may regret.
          
          >>> myU6 = U6()
          >>> myU6.writeMem(0, [ < userdata to be stored in block 0 > ])
          
          NOTE: Do not call this function while streaming.
          """
          if not isinstance(Data, list):
              raise LabJackException("Data must be a list of bytes")
          
          command = [ 0 ] * 40
          
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x11
          command[3] = 0x28
          if WriteCal:
              command[3] = 0x2B
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          command[6] = 0x00
          command[7] = BlockNum
          command[8:] = Data
          
          self._writeRead(command, 8, [0xF8, 0x01, command[3]])
  
      def writeCal(self, BlockNum, Data):
          return self.writeMem(BlockNum, Data, WriteCal = True)
          
      def eraseMem(self, EraseCal=False):
          """
          Name: U6.eraseMem(EraseCal=False)
          Args: EraseCal, set to True to erase the calibration memory.
          Desc: The U6 uses flash memory that must be erased before writing.
                Please read section 5.2.8 of the user's guide before you do
                something you may regret.
  
          >>> myU6 = U6()
          >>> myU6.eraseMem()
  
          NOTE: Do not call this function while streaming.
          """
          if not isinstance(EraseCal, bool):
              raise LabJackException("EraseCal must be a Boolean value (True or False).")
          
          if EraseCal:
              command = [ 0 ] * 8
              
              #command[0] = Checksum8
              command[1] = 0xF8
              command[2] = 0x01
              command[3] = 0x2C
              #command[4] = Checksum16 (LSB)
              #command[5] = Checksum16 (MSB)
              command[6] = 0x4C
              command[7] = 0x6C
          else:
              command = [ 0 ] * 6
              
              #command[0] = Checksum8
              command[1] = 0xF8
              command[2] = 0x00
              command[3] = 0x29
              #command[4] = Checksum16 (LSB)
              #command[5] = Checksum16 (MSB)
          
          self._writeRead(command, 8, [0xF8, 0x01, command[3]])
  
      def eraseCal(self):
          return self.eraseMem(EraseCal=True)
  
      def streamConfig(self, NumChannels = 1, ResolutionIndex = 0, SamplesPerPacket = 25, SettlingFactor = 0, InternalStreamClockFrequency = 0, DivideClockBy256 = False, ScanInterval = 1, ChannelNumbers = [0], ChannelOptions = [0], ScanFrequency = None, SampleFrequency = None):
          """
          Name: U6.streamConfig(NumChannels = 1, ResolutionIndex = 0,
                                SamplesPerPacket = 25, SettlingFactor = 0,
                                InternalStreamClockFrequency = 0, DivideClockBy256 = False,
                                ScanInterval = 1, ChannelNumbers = [0],
                                ChannelOptions = [0], ScanFrequency = None,
                                SampleFrequency = None )
          Args: NumChannels, the number of channels to stream
                ResolutionIndex, the resolution index of the samples (0-8)
                SettlingFactor, the settling factor to be used
                ChannelNumbers, a list of channel numbers to stream
                ChannelOptions, a list of channel options bytes.
                                ChannelOptions byte:  bit 7 = Differential,
                                                      bit 4-5 = GainIndex
                                  Set bit 7 for differential reading.
                                  GainIndex: 0(b00)=x1,  1(b01)=x10, 2(b10)=x100,
                                             3(b11)=x1000
  
                Set Either:
  
                ScanFrequency, the frequency in Hz to scan the channel list (ChannelNumbers).
                               sample rate (Hz) = ScanFrequency * NumChannels
  
                -- OR --
  
                SamplesPerPacket, how many samples make one packet
                InternalStreamClockFrequency, 0 = 4 MHz, 1 = 48 MHz
                DivideClockBy256, True = divide the clock by 256
                ScanInterval, clock/ScanInterval = frequency.
  
                See Section 5.2.12 of the User's Guide for more details.
  
                Deprecated:
                
                SampleFrequency, the frequency in Hz to sample.  Use ScanFrequency
                                 since SampleFrequency has always set the scan
                                 frequency and the name is confusing.
  
          Desc: Configures streaming on the U6.
          """
          if NumChannels != len(ChannelNumbers) or NumChannels != len(ChannelOptions):
              raise LabJackException("NumChannels must match length of ChannelNumbers and ChannelOptions")
          if len(ChannelNumbers) != len(ChannelOptions):
              raise LabJackException("len(ChannelNumbers) doesn't match len(ChannelOptions)")
  
          if (ScanFrequency is not None) or (SampleFrequency is not None):
              if ScanFrequency is None:
                  ScanFrequency = SampleFrequency
              if ScanFrequency < 1000:
                  if ScanFrequency < 25:
                      SamplesPerPacket = ScanFrequency
                  DivideClockBy256 = True
                  ScanInterval = 15625 // ScanFrequency
              else:
                  DivideClockBy256 = False
                  ScanInterval = 4000000 // ScanFrequency
  
          # Force Scan Interval into correct range
          ScanInterval = min(ScanInterval, 65535)
          ScanInterval = int(ScanInterval)
          ScanInterval = max(ScanInterval, 1)
  
          # Same with Samples Per Packet
          SamplesPerPacket = max(SamplesPerPacket, 1)
          SamplesPerPacket = int(SamplesPerPacket)
          SamplesPerPacket = min(SamplesPerPacket, 25)
  
          command = [0] * (14 + NumChannels*2)
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = NumChannels + 4
          command[3] = 0x11
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          command[6] = NumChannels
          command[7] = ResolutionIndex
          command[8] = SamplesPerPacket
          #command[9] = Reserved
          command[10] = SettlingFactor
          command[11] = (InternalStreamClockFrequency & 1) << 3
          if DivideClockBy256:
              command[11] |= 1 << 1
          command[12] = ScanInterval & 0xFF
          command[13] = (ScanInterval >> 8) & 0xFF
          for i in range(NumChannels):
              command[14+(i*2)] = ChannelNumbers[i]
              command[15+(i*2)] = ChannelOptions[i]
  
          self._writeRead(command, 8, [0xF8, 0x01, 0x11])
  
          # Set up the variables for future use.
          self.streamSamplesPerPacket = SamplesPerPacket
          self.streamChannelNumbers = ChannelNumbers
          self.streamChannelOptions = ChannelOptions
          self.streamConfiged = True
  
          if InternalStreamClockFrequency == 1:
              freq = float(48000000)
          else:
              freq = float(4000000)
  
          if DivideClockBy256:
              freq /= 256
  
          freq = freq / ScanInterval
  
          if SamplesPerPacket < 25:
              # limit to one packet
              self.packetsPerRequest = 1
          else:
              self.packetsPerRequest = max(1, int(freq/SamplesPerPacket))
              self.packetsPerRequest = min(self.packetsPerRequest, 48)
  
      def processStreamData(self, result, numBytes = None):
          """
          Name: U6.processStreamData(result, numPackets = None)
          Args: result, the string returned from streamData()
                numBytes, the number of bytes per packet
          Desc: Breaks stream data into individual channels and applies
                calibrations.
  
          >>> reading = d.streamData(convert = False)
          >>> print(processStreamData(reading['result']))
          defaultDict(list, {'AIN0': [3.123, 3.231, 3.232, ...]})
          """
          if numBytes is None:
              numBytes = 14 + (self.streamSamplesPerPacket * 2)
  
          returnDict = collections.defaultdict(list)
  
          numChannels = len(self.streamChannelNumbers)
          j = self.streamPacketOffset
          for packet in self.breakupPackets(result, numBytes):
              for sample in self.samplesFromPacket(packet):
                  if j >= numChannels:
                      j = 0
  
                  if self.streamChannelNumbers[j] in (193, 194):
                      value = unpack('<BB', sample)
                  elif self.streamChannelNumbers[j] >= 200:
                      value = unpack('<H', sample)[0]
                  else:
                      value = unpack('<H', sample)[0]
                      gainIndex = (self.streamChannelOptions[j] >> 4) & 0x3
                      value = self.binaryToCalibratedAnalogVoltage(gainIndex, value, is16Bits = True, resolutionIndex = 1)
  
                  returnDict["AIN%s" % self.streamChannelNumbers[j]].append(value)
  
                  j += 1
  
              self.streamPacketOffset = j
  
          return returnDict
  
      def watchdog(self, Write = False, ResetOnTimeout = False, SetDIOStateOnTimeout = False, TimeoutPeriod = 60, DIOState = 0, DIONumber = 0):
          """
          Name: U6.watchdog(Write = False, ResetOnTimeout = False,
                            SetDIOStateOnTimeout = False, TimeoutPeriod = 60,
                            DIOState = 0, DIONumber = 0)
          Args: Write, Set to True to write new values to the watchdog.
                ResetOnTimeout, True means reset the device on timeout.
                SetDIOStateOnTimeout, True means set the sate of a DIO on timeout.
                TimeoutPeriod, Time (in seconds) to wait before timing out.
                DIOState, 1 = High, 0 = Low.
                DIONumber, Which DIO to set.
          Desc: Controls a firmware based watchdog timer.
          """
          command = [0] * 16
  
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x05
          command[3] = 0x09
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          if Write:
              command[6] = 1
          if ResetOnTimeout:
              command[7] = (1 << 5)
          if SetDIOStateOnTimeout:
              command[7] |= (1 << 4)
  
          t = pack("<H", TimeoutPeriod)
          command[8] = TimeoutPeriod & 0xFF
          command[9] = TimeoutPeriod >> 8
          command[10] = ((DIOState & 1 ) << 7)
          command[10] |= (DIONumber & 0xF)
  
          result = self._writeRead(command, 16, [0xF8, 0x05, 0x09])
  
          watchdogStatus = {}
  
          if result[7] == 0:
              watchdogStatus['WatchDogEnabled'] = False
              watchdogStatus['ResetOnTimeout'] = False
              watchdogStatus['SetDIOStateOnTimeout'] = False
          else:
              watchdogStatus['WatchDogEnabled'] = True
  
              if (result[7] >> 5) & 1:
                  watchdogStatus['ResetOnTimeout'] = True
              else:
                  watchdogStatus['ResetOnTimeout'] = False
  
              if (result[7] >> 4) & 1:
                  watchdogStatus['SetDIOStateOnTimeout'] = True
              else:
                  watchdogStatus['SetDIOStateOnTimeout'] = False
  
          watchdogStatus['TimeoutPeriod'] = unpack('<H', pack("BB", *result[8:10]))
  
          if (result[10] >> 7) & 1:
              watchdogStatus['DIOState'] = 1
          else:
              watchdogStatus['DIOState'] = 0 
  
          watchdogStatus['DIONumber'] = (result[10] & 15)
  
          return watchdogStatus
  
      def spi(self, SPIBytes, AutoCS=True, DisableDirConfig = False, SPIMode = 'A', SPIClockFactor = 0, CSPinNum = 0, CLKPinNum = 1, MISOPinNum = 2, MOSIPinNum = 3, CSPINNum = None):
          """
          Name: U6.spi(SPIBytes, AutoCS=True, DisableDirConfig = False,
                       SPIMode = 'A', SPIClockFactor = 0, CSPinNum = 0,
                       CLKPinNum = 1, MISOPinNum = 2, MOSIPinNum = 3)
  
          Args: SPIBytes, A list of bytes to send.
                AutoCS, If True, the CS line is automatically driven low
                        during the SPI communication and brought back high
                        when done.
                DisableDirConfig, If True, function does not set the direction
                                  of the line.
                SPIMode, 'A', 'B', 'C',  or 'D'.
                SPIClockFactor, Sets the frequency of the SPI clock.
                CSPinNum, which pin is CS
                CLKPinNum, which pin is CLK
                MISOPinNum, which pin is MISO
                MOSIPinNum, which pin is MOSI
  
          Desc: Sends and receives serial data using SPI synchronous
                communication. See Section 5.2.17 of the user's guide.
  
          NOTE: The keyword argument CSPinNum was named CSPINNum in
                old versions.
          """
          if not isinstance(SPIBytes, list):
              raise LabJackException("SPIBytes MUST be a list of bytes")
  
          if CSPINNum is not None:
              warnings.warn("CSPINNum is deprecated, use CSPinNum instead", DeprecationWarning)
              CSPinNum = CSPINNum
  
          numSPIBytes = len(SPIBytes)
  
          if numSPIBytes > 50:
              raise LabJackException("The maximum number of bytes that can be sent/received is 50")
  
          oddPacket = False
          if numSPIBytes%2 != 0:
              SPIBytes.append(0)
              numSPIBytes = numSPIBytes + 1
              oddPacket = True
  
          command = [0] * (13 + numSPIBytes)
  
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 4 + (numSPIBytes//2)
          command[3] = 0x3A
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
  
          if AutoCS:
              command[6] |= (1 << 7)
          if DisableDirConfig:
              command[6] |= (1 << 6)
  
          spiModes = ('A', 'B', 'C', 'D')
          try:
              modeIndex = spiModes.index(SPIMode)
          except ValueError:
              raise LabJackException("Invalid SPIMode %r, valid modes are: %r" % (SPIMode, spiModes))
          command[6] |= modeIndex
          
          command[7] = SPIClockFactor
          #command[8] = Reserved
          command[9] = CSPinNum
          command[10] = CLKPinNum
          command[11] = MISOPinNum
          command[12] = MOSIPinNum
          command[13] = numSPIBytes
          if oddPacket:
              command[13] = numSPIBytes - 1
  
          command[14:] = SPIBytes
  
          result = self._writeRead(command, 8+numSPIBytes, [0xF8, 1+(numSPIBytes//2), 0x3A])
  
          return {'NumSPIBytesTransferred': result[7], 'SPIBytes': result[8:]}
  
      def asynchConfig(self, Update = True, UARTEnable = True, DesiredBaud = None, BaudFactor = 63036):
          """
          Name: U6.asynchConfig(Update = True, UARTEnable = True,
                                DesiredBaud = None, BaudFactor = 63036)
          Args: Update, If True, new values are written.
                UARTEnable, If True, UART will be enabled.
                DesiredBaud, If set, will apply the formula to calculate
                             BaudFactor.
                BaudFactor, = 2^16 - 48000000/(2 * Desired Baud). Ignored
                            if DesiredBaud is set.
          Desc: Configures the U6 UART for asynchronous communication. See
                section 5.2.18 of the User's Guide.
  
          returns a dictionary:
          {
              'Update': True means new parameters were written
              'UARTEnable': True means the UART is enabled
              'BaudFactor': The baud factor being used
          }
          """
          if UARTEnable:
              self.configIO(EnableUART = True)
  
          command = [0] * 10
  
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x02
          command[3] = 0x14
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          #commmand[6] = 0x00
          if Update:
              command[7] = (1 << 7)
          if UARTEnable:
              command[7] |= (1 << 6)
  
          if DesiredBaud is not None:
              BaudFactor = (2**16) - 48000000 // (2 * DesiredBaud)
  
          command[8] = BaudFactor & 0xFF
          command[9] = BaudFactor >> 8
  
          result = self._writeRead(command, 10, [0xF8, 0x02, 0x14])
  
          returnDict = {}
  
          if (result[7] >> 7) & 1:
              returnDict['Update'] = True
          else:
              returnDict['Update'] = False
  
          if (result[7] >> 6) & 1:
              returnDict['UARTEnable'] = True
          else:
              returnDict['UARTEnable'] = False
  
          returnDict['BaudFactor'] = unpack("<H", pack("BB", *result[8:]))[0]
  
          return returnDict
  
      def asynchTX(self, AsynchBytes):
          """
          Name: U6.asynchTX(AsynchBytes)
  
          Args: AsynchBytes, List of bytes to send
  
          Desc: Sends bytes to the U6 UART which will be sent asynchronously
                on the transmit line. Section 5.2.19 of the User's Guide.
  
          returns a dictionary:
          {
              'NumAsynchBytesSent' : Number of Asynch Bytes Sent
              'NumAsynchBytesInRXBuffer' : How many bytes are currently in the
                                           RX buffer.
          }
          """
          numBytes = len(AsynchBytes)
  
          if numBytes > 56:
              raise LabJackException("The maximum number of bytes that can be sent is 56")
  
          oddPacket = False
          if numBytes % 2 != 0:
              oddPacket = True
              AsynchBytes.append(0)
              numBytes = numBytes + 1
  
          command = [0] * (8 + numBytes)
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 1 + (numBytes // 2)
          command[3] = 0x15
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          #commmand[6] = 0x00
          command[7] = numBytes
          if oddPacket:
              command[7] = numBytes - 1
          command[8:] = AsynchBytes
  
          result = self._writeRead(command, 10, [0xF8, 0x02, 0x15])
  
          return {'NumAsynchBytesSent': result[7], 'NumAsynchBytesInRXBuffer': result[8]}
  
      def asynchRX(self, Flush = False):
          """
          Name: U6.asynchRX(Flush = False)
  
          Args: Flush, If True, empties the entire 256-byte RX buffer.
  
          Desc: Sends bytes to the U6 UART which will be sent asynchronously
                on the transmit line. Section 5.2.20 of the User's Guide.
  
          returns a dictonary:
          {
              'AsynchBytes' : List of received bytes
              'NumAsynchBytesInRXBuffer' : Number of AsynchBytes are in the RX
                                           Buffer.
          }
          """
          # command = [0, 0xF8, 0x01, 0x16, 0, 0, 0, int(Flush)]
          command = [0] * 8
  
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x01
          command[3] = 0x16
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          #command[6] = 0x00
          if Flush:
              command[7] = 1
  
          result = self._writeRead(command, 40, [0xF8, 0x11, 0x16])
  
          return {'AsynchBytes': result[8:], 'NumAsynchBytesInRXBuffer': result[7]}
  
      def i2c(self, Address, I2CBytes, EnableClockStretching = False, NoStopWhenRestarting = False, ResetAtStart = False, SpeedAdjust = 0, SDAPinNum = 0, SCLPinNum = 1, NumI2CBytesToReceive = 0, AddressByte = None):
          """
          Name: U6.i2c(Address, I2CBytes,
                       EnableClockStretching = False, NoStopWhenRestarting = False,
                       ResetAtStart = False, SpeedAdjust = 0,
                       SDAPinNum = 0, SCLPinNum = 1,
                       NumI2CBytesToReceive = 0, AddressByte = None)
  
          Args: Address, the address (Not shifted over)
                I2CBytes, a list of bytes to send
                EnableClockStretching, True enables clock stretching
                NoStopWhenRestarting, True means no stop sent when restarting
                ResetAtStart, if True, an I2C bus reset will be done
                              before communicating.
                SpeedAdjust, Allows the communication frequency to be reduced.
                SDAPinNum, Which pin will be data
                SCLPinNum, Which pin is clock
                NumI2CBytesToReceive, Number of I2C bytes to expect back.
                AddressByte, The address as you would put it in the low-level
                             packet. Overrides Address. Optional.
  
          Desc: Sends and receives serial data using I2C synchronous
                communication. Section 5.2.21 of the User's Guide.
          """
          numBytes = len(I2CBytes)
          if numBytes > 50:
              raise LabJackException("The maximum number of bytes that can be sent is 50")
          if NumI2CBytesToReceive > 52:
              raise LabJackException("The maximum number of bytes that can be received is 52")
  
          oddPacket = False
          if numBytes % 2 != 0:
              oddPacket = True
              I2CBytes.append(0)
              numBytes = numBytes + 1
  
          command = [0] * (14+numBytes)
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 4 + (numBytes//2)
          command[3] = 0x3B
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          if EnableClockStretching:
              command[6] |= (1 << 3)
          if NoStopWhenRestarting:
              command[6] |= (1 << 2)
          if ResetAtStart:
              command[6] |= (1 << 1)
  
          command[7] = SpeedAdjust
          command[8] = SDAPinNum
          command[9] = SCLPinNum
  
          if AddressByte is not None:
              command[10] = AddressByte
          else:
              command[10] = Address << 1
          #command[11] = Reserved
          command[12] = numBytes
          if oddPacket:
              command[12] = numBytes - 1
          command[13] = NumI2CBytesToReceive
          command[14:] = I2CBytes
  
          oddResponse = False
          if NumI2CBytesToReceive % 2 != 0:
              NumI2CBytesToReceive = NumI2CBytesToReceive + 1
              oddResponse = True
  
          result = self._writeRead(command, (12 + NumI2CBytesToReceive), [0xF8, (3 + (NumI2CBytesToReceive/2)), 0x3B])
  
          if NumI2CBytesToReceive != 0:
              if oddResponse:
                  return {'AckArray': result[8:12], 'I2CBytes': result[12:-1]}
              else:
                  return {'AckArray': result[8:12], 'I2CBytes': result[12:]}
          else:
              return {'AckArray': result[8:12]}
  
      def sht1x(self, DataPinNum = 0, ClockPinNum = 1, SHTOptions = 0xc0):
          """
          Name: U6.sht1x(DataPinNum = 0, ClockPinNum = 1, SHTOptions = 0xc0)
          Args: DataPinNum, Which pin is the Data
                ClockPinNum, Which pin is the Clock
                SHTOptions:
                    bit 7 = Read Relative Humidity
                    bit 6 = Read Temperature
                    bit 2 = Heater: 1 = on, 0 = off
                    bit 1 = Reserved at 0
                    bit 0 = Resolution:
                            1 = 8-bit RH, 12-bit Temp
                            0 = 12-bit RH, 14-bit Temp
          Desc: Reads temperature and humidity from a Sensirion SHT1X sensor,
                which is used by the EI-1050.
                See section 5.2.22 of the User's Guide for more details.
  
          Returns a dictonary:
          {
              'StatusReg' : SHT1X status register
              'StatusRegCRC' : SHT1X status register CRC value
              'Temperature' : The temperature in C
              'TemperatureCRC' : The CRC value for the temperature
              'Humidity' : The humidity
              'HumidityCRC' : The CRC value for the humidity
          }
          """
          command = [0] * 10
  
          #command[0] = Checksum8
          command[1] = 0xF8
          command[2] = 0x02
          command[3] = 0x39
          #command[4] = Checksum16 (LSB)
          #command[5] = Checksum16 (MSB)
          command[6] = DataPinNum
          command[7] = ClockPinNum
          #command[8] = Reserved
          command[9] = SHTOptions
  
          result = self._writeRead(command, 16, [0xF8, 0x05, 0x39])
  
          val = (result[11]*256) + result[10]
          temp = -39.60 + 0.01*val
  
          val = (result[14]*256) + result[13]
          humid = -4 + 0.0405*val + -.0000028*(val*val)
          humid = (temp - 25)*(0.01 + 0.00008*val) + humid
  
          return {'StatusReg': result[8], 'StatusCRC': result[9], 'Temperature': temp, 'TemperatureCRC': result[12], 'Humidity': humid, 'HumidityCRC': result[15]}
  
      # --------------------------- Old U6 code -------------------------------
  
      def _readCalDataBlock(self, n):
          """
          Internal routine to read the specified calibration block (0-2)
          """
          sendBuffer = [0] * 8
          sendBuffer[1] = 0xF8  # command byte
          sendBuffer[2] = 0x01  #  number of data words
          sendBuffer[3] = 0x2D  #  extended command number
          sendBuffer[6] = 0x00
          sendBuffer[7] = n     # Blocknum = 0
          self.write(sendBuffer)
          buff = self.read(40)
          return buff[8:]
  
      def getCalibrationData(self):
          """
          Name: U6.getCalibrationData()
          Args: None
          Desc: Gets the slopes and offsets for AIN and DACs,
                as well as other calibration data
  
          >>> myU6 = U6()
          >>> myU6.getCalibrationData()
          >>> myU6.calInfo
          <ainDiffOffset: -2.46886488446,...>
          """
          if self.debug is True:
              print("Calibration data retrieval")
  
          self.calInfo.nominal = False
  
          # Reading block 0 from memory
          rcvBuffer = self._readCalDataBlock(0)
  
          # Positive Channel calibration
          self.calInfo.ain10vSlope = toDouble(rcvBuffer[:8])
          self.calInfo.ain10vOffset = toDouble(rcvBuffer[8:16])
          self.calInfo.ain1vSlope = toDouble(rcvBuffer[16:24])
          self.calInfo.ain1vOffset = toDouble(rcvBuffer[24:])
  
          # Reading block 1 from memory
          rcvBuffer = self._readCalDataBlock(1)
  
          self.calInfo.ain100mvSlope = toDouble(rcvBuffer[:8])
          self.calInfo.ain100mvOffset = toDouble(rcvBuffer[8:16])
          self.calInfo.ain10mvSlope = toDouble(rcvBuffer[16:24])
          self.calInfo.ain10mvOffset = toDouble(rcvBuffer[24:])
  
          self.calInfo.ainSlope = [self.calInfo.ain10vSlope, self.calInfo.ain1vSlope, self.calInfo.ain100mvSlope, self.calInfo.ain10mvSlope]
          self.calInfo.ainOffset = [self.calInfo.ain10vOffset, self.calInfo.ain1vOffset, self.calInfo.ain100mvOffset, self.calInfo.ain10mvOffset]
  
          # Reading block 2 from memory
          rcvBuffer = self._readCalDataBlock(2)
  
          # Negative channel calibration
          self.calInfo.ain10vNegSlope = toDouble(rcvBuffer[:8])
          self.calInfo.ain10vCenter = toDouble(rcvBuffer[8:16])
          self.calInfo.ain1vNegSlope = toDouble(rcvBuffer[16:24])
          self.calInfo.ain1vCenter = toDouble(rcvBuffer[24:])
  
          # Reading block 3 from memory
          rcvBuffer = self._readCalDataBlock(3)
  
          self.calInfo.ain100mvNegSlope = toDouble(rcvBuffer[:8])
          self.calInfo.ain100mvCenter = toDouble(rcvBuffer[8:16])
          self.calInfo.ain10mvNegSlope = toDouble(rcvBuffer[16:24])
          self.calInfo.ain10mvCenter = toDouble(rcvBuffer[24:])
  
          self.calInfo.ainNegSlope = [self.calInfo.ain10vNegSlope, self.calInfo.ain1vNegSlope, self.calInfo.ain100mvNegSlope, self.calInfo.ain10mvNegSlope]
          self.calInfo.ainCenter = [self.calInfo.ain10vCenter, self.calInfo.ain1vCenter, self.calInfo.ain100mvCenter, self.calInfo.ain10mvCenter]
  
          # Reading block 4 from memory
          rcvBuffer = self._readCalDataBlock(4)
  
          # Miscellaneous
          self.calInfo.dac0Slope = toDouble(rcvBuffer[:8])
          self.calInfo.dac0Offset = toDouble(rcvBuffer[8:16])
          self.calInfo.dac1Slope = toDouble(rcvBuffer[16:24])
          self.calInfo.dac1Offset = toDouble(rcvBuffer[24:])
  
          self.calInfo.dacSlope = [self.calInfo.dac0Slope, self.calInfo.dac1Slope]
          self.calInfo.dacOffset = [self.calInfo.dac0Offset, self.calInfo.dac1Offset]
  
          # Reading block 5 from memory
          rcvBuffer = self._readCalDataBlock(5)
  
          self.calInfo.currentOutput0 = toDouble(rcvBuffer[:8])
          self.calInfo.currentOutput1 = toDouble(rcvBuffer[8:16])
          
          self.calInfo.temperatureSlope = toDouble(rcvBuffer[16:24])
          self.calInfo.temperatureOffset = toDouble(rcvBuffer[24:])
  
          if self.isPro:
              # Hi-Res ADC stuff
  
              # Reading block 6 from memory
              rcvBuffer = self._readCalDataBlock(6)
  
              # Positive Channel calibration
              self.calInfo.proAin10vSlope = toDouble(rcvBuffer[:8])
              self.calInfo.proAin10vOffset = toDouble(rcvBuffer[8:16])
              self.calInfo.proAin1vSlope = toDouble(rcvBuffer[16:24])
              self.calInfo.proAin1vOffset = toDouble(rcvBuffer[24:])
  
              # Reading block 7 from memory
              rcvBuffer = self._readCalDataBlock(7)
  
              self.calInfo.proAin100mvSlope = toDouble(rcvBuffer[:8])
              self.calInfo.proAin100mvOffset = toDouble(rcvBuffer[8:16])
              self.calInfo.proAin10mvSlope = toDouble(rcvBuffer[16:24])
              self.calInfo.proAin10mvOffset = toDouble(rcvBuffer[24:])
  
              self.calInfo.proAinSlope = [self.calInfo.proAin10vSlope, self.calInfo.proAin1vSlope, self.calInfo.proAin100mvSlope, self.calInfo.proAin10mvSlope]
              self.calInfo.proAinOffset = [self.calInfo.proAin10vOffset, self.calInfo.proAin1vOffset, self.calInfo.proAin100mvOffset, self.calInfo.proAin10mvOffset]
  
              # Reading block 8 from memory
              rcvBuffer = self._readCalDataBlock(8)
  
              # Negative Channel calibration
              self.calInfo.proAin10vNegSlope = toDouble(rcvBuffer[:8])
              self.calInfo.proAin10vCenter = toDouble(rcvBuffer[8:16])
              self.calInfo.proAin1vNegSlope = toDouble(rcvBuffer[16:24])
              self.calInfo.proAin1vCenter = toDouble(rcvBuffer[24:])
  
              # Reading block 9 from memory
              rcvBuffer = self._readCalDataBlock(9)
  
              self.calInfo.proAin100mvNegSlope = toDouble(rcvBuffer[:8])
              self.calInfo.proAin100mvCenter = toDouble(rcvBuffer[8:16])
              self.calInfo.proAin10mvNegSlope = toDouble(rcvBuffer[16:24])
              self.calInfo.proAin10mvCenter = toDouble(rcvBuffer[24:])
  
              self.calInfo.proAinNegSlope = [self.calInfo.proAin10vNegSlope, self.calInfo.proAin1vNegSlope, self.calInfo.proAin100mvNegSlope, self.calInfo.proAin10mvNegSlope]
              self.calInfo.proAinCenter = [self.calInfo.proAin10vCenter, self.calInfo.proAin1vCenter, self.calInfo.proAin100mvCenter, self.calInfo.proAin10mvCenter]
  
      def binaryToCalibratedAnalogVoltage(self, gainIndex, bytesVoltage, is16Bits=False, resolutionIndex=0):
          """
          Name: U6.binaryToCalibratedAnalogVoltage(gainIndex, bytesVoltage, 
                                                   is16Bits = False, resolutionIndex = 0)
          Args: gainIndex, which gain index did you use?
                bytesVoltage, bytes returned from the U6
                is16Bits, set to True if bytesVoltage is 16 bits (not 24)
                resolutionIndex, which resolution index did you use?  Set this for
                                 U6-Pro devices to ensure proper hi-res conversion.
          Desc: Converts binary voltage to an analog value.
          """
          if not is16Bits:
              bits = float(bytesVoltage)/256
          else:
              bits = float(bytesVoltage)
  
          if self.isPro and (resolutionIndex > 8 or resolutionIndex == 0):
              #Use hi-res calibration constants
              center = self.calInfo.proAinCenter[gainIndex]
              negSlope = self.calInfo.proAinNegSlope[gainIndex]
              posSlope = self.calInfo.proAinSlope[gainIndex]
          else:
              #Use normal calibration constants
              center = self.calInfo.ainCenter[gainIndex]
              negSlope = self.calInfo.ainNegSlope[gainIndex]
              posSlope = self.calInfo.ainSlope[gainIndex]
  
          if bits < center:
              return (center - bits) * negSlope
          else:
              return (bits - center) * posSlope
  
      def binaryToCalibratedAnalogTemperature(self, bytesTemperature, is16Bits=False):
          """
          Name: U6.binaryToCalibratedAnalogTemperature(bytesTemperature, is16Bits = False)
          Args: bytesTemperature, bytes returned from the U6
                is16Bits, set to True if bytesTemperature is 16 bits (not 24)
          Desc: Converts binary temperature to Kelvin.
          """
          voltage = self.binaryToCalibratedAnalogVoltage(0, bytesTemperature, is16Bits, 1)
          return self.calInfo.temperatureSlope * float(voltage) + self.calInfo.temperatureOffset
  
      def voltageToDACBits(self, volts, dacNumber = 0, is16Bits = False):
          """
          Name: U6.voltageToDACBits(volts, dacNumber = 0, is16Bits = False)
          Args: volts, the voltage you would like to set the DAC to.
                dacNumber, 0 or 1, helps apply the correct calibration
                is16Bits, True if you are going to use the 16-bit DAC command
          Desc: Takes a voltage, and turns it into the bits needed for the DAC
                Feedback commands.
          """
          bits = ( volts * self.calInfo.dacSlope[dacNumber] ) + self.calInfo.dacOffset[dacNumber]
          if is16Bits:
              bits = min(bits, 0xFFFF)
          else:
              bits = min(bits/256, 0xFF)
          
          return int(max(bits, 0))
  
      def softReset(self):
          """
          Name: U6.softReset()
          Args: none
          Desc: Send a soft reset.
          
          >>> myU6 = U6()
          >>> myU6.softReset()
          """
          command = [ 0x00, 0x99, 0x01, 0x00 ]
          command = setChecksum8(command, 4)
          
          self.write(command, False, False)
          results = self.read(4)
          
          if results[3] != 0:
              raise LowlevelErrorException(results[3], "The softReset command returned an error:\n    %s" % lowlevelErrorToString(results[3]))
  
      def hardReset(self):
          """
          Name: U6.hardReset()
          Args: none
          Desc: Send a hard reset.
          
          >>> myU6 = U6()
          >>> myU6.hardReset()
          """
          command = [ 0x00, 0x99, 0x02, 0x00 ]
          command = setChecksum8(command, 4)
          
          self.write(command, False, False)
          results = self.read(4)
          
          if results[3] != 0:
              raise LowlevelErrorException(results[3], "The softHard command returned an error:\n    %s" % lowlevelErrorToString(results[3]))
              
          self.close()
  
      def setLED(self, state):
          """
          Name: U6.setLED(state)
          Args: state: 1 = On, 0 = Off
          Desc: Sets the state of the LED. (5.2.5.4 of user's guide)
          
          >>> d = u6.U6()
          >>> d.setLED(0)
          ... (LED turns off) ...
          """
          self.getFeedback(LED(state))
  
      def setDOState(self, ioNum, state = 1):
          """
          Name: U6.setDOState(ioNum, state = 1)
          Args: ioNum, which digital I/O to change
                    0 - 7   = FIO0 - FIO7
                    8 - 15  = EIO0 - EIO7
                    16 - 19 = CIO0 - CIO3
                    20 - 22 = MIO0 - MIO2
                state, 1 = High, 0 = Low
          Desc: A convenience function to set the state of a digital I/O. Will
                also set the direction to output.
          
          Example:
          >>> import u6
          >>> d = u6.U6()
          >>> d.setDOState(0, state = 1)
          """
          self.getFeedback(BitDirWrite(ioNum, 1), BitStateWrite(ioNum, state))
  
      def getDIState(self, ioNum):
          """
          Name: U6.getDIState(ioNum)
          Args: ioNum, which digital I/O to read
                    0 - 7   = FIO0 - FIO7
                    8 - 15  = EIO0 - EIO7
                    16 - 19 = CIO0 - CIO3
                    20 - 22 = MIO0 - MIO2
          Desc: A convenience function to read the state of a digital I/O.  Will
                also set the direction to input.
          
          Example:
          >>> import u6
          >>> d = u6.U6()
          >>> print(d.getDIState(0))
          1
          """
          return self.getFeedback(BitDirWrite(ioNum, 0), BitStateRead(ioNum))[1]
  
      def getDIOState(self, ioNum):
          """
          Name: U6.getDIOState(ioNum)
          Args: ioNum, which digital I/O to read
                    0 - 7   = FIO0 - FIO7
                    8 - 15  = EIO0 - EIO7
                    16 - 19 = CIO0 - CIO3
                    20 - 22 = MIO0 - MIO2
          Desc: A convenience function to read the state of a digital I/O.  Will
                not change the direction.
          
          Example:
          >>> import u6
          >>> d = u6.U6()
          >>> print(d.getDIOState(0))
          1
          """
          return self.getFeedback(BitStateRead(ioNum))[0]
  
      def getTemperature(self):
          """
          Name: U6.getTemperature()
          Args: none
          Desc: Reads the U6's internal temperature sensor in Kelvin. 
                See Section 2.6.4 of the U6 User's Guide.
          
          >>> myU6.getTemperature()
          299.87723471224308
          """
          if self.calInfo.nominal:
              # Read the actual calibration constants if we haven't already.
              self.getCalibrationData()
          
          result = self.getFeedback(AIN24AR(14))
          return self.binaryToCalibratedAnalogTemperature(result[0]['AIN'])
  
      def getAIN(self, positiveChannel, resolutionIndex=0, gainIndex=0, settlingFactor=0, differential=False):
          """
          Name: U6.getAIN(positiveChannel, resolutionIndex = 0, gainIndex = 0,
                          settlingFactor = 0, differential = False)
          Args: positiveChannel, the positive channel to read from
                resolutionIndex, the resolution index.  0 = default, 1-8 = high-speed
                                 ADC, 9-12 = high-res ADC (U6-Pro only).
                gainIndex, the gain index.  0=x1, 1=x10, 2=x100, 3=x1000,
                           15=autorange.
                settlingFactor, the settling factor.  0=Auto, 1=20us, 2=50us,
                                3=100us, 4=200us, 5=500us, 6=1ms, 7=2ms, 8=5ms, 
                                9=10ms.
                differential, set to True for differential reading.  Negative
                              channel is positiveChannel+1.
          Desc: Reads an AIN and applies the calibration constants to it.
          
          >>> myU6.getAIN(14)
          299.87723471224308
          """
          result = self.getFeedback(AIN24AR(positiveChannel, resolutionIndex, gainIndex, settlingFactor, differential))
          
          return self.binaryToCalibratedAnalogVoltage(result[0]['GainIndex'], result[0]['AIN'], resolutionIndex = resolutionIndex)
  
      def readDefaultsConfig(self):
          """
          Name: U6.readDefaultsConfig()
          Args: None
          Desc: Reads the power-up defaults stored in flash.
          """
          results = dict()
          defaults = self.readDefaults(0)
          
          results['FIODirection'] = defaults[4]
          results['FIOState'] = defaults[5]
          
          results['EIODirection'] = defaults[8]
          results['EIOState'] = defaults[9]
          
          results['CIODirection'] = defaults[12]
          results['CIOState'] = defaults[13]
          
          results['ConfigWriteMask'] = defaults[16]
          results['NumOfTimersEnable'] = defaults[17]
          results['CounterMask'] = defaults[18]
          results['PinOffset'] = defaults[19]
          
          defaults = self.readDefaults(1)
          results['ClockSource'] = defaults[0]
          results['Divisor'] = defaults[1]
          
          results['TMR0Mode'] = defaults[16]
          results['TMR0ValueL'] = defaults[17]
          results['TMR0ValueH'] = defaults[18]
          
          results['TMR1Mode'] = defaults[20]
          results['TMR1ValueL'] = defaults[21]
          results['TMR1ValueH'] = defaults[22]
          
          results['TMR2Mode'] = defaults[24]
          results['TMR2ValueL'] = defaults[25]
          results['TMR2ValueH'] = defaults[26]
          
          results['TMR3Mode'] = defaults[28]
          results['TMR3ValueL'] = defaults[29]
          results['TMR3ValueH'] = defaults[30]
          
          defaults = self.readDefaults(2)
          
          results['DAC0'] = unpack( "<H", pack("BB", *defaults[16:18]) )[0]
          
          results['DAC1'] = unpack( "<H", pack("BB", *defaults[20:22]) )[0]
          
          defaults = self.readDefaults(3)
          
          for i in range(14):
              results["AIN%sGainRes" % i] = defaults[i]
              results["AIN%sOptions" % i] = defaults[i+16]
          
          return results
  
      def exportConfig(self):
          """
          Name: U6.exportConfig()
          Args: None
          Desc: Takes a configuration and puts it into a ConfigParser object.
          """
          # Make a new configuration file
          parser = ConfigParser.SafeConfigParser()
          
          # Change optionxform so that options preserve their case.
          parser.optionxform = str
          
          # Local Id and name
          section = "Identifiers"
          parser.add_section(section)
          parser.set(section, "Local ID", str(self.localId))
          parser.set(section, "Name", str(self.getName()))
          parser.set(section, "Device Type", str(self.devType))
          
          # FIO Direction / State
          section = "FIOs"
          parser.add_section(section)
          
          dirs, states = self.getFeedback( PortDirRead(), PortStateRead() )
          
          for key, value in dirs.items():
              parser.set(section, "%s Directions" % key, str(value))
              
          for key, value in states.items():
              parser.set(section, "%s States" % key, str(value))
              
          # DACs
          section = "DACs"
          parser.add_section(section)
          
          dac0 = self.readRegister(5000)
          dac0 = max(dac0, 0)
          dac0 = min(dac0, 5)
          parser.set(section, "DAC0", "%0.2f" % dac0)
          
          dac1 = self.readRegister(5002)
          dac1 = max(dac1, 0)
          dac1 = min(dac1, 5)
          parser.set(section, "DAC1", "%0.2f" % dac1)
          
          # Timer Clock Configuration
          section = "Timer Clock Speed Configuration"
          parser.add_section(section)
          
          timerclockconfig = self.configTimerClock()
          for key, value in timerclockconfig.items():
              parser.set(section, key, str(value))
          
          # Timers / Counters
          section = "Timers And Counters"
          parser.add_section(section)
          
          ioconfig = self.configIO()
          for key, value in ioconfig.items():
              parser.set(section, key, str(value))
              
          
          for i in range(ioconfig['NumberTimersEnabled']):
              mode, value = self.readRegister(7100 + (2 * i), numReg = 2, format = ">HH")
              parser.set(section, "Timer%s Mode" % i, str(mode))
              parser.set(section, "Timer%s Value" % i, str(value))
          
          return parser
  
      def loadConfig(self, configParserObj):
          """
          Name: U6.loadConfig(configParserObj)
          Args: configParserObj, A Config Parser object to load in
          Desc: Takes a configuration and updates the U6 to match it.
          """
          parser = configParserObj
          
          # Set Identifiers:
          section = "Identifiers"
          if parser.has_section(section):
              if parser.has_option(section, "device type"):
                  if parser.getint(section, "device type") != self.devType:
                      raise Exception("Not a U6 Config file.")
              
              if parser.has_option(section, "local id"):
                  self.configU6( LocalID = parser.getint(section, "local id"))
                  
              if parser.has_option(section, "name"):
                  self.setName( parser.get(section, "name") )
              
          # Set FIOs:
          section = "FIOs"
          if parser.has_section(section):
              fiodirs = 0
              eiodirs = 0
              ciodirs = 0
              
              fiostates = 0
              eiostates = 0
              ciostates = 0
              
              if parser.has_option(section, "fios directions"):
                  fiodirs = parser.getint(section, "fios directions")
              if parser.has_option(section, "eios directions"):
                  eiodirs = parser.getint(section, "eios directions")
              if parser.has_option(section, "cios directions"):
                  ciodirs = parser.getint(section, "cios directions")
              
              if parser.has_option(section, "fios states"):
                  fiostates = parser.getint(section, "fios states")
              if parser.has_option(section, "eios states"):
                  eiostates = parser.getint(section, "eios states")
              if parser.has_option(section, "cios states"):
                  ciostates = parser.getint(section, "cios states")
              
              self.getFeedback( PortStateWrite([fiostates, eiostates, ciostates]), PortDirWrite([fiodirs, eiodirs, ciodirs]) )
              
          # Set DACs:
          section = "DACs"
          if parser.has_section(section):
              if parser.has_option(section, "dac0"):
                  self.writeRegister(5000, parser.getfloat(section, "dac0"))
              
              if parser.has_option(section, "dac1"):
                  self.writeRegister(5002, parser.getfloat(section, "dac1"))
                  
          # Set Timer Clock Configuration
          section = "Timer Clock Speed Configuration"
          if parser.has_section(section):
              if parser.has_option(section, "timerclockbase") and parser.has_option(section, "timerclockdivisor"):
                  self.configTimerClock(TimerClockBase = parser.getint(section, "timerclockbase"), TimerClockDivisor = parser.getint(section, "timerclockdivisor"))
          
          # Set Timers / Counters
          section = "Timers And Counters"
          if parser.has_section(section):
              nte = None
              c0e = None
              c1e = None
              cpo = None
              
              if parser.has_option(section, "NumberTimersEnabled"):
                  nte = parser.getint(section, "NumberTimersEnabled")
              
              if parser.has_option(section, "TimerCounterPinOffset"):
                  cpo = parser.getint(section, "TimerCounterPinOffset")
              
              if parser.has_option(section, "Counter0Enabled"):
                  c0e = parser.getboolean(section, "Counter0Enabled")
              
              if parser.has_option(section, "Counter1Enabled"):
                  c1e = parser.getboolean(section, "Counter1Enabled")
                  
              self.configIO(NumberTimersEnabled = nte, EnableCounter1 = c1e, EnableCounter0 = c0e, TimerCounterPinOffset = cpo)
              
              
              mode = None
              value = None
              
              for i in range(4):
                  if parser.has_option(section, "timer%i mode" % i):
                      mode = parser.getint(section, "timer%i mode" % i)
                      
                      if parser.has_option(section, "timer%i value" % i):
                          value = parser.getint(section, "timer%i value" % i)
                      
                      self.getFeedback( TimerConfig(i, mode, value) )
  
  class FeedbackCommand(object):
      '''
      The base FeedbackCommand class
      
      Used to make Feedback easy. Make a list of these
      and call getFeedback.
      '''
      readLen = 0
      def handle(self, input):
          return None
  
  _validChannels = frozenset(range(144))
  
  class AIN(FeedbackCommand):
      '''
      Analog Input Feedback command
  
      AIN(PositiveChannel)
      
      PositiveChannel : the positive channel to use 
  
      NOTE: This function kept for compatibility. Please use
            the new AIN24 and AIN24AR.
      
      returns 16-bit unsigned int sample
      
      >>> d.getFeedback( u6.AIN( PositiveChannel ) )
      [ 19238 ]
      '''
      def __init__(self, PositiveChannel):
          if PositiveChannel not in _validChannels:
              raise LabJackException("Invalid Positive Channel specified")
          
          self.positiveChannel = PositiveChannel
          self.cmdBytes = [ 0x01, PositiveChannel, 0 ]
  
      readLen =  2
      
      def __repr__(self):
          return "<u6.AIN( PositiveChannel = %s )>" % self.positiveChannel
  
      def handle(self, input):
          result = (input[1] << 8) + input[0]
          return result
  
  class AIN24(FeedbackCommand):
      '''
      Analog Input 24-bit Feedback command
  
      ainCommand = AIN24(PositiveChannel, ResolutionIndex = 0, GainIndex = 0,
                         SettlingFactor = 0, Differential = False)
      
      See section 5.2.5.2 of the user's guide.
      
      NOTE: If you use a gain index of 15 (autorange), you should be using
            the AIN24AR command instead. 
      
      positiveChannel : The positive channel to use
      resolutionIndex : 0=default, 1-8 for high-speed ADC,
                        9-12 for high-res ADC on U6-Pro.
      gainIndex : 0=x1, 1=x10, 2=x100, 3=x1000, 15=autorange
      settlingFactor : 0=Auto, 1=20us, 2=50us, 3=100us, 4=200us, 5=500us, 6=1ms,
                       7=2ms, 8=5ms, 9=10ms.
      differential : If this bit is set, a differential reading is done where
                     the negative channel is positiveChannel+1
      
      returns 24-bit unsigned int sample
      
      >>> d.getFeedback( u6.AIN24(PositiveChannel, ResolutionIndex = 0,
                                  GainIndex = 0, SettlingFactor = 0,
                                  Differential = False ) )
      [ 193847 ]
      '''
      def __init__(self, PositiveChannel, ResolutionIndex = 0, GainIndex = 0, SettlingFactor = 0, Differential = False):
          if PositiveChannel not in _validChannels:
              raise LabJackException("Invalid Positive Channel specified")
  
          self.positiveChannel = PositiveChannel
          self.resolutionIndex = ResolutionIndex
          self.gainIndex = GainIndex
          self.settlingFactor = SettlingFactor
          self.differential = Differential
          
          byte2 = ( ResolutionIndex & 0xf )
          byte2 = ( ( GainIndex & 0xf ) << 4 ) + byte2
          
          byte3 = (int(Differential) << 7) + SettlingFactor
          self.cmdBytes = [ 0x02, PositiveChannel, byte2, byte3 ]
  
      def __repr__(self):
          return "<u6.AIN24( PositiveChannel = %s, ResolutionIndex = %s, GainIndex = %s, SettlingFactor = %s, Differential = %s )>" % (self.positiveChannel, self.resolutionIndex, self.gainIndex, self.settlingFactor, self.differential)
  
      readLen =  3
  
      def handle(self, input):
          #Put it all into an integer.
          result = (input[2] << 16 ) + (input[1] << 8 ) + input[0]
          return result
  
  class AIN24AR(FeedbackCommand):
      '''
      Autorange Analog Input 24-bit Feedback command
  
      ainARCommand = AIN24AR(0, ResolutionIndex = 0, GainIndex = 0, 
                             SettlingFactor = 0, Differential = False)
      
      See section 5.2.5.3 of the user's guide
      
      PositiveChannel : The positive channel to use
      ResolutionIndex : 0=default, 1-8 for high-speed ADC, 
                        9-13 for high-res ADC on U6-Pro.
      GainIndex : 0=x1, 1=x10, 2=x100, 3=x1000, 15=autorange
      SettlingFactor : 0=Auto, 1=20us, 2=50us, 3=100us, 4=200us, 5=500us, 6=1ms,
                       7=2ms, 8=5ms, 9=10ms.
      Differential : If this bit is set, a differential reading is done where
                     the negative channel is positiveChannel+1
      
      returns a dictionary:
          { 
          'AIN' : < 24-bit binary reading >, 
          'ResolutionIndex' : < actual resolution setting used for the reading >,
          'GainIndex' : < actual gain used for the reading >,
          'Status' : < reserved for future use >
          }
      
      >>> d.getFeedback( u6.AIN24AR( PositiveChannel, ResolutionIndex = 0,
                                     GainIndex = 0, SettlingFactor = 0,
                                     Differential = False ) )
      { 'AIN' : 193847, 'ResolutionIndex' : 0, 'GainIndex' : 0, 'Status' : 0 }
      '''
      def __init__(self, PositiveChannel, ResolutionIndex = 0, GainIndex = 0, SettlingFactor = 0, Differential = False):
          if PositiveChannel not in _validChannels:
              raise LabJackException("Invalid Positive Channel specified")
  
          self.positiveChannel = PositiveChannel
          self.resolutionIndex = ResolutionIndex
          self.gainIndex = GainIndex
          self.settlingFactor = SettlingFactor
          self.differential = Differential
  
          byte2 = ( ResolutionIndex & 0xf )
          byte2 = ( ( GainIndex & 0xf ) << 4 ) + byte2
          
          byte3 = (int(Differential) << 7) + SettlingFactor
          self.cmdBytes = [ 0x03, PositiveChannel, byte2, byte3 ]
  
      def __repr__(self):
          return "<u6.AIN24AR( PositiveChannel = %s, ResolutionIndex = %s, GainIndex = %s, SettlingFactor = %s, Differential = %s )>" % (self.positiveChannel, self.resolutionIndex, self.gainIndex, self.settlingFactor, self.differential)
  
      readLen =  5
  
      def handle(self, input):
          #Put it all into an integer.
          result = (input[2] << 16 ) + (input[1] << 8 ) + input[0]
          resolutionIndex = input[3] & 0xf
          gainIndex = ( input[3] >> 4 ) & 0xf 
          status = input[4]
          
          return { 'AIN' : result, 'ResolutionIndex' : resolutionIndex, 'GainIndex' : gainIndex, 'Status' : status }   
  
  class WaitShort(FeedbackCommand):
      '''
      WaitShort Feedback command
  
      specify the number of 64 microseconds time increments to wait (0-255)
  
      >>> d.getFeedback( u6.WaitShort( Time ) )
      [ None ]
      '''
      def __init__(self, Time):
          self.time = Time % 256
          self.cmdBytes = [ 5, Time % 256 ]
  
      def __repr__(self):
          return "<u6.WaitShort( Time = %s )>" % self.time
  
  class WaitLong(FeedbackCommand):
      '''
      WaitLong Feedback command
  
      specify the number of 16384 microseconds time increments to wait (0-255)
  
      >>> d.getFeedback( u6.WaitLog( Time ) )
      [ None ]
      '''
      def __init__(self, Time):
          self.time = Time
          self.cmdBytes = [ 6, Time % 256 ]
  
      def __repr__(self):
          return "<u6.WaitLog( Time = %s )>" % self.time
  
  class LED(FeedbackCommand):
      '''
      LED Toggle
  
      specify whether the LED should be on or off by truth value
      
      1 or True = On, 0 or False = Off
      
      >>> d.getFeedback( u6.LED( State ) )
      [ None ]
      '''
      def __init__(self, State):
          self.state = State
          self.cmdBytes = [ 9, int(bool(State)) ]
          
      def __repr__(self):
          return "<u6.LED( State = %s )>" % self.state
  
  class BitStateRead(FeedbackCommand):
      '''
      BitStateRead Feedback command
  
      read the state of a single bit of digital I/O.  Only digital lines return
      valid readings.
  
      IONumber: 0-7=FIO, 8-15=EIO, 16-19=CIO
      return 0 or 1
      
      >>> d.getFeedback( u6.BitStateRead( IONumber ) )
      [ 1 ]
      '''
      def __init__(self, IONumber):
          self.ioNumber = IONumber
          self.cmdBytes = [ 10, IONumber % 20 ]
  
      def __repr__(self):
          return "<u6.BitStateRead( IONumber = %s )>" % self.ioNumber
  
      readLen = 1
  
      def handle(self, input):
          return int(bool(input[0]))
  
  class BitStateWrite(FeedbackCommand):
      '''
      BitStateWrite Feedback command
  
      write a single bit of digital I/O.  The direction of the specified line is
      forced to output.
  
      IONumber: 0-7=FIO, 8-15=EIO, 16-19=CIO
      State: 0 or 1
      
      >>> d.getFeedback( u6.BitStateWrite( IONumber, State ) )
      [ None ]
      '''
      def __init__(self, IONumber, State):
          self.ioNumber = IONumber
          self.state = State
          self.cmdBytes = [ 11, (IONumber % 20) + (int(bool(State)) << 7) ]
      
      def __repr__(self):
          return "<u6.BitStateWrite( IONumber = %s, State = %s )>" % self.ioNumber
  
  class BitDirRead(FeedbackCommand):
      '''
      Read the digital direction of one I/O
  
      IONumber: 0-7=FIO, 8-15=EIO, 16-19=CIO
      returns 1 = Output, 0 = Input
      
      >>> d.getFeedback( u6.BitDirRead( IONumber ) )
      [ 1 ]
      '''
      def __init__(self, IONumber):
          self.ioNumber = IONumber
          self.cmdBytes = [ 12, IONumber % 20 ]
  
      def __repr__(self):
          return "<u6.BitDirRead( IONumber = %s )>" % self.ioNumber
  
      readLen = 1
  
      def handle(self, input):
          return int(bool(input[0]))
  
  class BitDirWrite(FeedbackCommand):
      '''
      BitDirWrite Feedback command
  
      Set the digital direction of one I/O
  
      IONumber: 0-7=FIO, 8-15=EIO, 16-19=CIO
      Direction: 1 = Output, 0 = Input
      
      >>> d.getFeedback( u6.BitDirWrite( IONumber, Direction ) )
      [ None ] 
      '''
      def __init__(self, IONumber, Direction):
          self.ioNumber = IONumber
          self.direction = Direction
          self.cmdBytes = [ 13, (IONumber % 20) + (int(bool(Direction)) << 7) ]
          
      def __repr__(self):
          return "<u6.BitDirWrite( IONumber = %s, Direction = %s )>" % (self.ioNumber, self.direction)
  
  class PortStateRead(FeedbackCommand):
      """
      PortStateRead Feedback command
  
      Reads the state of all digital I/O.
      
      >>> d.getFeedback( u6.PortStateRead() )
      [ { 'FIO' : 10, 'EIO' : 0, 'CIO' : 0 } ]
      """
      def __init__(self):
          self.cmdBytes = [ 26 ]
          
      def __repr__(self):
          return "<u6.PortStateRead()>"
          
      readLen = 3
      
      def handle(self, input):
          return {'FIO' : input[0], 'EIO' : input[1], 'CIO' : input[2] }
  
  class PortStateWrite(FeedbackCommand):
      """
      PortStateWrite Feedback command
      
      State: A list of 3 bytes representing FIO, EIO, CIO
      WriteMask: A list of 3 bytes, representing which to update.
                 The Default is all ones.
      
      >>> d.getFeedback( u6.PortStateWrite( State,
                                            WriteMask = [ 0xff, 0xff, 0xff] ) )
      [ None ]
      """
      def __init__(self, State, WriteMask = [ 0xff, 0xff, 0xff]):
          self.state = State
          self.writeMask = WriteMask
          self.cmdBytes = [ 27 ] + WriteMask + State
          
      def __repr__(self):
          return "<u6.PortStateWrite( State = %s, WriteMask = %s )>" % (self.state, self.writeMask)
          
  class PortDirRead(FeedbackCommand):
      """
      PortDirRead Feedback command
      Reads the direction of all digital I/O.
      
      >>> d.getFeedback( u6.PortDirRead() )
      [ { 'FIO' : 10, 'EIO' : 0, 'CIO' : 0 } ]
      """
      def __init__(self):
          self.cmdBytes = [ 28 ]
      
      def __repr__(self):
          return "<u6.PortDirRead()>"
      
      readLen = 3
      
      def handle(self, input):
          return {'FIO' : input[0], 'EIO' : input[1], 'CIO' : input[2] }
  
  class PortDirWrite(FeedbackCommand):
      """
      PortDirWrite Feedback command
      
      Direction: A list of 3 bytes representing FIO, EIO, CIO
      WriteMask: A list of 3 bytes, representing which to update. Default is all ones.
      
      >>> d.getFeedback( u6.PortDirWrite( Direction, 
                                          WriteMask = [ 0xff, 0xff, 0xff] ) )
      [ None ]
      """
      def __init__(self, Direction, WriteMask = [ 0xff, 0xff, 0xff]):
          self.direction = Direction
          self.writeMask = WriteMask
          self.cmdBytes = [ 29 ] + WriteMask + Direction
          
      def __repr__(self):
          return "<u6.PortDirWrite( Direction = %s, WriteMask = %s )>" % (self.direction, self.writeMask)
      
  class DAC8(FeedbackCommand):
      '''
      8-bit DAC Feedback command
      
      Controls a single analog output
  
      Dac: 0 or 1
      Value: 0-255
      
      >>> d.getFeedback( u6.DAC8( Dac, Value ) )
      [ None ]
      '''
      def __init__(self, Dac, Value):
          self.dac = Dac
          self.value = Value % 256
          self.cmdBytes = [ 34 + (Dac % 2), Value % 256 ]
      
      def __repr__(self):
          return "<u6.DAC8( Dac = %s, Value = %s )>" % (self.dac, self.value)
          
  class DAC0_8(DAC8):
      """
      8-bit DAC Feedback command for DAC0
      
      Controls DAC0 in 8-bit mode.
  
      Value: 0-255
      
      >>> d.getFeedback( u6.DAC0_8( Value ) )
      [ None ]
      """
      def __init__(self, Value):
          DAC8.__init__(self, 0, Value)
  
      def __repr__(self):
          return "<u6.DAC0_8( Value = %s )>" % self.value
  
  class DAC1_8(DAC8):
      """
      8-bit DAC Feedback command for DAC1
      
      Controls DAC1 in 8-bit mode.
  
      Value: 0-255
      
      >>> d.getFeedback( u6.DAC1_8( Value ) )
      [ None ]
      """
      def __init__(self, Value):
          DAC8.__init__(self, 1, Value)
      
      def __repr__(self):
          return "<u6.DAC1_8( Value = %s )>" % self.value
  
  class DAC16(FeedbackCommand):
      '''
      16-bit DAC Feedback command
  
      Controls a single analog output
  
      Dac: 0 or 1
      Value: 0-65535
      
      >>> d.getFeedback( u6.DAC16( Dac, Value ) )
      [ None ]
      '''
      def __init__(self, Dac, Value):
          self.dac = Dac
          self.value = Value
          self.cmdBytes = [ 38 + (Dac % 2), Value % 256, Value >> 8 ]
      
      def __repr__(self):
          return "<u6.DAC8( Dac = %s, Value = %s )>" % (self.dac, self.value)
  
  class DAC0_16(DAC16):
      """
      16-bit DAC Feedback command for DAC0
      
      Controls DAC0 in 16-bit mode.
  
      Value: 0-65535
      
      >>> d.getFeedback( u6.DAC0_16( Value ) )
      [ None ]
      """
      def __init__(self, Value):
          DAC16.__init__(self, 0, Value)
      
      def __repr__(self):
          return "<u6.DAC0_16( Value = %s )>" % self.value
  
  class DAC1_16(DAC16):
      """
      16-bit DAC Feedback command for DAC1
      
      Controls DAC1 in 16-bit mode.
  
      Value: 0-65535
      
      >>> d.getFeedback( u6.DAC1_16( Value ) )
      [ None ]
      """
      def __init__(self, Value):
          DAC16.__init__(self, 1, Value)
          
      def __repr__(self):
          return "<u6.DAC1_16( Value = %s )>" % self.value
  
          
  class Timer(FeedbackCommand):
      """
      For reading the value of the Timer. It provides the ability to update/reset
      a given timer, and read the timer value.
      ( Section 5.2.5.17 of the User's Guide)
      
      timer: 0 to 3 for timer0 to timer3
      
      UpdateReset: Set True if you want to update the value
      
      Value: Only updated if the UpdateReset bit is 1.  The meaning of this
             parameter varies with the timer mode.
  
      Mode: Set to the timer mode to handle any special processing. See classes
            QuadratureInputTimer and TimerStopInput1.
  
      Returns an unsigned integer of the timer value, unless Mode has been
      specified and there are special return values. See Section 2.9.1 for
      expected return values. 
  
      >>> d.getFeedback( u6.Timer( timer, UpdateReset = False, Value = 0 \
      ... , Mode = None ) )
      [ 12314 ]
      """
      def __init__(self, timer, UpdateReset = False, Value=0, Mode = None):
          if timer not in range(4):
              raise LabJackException("Timer should be 0-3.")
          if UpdateReset and (Value is None):
              raise LabJackException("UpdateReset set but no value.")
          
          self.timer = timer
          self.updateReset = UpdateReset
          self.value = Value
          self.mode = Mode
          
          self.cmdBytes = [ (42 + (2*timer)), UpdateReset, Value % 256, Value >> 8 ]
      
      readLen = 4
      
      def __repr__(self):
          return "<u6.Timer( timer = %s, UpdateReset = %s, Value = %s, Mode = %s )>" % (self.timer, self.updateReset, self.value, self.mode)
      
      def handle(self, input):
          inStr = pack('B' * len(input), *input)
          if self.mode == 8:
              return unpack('<i', inStr )[0]
          elif self.mode == 9:
              maxCount, current = unpack('<HH', inStr )
              return current, maxCount
          else:
              return unpack('<I', inStr )[0]
  
  
  class Timer0(Timer):
      """
      For reading the value of Timer0. It provides the ability to update/reset
      Timer0, and read the timer value.
      (Section 5.2.5.17 of the User's Guide)
      
      UpdateReset: Set True if you want to update the value
      
      Value: Only updated if the UpdateReset bit is 1.  The meaning of this
             parameter varies with the timer mode.
  
      Mode: Set to the timer mode to handle any special processing. See classes
            QuadratureInputTimer and TimerStopInput1.
  
      >>> d.getFeedback( u6.Timer0( UpdateReset = False, Value = 0, \
      ... Mode = None ) )
      [ 12314 ]
      """
      def __init__(self, UpdateReset = False, Value = 0, Mode = None):
          Timer.__init__(self, 0, UpdateReset, Value, Mode)
          
      def __repr__(self):
          return "<u6.Timer0( UpdateReset = %s, Value = %s, Mode = %s )>" % (self.updateReset, self.value, self.mode)
  
  
  class Timer1(Timer):
      """
      For reading the value of Timer1. It provides the ability to update/reset
      Timer1, and read the timer value.
      (Section 5.2.5.17 of the User's Guide)
      
      UpdateReset: Set True if you want to update the value
      
      Value: Only updated if the UpdateReset bit is 1.  The meaning of this
             parameter varies with the timer mode.
  
      Mode: Set to the timer mode to handle any special processing. See classes
            QuadratureInputTimer and TimerStopInput1.
  
      >>> d.getFeedback( u6.Timer1( UpdateReset = False, Value = 0, \
      ... Mode = None ) )
      [ 12314 ]
      """
      def __init__(self, UpdateReset = False, Value = 0, Mode = None):
          Timer.__init__(self, 1, UpdateReset, Value, Mode)
      
      def __repr__(self):
          return "<u6.Timer1( UpdateReset = %s, Value = %s, Mode = %s )>" % (self.updateReset, self.value, self.mode)
  
  
  class Timer2(Timer):
      """
      For reading the value of Timer2. It provides the ability to update/reset
      Timer2, and read the timer value.
      (Section 5.2.5.17 of the User's Guide)
      
      UpdateReset: Set True if you want to update the value
      
      Value: Only updated if the UpdateReset bit is 1.  The meaning of this
             parameter varies with the timer mode.
  
      Mode: Set to the timer mode to handle any special processing. See classes
            QuadratureInputTimer and TimerStopInput1.
  
      >>> d.getFeedback( u6.Timer2( UpdateReset = False, Value = 0, \
      ... Mode = None ) )
      [ 12314 ]
      """
      def __init__(self, UpdateReset = False, Value = 0, Mode = None):
          Timer.__init__(self, 2, UpdateReset, Value, Mode)
      
      def __repr__(self):
          return "<u6.Timer2( UpdateReset = %s, Value = %s, Mode = %s )>" % (self.updateReset, self.value, self.mode)
  
  
  class Timer3(Timer):
      """
      For reading the value of Timer3. It provides the ability to update/reset
      Timer3, and read the timer value.
      (Section 5.2.5.17 of the User's Guide)
      
      UpdateReset: Set True if you want to update the value
      
      Value: Only updated if the UpdateReset bit is 1.  The meaning of this
             parameter varies with the timer mode.
  
      Mode: Set to the timer mode to handle any special processing. See classes
            QuadratureInputTimer and TimerStopInput1.
  
      >>> d.getFeedback( u6.Timer3( UpdateReset = False, Value = 0, \
      ... Mode = None ) )
      [ 12314 ]
      """
      def __init__(self, UpdateReset = False, Value = 0, Mode = None):
          Timer.__init__(self, 3, UpdateReset, Value, Mode)
      
      def __repr__(self):
          return "<u6.Timer3( UpdateReset = %s, Value = %s, Mode = %s )>" % (self.updateReset, self.value, self.mode)
  
  
  class QuadratureInputTimer(Timer):
      """
      For reading Quadrature input timers. They are special because their values
      are signed.
      
      ( Section 2.9.1.8 of the User's Guide)
      
      Args:
         UpdateReset: Set True if you want to reset the counter.
         Value: Set to 0, and UpdateReset to True to reset the counter.
      
      Returns a signed integer.
      
      >>> # Setup the two timers to be quadrature
      >>> d.getFeedback( u6.Timer0Config( 8 ), u6.Timer1Config( 8 ) )
      [None, None]
      >>> # Read the value
      >>> d.getFeedback( u6.QuadratureInputTimer() )
      [-21]
      """
      def __init__(self, UpdateReset = False, Value = 0):
          Timer.__init__(self, 0, UpdateReset, Value, Mode = 8)
          
      def __repr__(self):
          return "<u6.QuadratureInputTimer( UpdateReset = %s, Value = %s )>" % (self.updateReset, self.value)
  
  
  class TimerStopInput1(Timer1):
      """
      For reading a stop input timer. They are special because the value returns
      the current edge count and the stop value.
      
      ( Section 2.9.1.9 of the User's Guide)
      
      Args:
          UpdateReset: Set True if you want to update the value.
          Value: The stop value. Only updated if the UpdateReset bit is 1.
      
      Returns a tuple where the first value is current edge count, and the second
      value is the stop value.
      
      >>> # Setup the timer to be Stop Input
      >>> d.getFeedback( u6.Timer0Config( 9, Value = 30 ) )
      [None]
      >>> # Read the timer
      >>> d.getFeedback( u6.TimerStopInput1() )
      [(0, 30)]
      """
      def __init__(self, UpdateReset = False, Value = 0):
          Timer.__init__(self, 1, UpdateReset, Value, Mode = 9)
      
      def __repr__(self):
          return "<u6.TimerStopInput1( UpdateReset = %s, Value = %s )>" % (self.updateReset, self.value)
  
  
  class TimerConfig(FeedbackCommand):
      """
      This IOType configures a particular timer.
      
      timer: # of the timer to configure
      
      TimerMode: See Section 2.9 for more information about the available modes.
      
      Value: The meaning of this parameter varies with the timer mode.
      
      >>> d.getFeedback( u6.TimerConfig( timer, TimerMode, Value = 0 ) )
      [ None ]
      """
      def __init__(self, timer, TimerMode, Value=0):
          '''Creates command bytes for configuring a Timer'''
          if timer not in range(4):
              raise LabJackException("Timer should be 0-3.")
          
          if TimerMode > 14 or TimerMode < 0:
              raise LabJackException("Invalid Timer Mode.")
          
          self.timer = timer
          self.timerMode = TimerMode
          self.value = Value
          
          self.cmdBytes = [43 + (timer * 2), TimerMode, Value % 256, Value >> 8]
      
      def __repr__(self):
          return "<u6.TimerConfig( timer = %s, TimerMode = %s, Value = %s )>" % (self.timer, self.timerMode, self.value)
  
  
  class Timer0Config(TimerConfig):
      """
      This IOType configures Timer0.
      
      TimerMode: See Section 2.9 for more information about the available modes.
      
      Value: The meaning of this parameter varies with the timer mode.
      
      >>> d.getFeedback( u6.Timer0Config( TimerMode, Value = 0 ) )
      [ None ]
      """
      def __init__(self, TimerMode, Value = 0):
          TimerConfig.__init__(self, 0, TimerMode, Value)
      
      def __repr__(self):
          return "<u6.Timer0Config( TimerMode = %s, Value = %s )>" % (self.timerMode, self.value)
  
  
  class Timer1Config(TimerConfig):
      """
      This IOType configures Timer1.
      
      TimerMode: See Section 2.9 for more information about the available modes.
      
      Value: The meaning of this parameter varies with the timer mode.
      
      >>> d.getFeedback( u6.Timer1Config( TimerMode, Value = 0 ) )
      [ None ]
      """
      def __init__(self, TimerMode, Value = 0):
          TimerConfig.__init__(self, 1, TimerMode, Value)
      
      def __repr__(self):
          return "<u6.Timer1Config( TimerMode = %s, Value = %s )>" % (self.timerMode, self.value)
  
  
  class Timer2Config(TimerConfig):
      """
      This IOType configures Timer2.
      
      TimerMode: See Section 2.9 for more information about the available modes.
      
      Value: The meaning of this parameter varies with the timer mode.
      
      >>> d.getFeedback( u6.Timer2Config( TimerMode, Value = 0 ) )
      [ None ]
      """
      def __init__(self, TimerMode, Value = 0):
          TimerConfig.__init__(self, 2, TimerMode, Value)
      
      def __repr__(self):
          return "<u6.Timer2Config( TimerMode = %s, Value = %s )>" % (self.timerMode, self.value)
  
  
  class Timer3Config(TimerConfig):
      """
      This IOType configures Timer3.
      
      TimerMode: See Section 2.9 for more information about the available modes.
      
      Value: The meaning of this parameter varies with the timer mode.
      
      >>> d.getFeedback( u6.Timer3Config( TimerMode, Value = 0 ) )
      [ None ]
      """
      def __init__(self, TimerMode, Value = 0):
          TimerConfig.__init__(self, 3, TimerMode, Value)
      
      def __repr__(self):
          return "<u6.Timer3Config( TimerMode = %s, Value = %s )>" % (self.timerMode, self.value)
  
  
  class Counter(FeedbackCommand):
      '''
      Counter Feedback command
  
      Reads a hardware counter, optionally resetting it
  
      counter: 0 or 1
      Reset: True ( or 1 ) = Reset, False ( or 0 ) = Don't Reset
  
      Returns the current count from the counter if enabled.  If reset,
      this is the value before the reset.
      
      >>> d.getFeedback(u6.Counter(counter, Reset = False))
      [ 2183 ]
      '''
      def __init__(self, counter, Reset):
          self.counter = counter
          self.reset = Reset
          self.cmdBytes = [54 + (counter % 2), int(bool(Reset))]
  
      def __repr__(self):
          return "<u6.Counter( counter = %s, Reset = %s )>" % (self.counter, self.reset)
  
      readLen = 4
  
      def handle(self, input):
          inStr = pack('B' * len(input), *input)
          return unpack('<I', inStr)[0]
  
  
  class Counter0(Counter):
      '''
      Counter0 Feedback command
  
      Reads hardware counter0, optionally resetting it
  
      Reset: True ( or 1 ) = Reset, False ( or 0 ) = Don't Reset
  
      Returns the current count from the counter if enabled.  If reset,
      this is the value before the reset.
      
      >>> d.getFeedback(u6.Counter0(Reset = False))
      [ 2183 ]
      '''
      def __init__(self, Reset = False):
          Counter.__init__(self, 0, Reset)
  
      def __repr__(self):
          return "<u6.Counter0( Reset = %s )>" % self.reset
  
  
  class Counter1(Counter):
      '''
      Counter1 Feedback command
  
      Reads hardware counter1, optionally resetting it
  
      Reset: True ( or 1 ) = Reset, False ( or 0 ) = Don't Reset
  
      Returns the current count from the counter if enabled.  If reset,
      this is the value before the reset.
  
      >>> d.getFeedback(u6.Counter1(Reset = False))
      [ 2183 ]
      '''
      def __init__(self, Reset = False):
          Counter.__init__(self, 1, Reset)
  
      def __repr__(self):
          return "<u6.Counter1( Reset = %s )>" % self.reset
  