Ejemplo n.º 1
0
    def __init__(self, port, smcID=(1, ), **kwargs):
        """
        If backlash_compensation is False, no backlash compensation will be done.
        If silent is False, then additional output will be emitted to aid in
        debugging.
        If sleepfunc is not None, then it will be used instead of time.sleep. It
        will be given the number of seconds (float) to sleep for, and is provided
        for ease integration with single threaded GUIs.
        Note that this method only connects to the controller, it otherwise makes
        no attempt to home or configure the controller for the attached stage. This
        delibrate to minimise realworld side effects.
        If the controller has previously been configured, it will suffice to simply
        call home() to take the controller out of not referenced mode. For a brand
        new controller, call reset_and_configure().
        """
        self.port_settings = dict(baudrate=57600,
                                  bytesize=8,
                                  stopbits=1,
                                  parity='N',
                                  xonxoff=True,
                                  timeout=0.050)

        SerialInstrument.__init__(self, port)
        Stage.__init__(self)
        # self._logger.debug('Connecting to SMC100 on %s' % (port))

        self.software_home = None
        self._last_sendcmd_time = 0
        if not hasattr(smcID, '__iter__'):
            smcID = (smcID, )
        self._smcID = list(smcID)
        self.axis_names = ()
        for id in self._smcID:
            self.axis_names += (str(id), )
            self._send_cmd('ID', id, '?', True)  # Just testing the connection
Ejemplo n.º 2
0
    def __init__(self,port=None,device_index=0,debug=0):
        self.debug = debug
        self.termination_character = '\n'
        self.port_settings = dict(baudrate=9600,
                                  bytesize=8,
                                  stopbits=1,
                                  parity='N',
                                  timeout=2,
                                  writeTimeout=2,
                                  xonxoff=False
        )

        SerialInstrument.__init__(self, port)
        Stage.__init__(self)
        self.ui = None

        #configure stage parameters
        if str(device_index) not in Thorlabs_ELL8K.VALID_DEVICE_IDs:
            raise ValueError("Device ID: {} is not valid!".format(device_index))
        self.device_index = device_index
        
        configuration = self.get_device_info()
        self.TRAVEL = configuration["travel"]
        self.PULSES_PER_REVOLUTION = configuration["pulses"]
        
        if self.debug > 0:
            print "Travel (degrees):", self.TRAVEL
            print "Pulses per revolution", self.PULSES_PER_REVOLUTION
            print "Device status:",self.get_device_status()
Ejemplo n.º 3
0
 def __init__(self, SerialNum, HWType=22):
     Stage.__init__(self, unit="u")
     APTMotor.__init__(self, SerialNum=SerialNum, HWTYPE=HWType)
     self.axis_names = ["deg"]
     self.zero_pos = 0.0
     self.serial_num = SerialNum
     self.ui = None
Ejemplo n.º 4
0
    def __init__(self,
                 port=None,
                 source=0x01,
                 destination=None,
                 use_si_units=False,
                 stay_alive=False,
                 unit='m',
                 **kwargs):
        """
        Set up the serial port, setting source and destinations, and hardware info.
        """
        APT_VCP.__init__(self,
                         port=port,
                         source=source,
                         destination=destination,
                         use_si_units=use_si_units,
                         stay_alive=stay_alive)  # this opens the port
        Stage.__init__(self, unit=unit)
        if self.model[1] in DC_status_motors:
            # Set the bit mask for DC controllers
            self.status_bit_mask = np.array(
                [[0x00000001, 'forward hardware limit switch is active'],
                 [0x00000002, 'reverse hardware limit switch is active'],
                 [0x00000010, 'in motion, moving forward'],
                 [0x00000020, 'in motion, moving reverse'],
                 [0x00000040, 'in motion, jogging forward'],
                 [0x00000080, 'in motion, jogging reverse'],
                 [0x00000200, 'in motion, homing'],
                 [0x00000400, 'homed (homing has been completed)'],
                 [0x00001000, 'tracking'], [0x00002000, 'settled'],
                 [0x00004000, 'motion error (excessive position error)'],
                 [0x01000000, 'motor current limit reached'],
                 [0x80000000, 'channel is enabled']])
            self.velocity_scaling_factor = 204.8  # for converting velocity to mm/sec
        else:
            # Set the bit mask for normal motor controllers
            self.status_bit_mask = {
                0x00000001: 'forward (CW) hardware limit switch is active',
                0x00000002: 'reverse (CCW) hardware limit switch is active',
                0x00000004: 'forward (CW) software limit switch is active',
                0x00000008: 'reverse (CCW) software limit switch is active',
                0x00000010: 'in motion, moving forward (CW)',
                0x00000020: 'in motion, moving reverse (CCW)',
                0x00000040: 'in motion, jogging forward (CW)',
                0x00000080: 'in motion, jogging reverse (CCW)',
                0x00000100: 'motor connected',
                0x00000200: 'in motion, homing',
                0x00000400: 'homed (homing has been completed)',
                0x00001000: 'interlock state (1 = enabled)'
            }

            # delattr(self, 'get_qt_ui')
        if type(destination) != dict and len(self.destination) == 1:
            self.destination = {'x': destination}
        else:
            self.axis_names = tuple(destination.keys())
            self.destination = destination
        self.make_all_parameters()
