Example #1
0
        def max_travel(self):
            """
            Gets the maximum travel for the specified piezo channel.

            :type: `~quantities.Quantity`
            :units: Nanometers
            """
            pkt = _packets.ThorLabsPacket(
                message_id=_cmds.ThorLabsCommands.PZ_REQ_MAXTRAVEL,
                param1=self._idx_chan,
                param2=0x00,
                dest=self._apt.destination,
                source=0x01,
                data=None)
            resp = self._apt.querypacket(pkt)

            # Not all APT piezo devices support querying the maximum travel
            # distance. Those that do not simply ignore the PZ_REQ_MAXTRAVEL
            # packet, so that the response is empty.
            if resp is None:
                return NotImplemented

            # chan, int_maxtrav
            _, int_maxtrav = struct.unpack('<HH', resp.data)
            return int_maxtrav * u.Quantity(100, 'nm')
Example #2
0
        def status_bits(self):
            """
            Gets the status bits for the specified motor channel.

            :type: `dict`
            """
            # NOTE: the difference between MOT_REQ_STATUSUPDATE and
            # MOT_REQ_DCSTATUSUPDATE confuses me
            pkt = _packets.ThorLabsPacket(
                message_id=_cmds.ThorLabsCommands.MOT_REQ_STATUSUPDATE,
                param1=self._idx_chan,
                param2=0x00,
                dest=self._apt.destination,
                source=0x01,
                data=None)
            # The documentation claims there are 14 data bytes, but it seems
            # there are sometimes some extra random ones...
            resp_data = self._apt.querypacket(pkt).data[:14]
            # ch_ident, position, enc_count, status_bits
            _, _, _, status_bits = struct.unpack('<HLLL', resp_data)

            status_dict = dict(
                (key, (status_bits & bit_mask > 0))
                for key, bit_mask in self.__STATUS_BIT_MASK.items())

            return status_dict
Example #3
0
 def move(self, pos, absolute=True):
     # Handle units as follows:
     # 1. Treat raw numbers as encoder counts.
     # 2. If units are provided (as a Quantity), check if they're encoder
     #    counts. If they aren't, apply scale factor.
     if not isinstance(pos, pq.Quantity):
         pos_ec = int(pos)
     else:
         if pos.units == pq.counts:
             pos_ec = int(pos.magnitude)
         else:
             scaled_pos = (pos * self.scale_factors[0])
             # Force a unit error.
             try:
                 pos_ec = int(scaled_pos.rescale(pq.counts).magnitude)
             except:
                 raise ValueError("Provided units are not compatible with current motor scale factor.")
     
     # Now that we have our position as an integer number of encoder
     # counts, we're good to move.
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.MOT_MOVE_ABSOLUTE if absolute else _cmds.ThorLabsCommands.MOT_MOVE_RELATIVE,
                                   param1=None,
                                   param2=None,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=struct.pack('<Hl', self._idx_chan, pos_ec))
                                   
     response = self._apt.querypacket(pkt, expect=_cmds.ThorLabsCommands.MOT_MOVE_COMPLETED)
def test_pack_with_data(params_or_data):
    """Pack a package with data."""
    message_id = 0x0000
    param1 = params_or_data[0]
    param2 = params_or_data[1]
    dest = 0x50
    source = 0x01
    data = params_or_data[2]
    pkt = _packets.ThorLabsPacket(
        message_id=message_id,
        param1=param1,
        param2=param2,
        dest=dest,
        source=source,
        data=data,
    )

    if params_or_data[3]:
        message_header = struct.Struct("<HHBB")
        pkt_expected = message_header.pack(message_id, len(data), 0x80 | dest,
                                           source) + data
    else:
        message_header = struct.Struct("<HBBBB")
        pkt_expected = message_header.pack(message_id, param1, param2, dest,
                                           source)
    assert pkt.pack() == pkt_expected
def test_string(params_or_data):
    """Return a string of the class."""
    message_id = 0x0000
    param1 = params_or_data[0]
    param2 = params_or_data[1]
    dest = 0x50
    source = 0x01
    data = params_or_data[2]
    pkt = _packets.ThorLabsPacket(
        message_id=message_id,
        param1=param1,
        param2=param2,
        dest=dest,
        source=source,
        data=data,
    )
    string_expected = """
ThorLabs APT packet:
    Message ID      {0}
    Parameter 1     {1}
    Parameter 2     {2}
    Destination     {3}
    Source          {4}
    Data            {5}
""".format(f"0x{message_id:x}",
           f"0x{param1:x}" if not params_or_data[3] else "None",
           f"0x{param2:x}" if not params_or_data[3] else "None", f"0x{dest:x}",
           f"0x{source:x}", f"{data}" if params_or_data[3] else "None")
    assert pkt.__str__() == string_expected