Ejemplo n.º 5
0
	def __init__(self,port='/dev/ttyUSB1', source=0x01, destination=0x50):
		Stage.__init__(self,unit="u")
		APT_VCP_motor.__init__(self,port=port,destination=destination,source=source)
		self.axis_names=["x"]
		self.set_channel_state(1,1)
		self.zero_pos = 0.0
		self.ui = None
		print("initialized NR360SM")
		self.set_motion_params()
		# self.get_home_parameters()
		self.set_home_parameters()
		self.get_home_parameters()
Ejemplo n.º 6
0
    def __init__(self, address, **kwargs):

        self.port_settings = dict(baudrate=38400,
                                  bytesize=8,
                                  stopbits=1,
                                  parity='N',
                                  xonxoff=True,
                                  timeout=0.5,
                                  writeTimeout=0.5,
                                  rtscts=True)
        SerialInstrument.__init__(self, address)
        self.termination_character = '\r\n'
        Stage.__init__(self, unit="step")
Ejemplo n.º 7
0
 def __init__(self):
     ScanningExperiment.__init__(self)
     TimedScan.__init__(self)
     self.stage = Stage()
     self.stage_units = 1
     self.axes = list(self.stage.axis_names)
     self.axes_names = list(str(ax) for ax in self.stage.axis_names)
     self.size = 1. * np.ones(len(self.axes), dtype=np.float64)
     self.step = 0.05 * np.ones(len(self.axes), dtype=np.float64)
     self.init = np.zeros(len(self.axes), dtype=np.float64)
     self.scan_axes = None
     # underscored attributes are made into properties
     self._num_axes = len(self.axes)
     self._unit_conversion = {'nm': 1e-9, 'um': 1e-6, 'mm': 1e-3}
     self._size_unit, self._step_unit, self._init_unit = ('um', 'um', 'um')
     self.grid_shape = (0,0)
Ejemplo n.º 8
0
    def __init__(self, address, **kwargs):

        self.port_settings = dict(baudrate=9600,
                                  bytesize=8,
                                  stopbits=1,
                                  parity='N',
                                  xonxoff=True,
                                  timeout=0.5,
                                  writeTimeout=0.5,
                                  rtscts=True)
        SerialInstrument.__init__(self, address)
        self.termination_character = '\r\n'
        Stage.__init__(self)

        if 'offsetOrigin' in kwargs:
            self.offsetOrigin(kwargs['offsetOrigin'])  # 20000)

        if 'home_on_start' in list(kwargs.keys()):
            if kwargs['home_on_start']:
                self.MechanicalHome()
Ejemplo n.º 9
0
 def __init__(self, port=None,unit='u'):
     '''Set up baudrate etc and recenters the stage to the center of it's range (50um)
     
     Args:
         port(int/str):  The port the device is connected 
                         to in any of the accepted serial formats
         
     '''
     self.termination_character = '\n'
     self.port_settings = {
                 'baudrate':115200,
                 'bytesize':serial.EIGHTBITS,
                 'parity':serial.PARITY_NONE,
                 'stopbits':serial.STOPBITS_ONE,
                 'timeout':1, #wait at most one second for a response
       #          'writeTimeout':1, #similarly, fail if writing takes >1s
        #         'xonxoff':False, 'rtscts':False, 'dsrdtr':False,
                 }
     si.SerialInstrument.__init__(self,port=port)
     Stage.__init__(self)
     self.unit = unit #This can be 'u' for micron or 'n' for nano
     self.recenter()
Ejemplo n.º 10
0
    def __init__(self, camera=None, stage=None):
        # If no camera or stage is supplied, attempt to retrieve them - but crash with an exception if they don't exist.
        if camera is None:
            camera = Camera.get_instance(create=False)
        if stage is None:
            stage = Stage.get_instance(create=False)
        self.camera = camera
        self.stage = stage
        self.filter_images = False
        Instrument.__init__(self)

        shape = self.camera.color_image().shape
        self.datum_pixel = np.array(shape[:2])/2.0 # Default to using the centre of the image as the datum point
   #     self.camera.set_legacy_click_callback(self.move_to_feature_pixel)
        self.camera.set_legacy_click_callback(self.move_to_pixel)