Example #6
0
    def __init__(self, filelike):
        super(ThorLabsAPT, self).__init__(filelike)
        self._dest = 0x50  # Generic USB device; make this configurable later.

        # Provide defaults in case an exception occurs below.
        self._serial_number = None
        self._model_number = None
        self._hw_type = None
        self._fw_version = None
        self._notes = ""
        self._hw_version = None
        self._mod_state = None
        self._n_channels = 0
        self._channel = ()

        # Perform a HW_REQ_INFO to figure out the model number, serial number,
        # etc.
        try:
            req_packet = _packets.ThorLabsPacket(
                message_id=_cmds.ThorLabsCommands.HW_REQ_INFO,
                param1=0x00,
                param2=0x00,
                dest=self._dest,
                source=0x01,
                data=None)
            hw_info = self.querypacket(
                req_packet, expect=_cmds.ThorLabsCommands.HW_GET_INFO)

            self._serial_number = str(hw_info.data[0:4]).encode('hex')
            self._model_number = str(hw_info.data[4:12]).replace('\x00',
                                                                 '').strip()

            hw_type_int = struct.unpack('<H', str(hw_info.data[12:14]))[0]
            if hw_type_int == 45:
                self._hw_type = 'Multi-channel controller motherboard'
            elif hw_type_int == 44:
                self._hw_type = 'Brushless DC controller'
            else:
                self._hw_type = 'Unknown type: {}'.format(hw_type_int)

            # Note that the fourth byte is padding, so we strip out the first
            # three bytes and format them.
            # pylint: disable=invalid-format-index
            self._fw_version = "{0[0]}.{0[1]}.{0[2]}".format(
                str(hw_info.data[14:18]).encode('hex'))
            self._notes = str(hw_info.data[18:66]).replace('\x00', '').strip()

            self._hw_version = struct.unpack('<H', str(hw_info.data[78:80]))[0]
            self._mod_state = struct.unpack('<H', str(hw_info.data[80:82]))[0]
            self._n_channels = struct.unpack('<H', str(hw_info.data[82:84]))[0]
        except IOError as e:
            logger.error("Exception occured while fetching hardware info: %s",
                         e)

        # Create a tuple of channels of length _n_channel_type
        if self._n_channels > 0:
            self._channel = tuple(
                self._channel_type(self, chan_idx)
                for chan_idx in range(self._n_channels))
Example #7
0
 def go_home(self):
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.MOT_MOVE_HOME,
                                   param1=self._idx_chan,
                                   param2=0x00,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=None)
     self._apt.sendpacket(pkt)
Example #8
0
 def output_position(self, pos):
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.PZ_SET_OUTPUTPOS,
                                   param1=None,
                                   param2=None,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=struct.pack('<HH', self._idx_chan, pos))
     self._apt.sendpacket(pkt)
Example #9
0
 def enabled(self, newval):
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.MOD_SET_CHANENABLESTATE,
                                   param1=self._idx_chan,
                                   param2=0x01 if newval else 0x02,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=None)
     self._apt.sendpacket(pkt)
Example #10
0
 def enabled(self):
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.MOD_REQ_CHANENABLESTATE,
                                   param1=self._idx_chan,
                                   param2=0x00,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=None)
     resp = self._apt.querypacket(pkt, expect=_cmds.ThorLabsCommands.MOD_GET_CHANENABLESTATE)
     return not bool(resp._param2 - 1)
Example #11
0
 def is_position_control_closed(self):
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.PZ_REQ_POSCONTROLMODE,
                                   param1=self._idx_chan,
                                   param2=0x00,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=None)
     resp = self._apt.querypacket(pkt, expect=_cmds.ThorLabsCommands.PZ_GET_POSCONTROLMODE)
     return bool((resp._param2 - 1) & 1)
Example #12
0
 def change_position_control_mode(self, closed, smooth=True):
     mode = 1 + (int(closed) | int(smooth) << 1)
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.PZ_SET_POSCONTROLMODE,
                                   param1=self._idx_chan,
                                   param2=mode,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=None)
     self._apt.sendpacket(pkt)
Example #13
0
 def position_encoder(self):
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.MOT_REQ_ENCCOUNTER,
                                   param1=self._idx_chan,
                                   param2=0x00,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=None)
     response = self._apt.querypacket(pkt, expect=_cmds.ThorLabsCommands.MOT_GET_ENCCOUNTER)
     chan, pos = struct.unpack('<Hl', response._data)
     return pq.Quantity(pos, 'counts')
def test_source():
    """Get / set source."""
    pkt = _packets.ThorLabsPacket(message_id=0x000,
                                  param1=0x01,
                                  param2=0x02,
                                  dest=0x50,
                                  source=0x01,
                                  data=None)
    pkt.source = 0x2A
    assert pkt.source == 0x2A
def test_destination():
    """Get / set destination."""
    pkt = _packets.ThorLabsPacket(message_id=0x000,
                                  param1=0x01,
                                  param2=0x02,
                                  dest=0x50,
                                  source=0x01,
                                  data=None)
    pkt.destination = 0x2A
    assert pkt.destination == 0x2A
Example #16
0
 def output_position(self):
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.PZ_REQ_OUTPUTPOS,
                                   param1=self._idx_chan,
                                   param2=0x00,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=None)
     resp = self._apt.querypacket(pkt, expect=_cmds.ThorLabsCommands.PZ_GET_OUTPUTPOS)
     chan, pos = struct.unpack('<HH', resp._data)
     return pos
def test_parameters():
    """Get / set both parameters."""
    pkt = _packets.ThorLabsPacket(message_id=0x000,
                                  param1=0x01,
                                  param2=0x02,
                                  dest=0x50,
                                  source=0x01,
                                  data=None)
    pkt.parameters = (0x0D, 0x2A)
    assert pkt.parameters == (0x0D, 0x2A)
Example #18
0
 def led_intensity(self, intensity):
     # pylint: disable=round-builtin
     pkt = _packets.ThorLabsPacket(
         message_id=_cmds.ThorLabsCommands.PZ_SET_TPZ_DISPSETTINGS,
         param1=None,
         param2=None,
         dest=self._dest,
         source=0x01,
         data=struct.pack('<H', int(round(255 * intensity))))
     self.sendpacket(pkt)
def test_data():
    """Get / set data."""
    pkt = _packets.ThorLabsPacket(message_id=0x000,
                                  param1=None,
                                  param2=None,
                                  dest=0x50,
                                  source=0x01,
                                  data=0x00)
    data = struct.pack("<H", 0x2A)
    pkt.data = data
    assert pkt.data == data
Example #20
0
 def identify(self):
     '''
     Causes a light on the APT instrument to blink, so that it can be
     identified.
     '''
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.MOD_IDENTIFY,
                                   param1=0x00,
                                   param2=0x00,
                                   dest=self._dest,
                                   source=0x01,
                                   data=None)
     self.sendpacket(pkt)
def test_unpack_empty_packet():
    """Raise ValueError if trying to unpack empty string."""
    pkt = _packets.ThorLabsPacket(message_id=0x000,
                                  param1=0x01,
                                  param2=0x02,
                                  dest=0x50,
                                  source=0x01,
                                  data=None)
    with pytest.raises(ValueError) as err_info:
        pkt.unpack("")
    err_msg = err_info.value.args[0]
    assert err_msg == "Expected a packet, got an empty string instead."
def test_unpack_too_short():
    """Raise ValueError if trying to unpack to short value."""
    pkt = _packets.ThorLabsPacket(message_id=0x000,
                                  param1=0x01,
                                  param2=0x02,
                                  dest=0x50,
                                  source=0x01,
                                  data=None)
    too_short_package = struct.pack("<HH", 0x01, 0x02)
    with pytest.raises(ValueError) as err_info:
        pkt.unpack(too_short_package)
    err_msg = err_info.value.args[0]
    assert err_msg == "Packet must be at least 6 bytes long."
Example #23
0
 def led_intensity(self, intensity):
     '''
     The output intensity of the LED display.
     
     :param float intensity: Ranges from 0 to 1.
     '''
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.PZ_SET_TPZ_DISPSETTINGS,
                                   param1=None,
                                   param2=None,
                                   dest=self._dest,
                                   source=0x01,
                                   data=struct.pack('<H', int(round(255*intensity))))
     self.sendpacket(pkt)
Example #24
0
 def go_home(self):
     """
     Instructs the specified motor channel to return to its home
     position
     """
     pkt = _packets.ThorLabsPacket(
         message_id=_cmds.ThorLabsCommands.MOT_MOVE_HOME,
         param1=self._idx_chan,
         param2=0x00,
         dest=self._apt.destination,
         source=0x01,
         data=None)
     self._apt.sendpacket(pkt)
Example #25
0
 def led_intensity(self):
     '''
     The output intensity of the LED display.
     
     :type: `float` between 0 and 1.
     '''
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.PZ_REQ_TPZ_DISPSETTINGS,
                                   param1=0x01,
                                   param2=0x00,
                                   dest=self._dest,
                                   source=0x01,
                                   data=None)
     resp = self.querypacket(pkt)
     return float(struct.unpack('<H', resp._data)[0])/255