Ejemplo n.º 11
0
class GridScan(ScanningExperiment, TimedScan):
    """
    Note that the axes (x,y,z) will relate to the indices (z,y,x) as per array standards.
    """

    def __init__(self):
        ScanningExperiment.__init__(self)
        TimedScan.__init__(self)
        self.stage = Stage()
        self.stage_units = 1
        self.axes = list(self.stage.axis_names)
        self.axes_names = list(str(ax) for ax in self.stage.axis_names)
        self.size = 1. * np.ones(len(self.axes), dtype=np.float64)
        self.step = 0.05 * np.ones(len(self.axes), dtype=np.float64)
        self.init = np.zeros(len(self.axes), dtype=np.float64)
        self.scan_axes = None
        # underscored attributes are made into properties
        self._num_axes = len(self.axes)
        self._unit_conversion = {'nm': 1e-9, 'um': 1e-6, 'mm': 1e-3}
        self._size_unit, self._step_unit, self._init_unit = ('um', 'um', 'um')
        self.grid_shape = (0,0)
        #self.init_grid(self.axes, self.size, self.step, self.init)

    def _update_axes(self, num_axes):
        """
        Updates all axes related objects (and sequence lengths) when the number of axes is changed.

        :param num_axes: the new number of axes to scan
        :return:
        """
        self._num_axes = num_axes
        # lists can be reassigned to copy
        current_axes = self.axes
        current_axes_names = self.axes_names
        # numpy arrays must be explicitly copied
        current_size, current_step, current_init = (self.size.copy(), self.step.copy(), self.init.copy())
        if self.num_axes > len(current_axes):
            self.axes = ['']*self.num_axes
            self.axes_names = ['']*self.num_axes
            self.size, self.step, self.init = (np.zeros(self.num_axes), np.zeros(self.num_axes), np.zeros(self.num_axes))
            self.axes[:len(current_axes)] = current_axes
            self.axes_names[:len(current_axes)] = current_axes_names
            self.size[:len(current_axes)] = current_size
            self.step[:len(current_axes)] = current_step
            self.init[:len(current_axes)] = current_init
        else:
            self.axes = current_axes[:self.num_axes]
            self.axes_names = current_axes_names[:self.num_axes]
            self.size = current_size[:self.num_axes]
            self.step = current_step[:self.num_axes]
            self.init = current_init[:self.num_axes]

    def rescale_parameter(self, param, value):
        """
        Rescales the parameter (size, step or init) if its units are changed.

        :param param:
        :param value:
        :return:
        """
        if value not in self._unit_conversion:
            raise ValueError('a valid unit must be supplied')
        unit_param = '_%s_unit' % param
        old_value = getattr(self, unit_param) if hasattr(self, unit_param) else value
        setattr(self, unit_param, value)
        a = getattr(self, param)
        a *= old_div(self._unit_conversion[old_value], self._unit_conversion[value])

    num_axes = property(fget=lambda self: self._num_axes, fset=_update_axes)
    size_unit = property(fget=lambda self: self._size_unit,
                         fset=lambda self, value: self.rescale_parameter('size', value))
    step_unit = property(fget=lambda self: self._step_unit,
                         fset=lambda self, value: self.rescale_parameter('step', value))
    init_unit = property(fget=lambda self: self._init_unit,
                         fset=lambda self, value: self.rescale_parameter('init', value))
    si_size = property(fget=lambda self: self.size*self._unit_conversion[self.size_unit])
    si_step = property(fget=lambda self: self.step*self._unit_conversion[self.step_unit])
    si_init = property(fget=lambda self: self.init*self._unit_conversion[self.init_unit])

    def run(self):
        """
        Starts the grid scan in its own thread and runs the update function at the specified
        rate whilst acquiring the data.

        :param rate: the update period in seconds
        :return:
        """
        if isinstance(self.acquisition_thread, threading.Thread) and self.acquisition_thread.is_alive():
            print('scan already running')
            return
        self.init_scan()
        self.acquisition_thread = threading.Thread(target=self.scan,
                                                   args=(self.axes, self.size, self.step, self.init))
        self.acquisition_thread.start()

    def init_grid(self, axes, size, step, init):
        """Create a grid on which to scan."""
        scan_axes = []
        for i in range(len(axes)):
            s = size[i] * self._unit_conversion[self.size_unit]
            st = step[i] * self._unit_conversion[self.step_unit]
            s0 = init[i] * self._unit_conversion[self.init_unit]
            ax = np.arange(0, s+st/2., st) - s/2. + s0
            scan_axes.append(ax)
        self.grid_shape = tuple(ax.size for ax in scan_axes)
        self.total_points = reduce(operator.mul, self.grid_shape, 1)
        self.scan_axes = scan_axes
        return scan_axes

    def init_current_grid(self):
        """Convenience method that initialises a grid based on current parameters."""
        axes, size, step, init = (self.axes[::-1], self.size[::-1], self.step[::-1], self.init[::-1])
        self.init_grid(axes, size, step, init)

    def set_stage(self, stage, axes=None):
        """
        Sets the stage and move methods.

        :param axes: sequence of axes
        """
        assert isinstance(stage, Stage), "stage must be an instance of Stage."
        self.stage = stage
        #self.move = self.stage.move
        if axes is None:
            self.axes = list(self.stage.axis_names[:self.num_axes])
        else:
            for ax in axes:
                if ax not in self.stage.axis_names:
                    raise ValueError('one of the supplied axes are invalid (not found in the stage axis names)')
            self.num_axes = len(axes)
            self.axes = list(axes)
        self.set_init_to_current_position()

    def move(self, position, axis):
        """Move to a position along a given axis."""
        self.stage.move(old_div(position,self.stage_units), axis=axis)

    def get_position(self, axis):
        return self.stage.get_position(axis=axis) * self.stage_units
    
    def outer_loop_start(self):
        """This function is called before the scan happens, for each value of the outermost variable (usually Z)"""
        pass
    def middle_loop_start(self):
        """This function is called before the scan happens, for each value of the second-outermost variable (usually Y)"""
        pass
    def outer_loop_end(self):
        """This function is called after the scan happens, for each value of the outermost variable (usually Z)"""
        pass
    def middle_loop_end(self):
        """This function is called after the scan happens, for each value of the second-outermost variable (usually Y)"""
        pass
    def scan(self, axes, size, step, init):
        """Scans a grid, applying a function at each position."""
        self.abort_requested = False
        axes, size, step, init = (axes[::-1], size[::-1], step[::-1], init[::-1])
        scan_axes = self.init_grid(axes, size, step, init)
        print(scan_axes)
        self.open_scan()
        # get the indices of points along each of the scan axes for use with snaking over array
        pnts = [list(range(axis.size)) for axis in scan_axes]

        self.indices = [-1,] * len(axes)
        self._index = 0
        self._step_times = np.zeros(self.grid_shape)
        self._step_times.fill(np.nan)
        self.status = 'acquiring data'
        self.acquiring.set()
        scan_start_time = time.time()
        for k in pnts[0]:  # outer most axis
            self.indices = list(self.indices)
            self.indices[0] = k # Make sure indices is always up-to-date, for the drift compensation
            if self.abort_requested:
                break
            self.outer_loop_start()
            self.status = 'Scanning layer {0:d}/{1:d}'.format(k + 1, len(pnts[0]))
            self.move(scan_axes[0][k], axes[0])
            pnts[1] = pnts[1][::-1]  # reverse which way is iterated over each time
            for j in pnts[1]:
                if self.abort_requested:
                    break
                self.move(scan_axes[1][j], axes[1])
                self.indices = list(self.indices)
                self.indices[1] = j
                if len(axes) == 3:  # for 3d grid (volume) scans
                    self.middle_loop_start()
                    pnts[2] = pnts[2][::-1]  # reverse which way is iterated over each time
                    for i in pnts[2]:
                        if self.abort_requested:
                            break
                        self.move(scan_axes[2][i], axes[2])
                        self.indices[2] = i # These two lines are redundant.  TODO: pick one...
                        #self.indices = (k, j, i) # keeping it as a list allows index assignment
                        self.scan_function(k, j, i)
                        self._step_times[k,j,i] = time.time()
                        self._index += 1
                    self.middle_loop_end()
                elif len(axes) == 2:  # for regular 2d grid scans ignore third axis i
                    self.indices = (k, j)
                    self.scan_function(k, j)
                    self._step_times[k,j] = time.time()
                    self._index += 1
            self.outer_loop_end()

        self.print_scan_time(time.time() - scan_start_time)
        self.acquiring.clear()
        # move back to initial positions
        for i in range(len(axes)):
            self.move(init[i]*self._unit_conversion[self._init_unit], axes[i])
        # finish the scan
        self.analyse_scan()
        self.close_scan()
        self.status = 'scan complete'

    def vary_axes(self, name, multiplier=2.):
        if 'increase_size' in name:
            self.size *= multiplier
        elif 'decrease_size' in name:
            self.size /= multiplier
        elif 'increase_step' in name:
            self.step *= multiplier
        elif 'decrease_step' in name:
            self.step /= multiplier

    def set_init_to_current_position(self):
        for i, ax in enumerate(self.axes):
            self.init[i] = old_div(self.get_position(ax), self._unit_conversion[self.init_unit])