Example #26
0
        def move(self, pos, absolute=True):
            """
            Instructs the specified motor channel to move to a specific
            location. The provided position can be either an absolute or
            relative position.

            :param pos: The position to move to. Provided value will be
                converted to encoder counts.
            :type pos: `~quantities.Quantity`
            :units pos: As specified, or assumed to of units encoder counts

            :param bool absolute: Specify if the position is a relative or
                absolute position. ``True`` means absolute, while ``False``
                is for a relative move.
            """
            # Handle units as follows:
            # 1. Treat raw numbers as encoder counts.
            # 2. If units are provided (as a Quantity), check if they're encoder
            #    counts. If they aren't, apply scale factor.
            if not isinstance(pos, pq.Quantity):
                pos_ec = int(pos)
            else:
                if pos.units == pq.counts:
                    pos_ec = int(pos.magnitude)
                else:
                    scaled_pos = (pos * self.scale_factors[0])
                    # Force a unit error.
                    try:
                        pos_ec = int(scaled_pos.rescale(pq.counts).magnitude)
                    except:
                        raise ValueError("Provided units are not compatible "
                                         "with current motor scale factor.")

            # Now that we have our position as an integer number of encoder
            # counts, we're good to move.
            pkt = _packets.ThorLabsPacket(
                message_id=_cmds.ThorLabsCommands.MOT_MOVE_ABSOLUTE if absolute
                else _cmds.ThorLabsCommands.MOT_MOVE_RELATIVE,
                param1=None,
                param2=None,
                dest=self._apt.destination,
                source=0x01,
                data=struct.pack('<Hl', self._idx_chan, pos_ec)
            )

            _ = self._apt.querypacket(
                pkt,
                expect=_cmds.ThorLabsCommands.MOT_MOVE_COMPLETED,
                timeout=self.motion_timeout
            )
Example #27
0
        def enabled(self):
            """
            Gets/sets the enabled status for the specified APT channel

            :type: `bool`
            """
            pkt = _packets.ThorLabsPacket(
                message_id=_cmds.ThorLabsCommands.MOD_REQ_CHANENABLESTATE,
                param1=self._idx_chan,
                param2=0x00,
                dest=self._apt.destination,
                source=0x01,
                data=None)
            resp = self._apt.querypacket(
                pkt, expect=_cmds.ThorLabsCommands.MOD_GET_CHANENABLESTATE)
            return not bool(resp.parameters[1] - 1)
Example #28
0
        def change_position_control_mode(self, closed, smooth=True):
            """
            Changes the position control mode of the piezo channel

            :param bool closed: `True` for closed, `False` for open
            :param bool smooth: `True` for smooth, `False` for otherwise.
                Default is `True`.
            """
            mode = 1 + (int(closed) | int(smooth) << 1)
            pkt = _packets.ThorLabsPacket(
                message_id=_cmds.ThorLabsCommands.PZ_SET_POSCONTROLMODE,
                param1=self._idx_chan,
                param2=mode,
                dest=self._apt.destination,
                source=0x01,
                data=None)
            self._apt.sendpacket(pkt)
Example #29
0
 def max_travel(self):
     pkt = _packets.ThorLabsPacket(message_id=_cmds.ThorLabsCommands.PZ_REQ_MAXTRAVEL,
                                   param1=self._idx_chan,
                                   param2=0x00,
                                   dest=self._apt._dest,
                                   source=0x01,
                                   data=None)
     resp = self._apt.querypacket(pkt)
     
     # Not all APT piezo devices support querying the maximum travel
     # distance. Those that do not simply ignore the PZ_REQ_MAXTRAVEL
     # packet, so that the response is empty.
     if resp is None:
         return NotImplemented
     
     chan, int_maxtrav = struct.unpack('<HH', resp._data)
     return int_maxtrav * pq.Quantity(100, 'nm')
Example #30
0
        def position(self):
            """
            Gets the current position of the specified motor channel

            :type: `~quantities.Quantity`
            """
            pkt = _packets.ThorLabsPacket(
                message_id=_cmds.ThorLabsCommands.MOT_REQ_POSCOUNTER,
                param1=self._idx_chan,
                param2=0x00,
                dest=self._apt.destination,
                source=0x01,
                data=None)
            response = self._apt.querypacket(
                pkt, expect=_cmds.ThorLabsCommands.MOT_GET_POSCOUNTER)
            # chan, pos
            _, pos = struct.unpack('<Hl', response.data)
            return u.Quantity(pos, 'counts') / self.scale_factors[0]