Exemple #1
0
class TakeImage(Script):
    """
    This wraps the ueye_camera get_frame method in a Script. This can be used to take images from the gui, or used like
    GalvoScan in Autofocus or similar Scripts.
    """

    _DEFAULT_SETTINGS = [
        Parameter('width', 800, int, 'width in pixels of image'),
        Parameter('height', 600, int, 'height in pixels of image')
    ]

    _INSTRUMENTS = {'Camera': UEyeCamera}

    _SCRIPTS = {}

    def __init__(self,
                 instruments=None,
                 scripts=None,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """
        Sets up script and sends width and height settings to camera
        Args:
            name (optional): name of script, if empty same as class name
            settings (optional): settings for this script, if empty same as default settings
        """
        Script.__init__(self,
                        name,
                        settings=settings,
                        instruments=instruments,
                        scripts=scripts,
                        log_function=log_function,
                        data_path=data_path)
        self.instruments['Camera']['instance'].update({
            'width':
            self.settings['width'],
            'height':
            self.settings['height']
        })

    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in the settings property
        will be overwritten in the __init__
        """
        self.data = {
            'image_data': self.instruments['Camera']['instance'].get_frame(),
            'extent': [0, self.settings['width'], self.settings['height'], 0]
        }

    def _plot(self, axes_list, data=None):
        """
        Plots camera image to first axis
        :param axes_list: list containing axis to plot to as zeroth element
        :param data:
        """
        plot_fluorescence_new(self.data['image_data'], self.data['extent'],
                              axes_list[0])
    def get_script_order(script_order):
        """

        Args:
            script_order:
                a dictionary giving the order that the scripts in the ScriptIterator should be executed.
                Must be in the form {'script_name': int}. Scripts are executed from lowest number to highest

        Returns:
            script_order_parameter:
                A list of parameters giving the order that the scripts in the ScriptIterator should be executed.
            script_execution_freq:
                A list of parameters giving the frequency with which each script should be executed,
                e.g. 1 is every loop, 3 is every third loop, 0 is never

        """
        script_order_parameter = []
        script_execution_freq = []
        # update the script order
        for sub_script_name in list(script_order.keys()):
            script_order_parameter.append(
                Parameter(sub_script_name, script_order[sub_script_name], int,
                          'Order in queue for this script'))

            script_execution_freq.append(
                Parameter(
                    sub_script_name, 1, int,
                    'How often the script gets executed ex. 1 is every loop, 3 is every third loop, 0 is never'
                ))

        return script_order_parameter, script_execution_freq
Exemple #3
0
class B26KDC001z(KDC001):
    '''

    Same as KDC001 except adds safety limits and specifies serial number for the y axis

    '''
    _DEFAULT_SETTINGS = Parameter([ # NB the serial number and min_, max_pos values will be overwritten
        Parameter('serial_number', 27001862, int, 'serial number written on device'),
        Parameter('position', 3.0, float, 'servo position (0 to 25) [mm]'),
        Parameter('velocity', 0.0, float,
                  'servo velocity (0 to 2.6) [mm/s]. If set to zero, instrument default will be used')
    ])

    _PROBES = {'position': 'current position of stage', 'velocity':'current velocity of stage', 'serial_number': 'serial number of device'}

    def __init__(self, name=None, settings=None):
        self.max_pos = 16.7 #16.0
        self.min_pos = 0.
        super(B26KDC001z, self).__init__()

    def set_position(self, verbose=True):
        '''

        sets position of the stage z if safety limits are met

        '''

        # check if safety limits are met
        if self.settings['position'] < self.max_pos and self.settings['position'] > self.min_pos:
            super(B26KDC001z, self).set_position(self)
        else:
            print('didnt make the safety cut! doing nothing')
Exemple #4
0
class AttoScanOpenLoop(Script):

    _DEFAULT_SETTINGS = [
        Parameter('scan_axis', 'x', ['x', 'y'], 'axis to scan on'),
        # Parameter('direction', 'positive', ['positive', 'negative'], 'direction to scan'),
        Parameter('num_steps', 100, int, 'number of points in the scan'),
        Parameter('num_points', 10, int, 'number of DAQ counter measurements'),
    ]

    _SCRIPTS = {'daq_read_counter': Daq_Read_Counter}
    _INSTRUMENTS = {'attocube': ANC300}

    def _function(self):
        self.attocube = self.instruments['attocube']['instance']

        scan_axis = self.settings['scan_axis']
        steps_per_meas = self.settings['num_steps'] // self.settings[
            'num_points']

        self.data = {
            'counts': [],
            'positions': np.arange(0, self.settings['num_steps'],
                                   steps_per_meas)
        }

        for i in range(len(self.data['positions'])):
            if self._abort:
                break

            # run daq_read_counter or the relevant script to get fluorescence
            self.scripts['daq_read_counter'].run()

            # add to output structures which will be plotted
            data = self.scripts['daq_read_counter'].data['counts']
            self.data['counts'].append(np.mean(data))

            self.attocube.multistep(scan_axis, steps_per_meas)

            self.progress = i * 100. / self.settings['num_points']
            self.updateProgress.emit(int(self.progress))

        # clean up data, as in daq_read_counter
        self.data['counts'] = list(self.data['counts'])
        self.data['positions'] = list(self.data['positions'])

    def _plot(self, axes_list, data=None):
        if data is None:
            data = self.data

        if data:
            axes_list[0].set_xlabel('steps')
            axes_list[0].set_ylabel('kCounts/sec')

            axes_list[0].plot(data['positions'][:len(data['counts'])],
                              data['counts'],
                              linewidth=2.0)

    def _update_plot(self, axes_list):
        update_counts_vs_pos(axes_list[0], self.data['counts'],
                             self.data['positions'][:len(self.data['counts'])])
Exemple #5
0
class ScriptTest(Script):
    """
Minimal Example Script that has only a single parameter (execution time)
    """

    _DEFAULT_SETTINGS = [
        Parameter('execution_time', 0.1, float, 'execution time of script (s)'),
        Parameter('p1', 0.1, float, 'asihdad')
    ]

    _INSTRUMENTS = {}
    _SCRIPTS = {}

    def __init__(self, name=None, settings=None, log_function = None, data_path = None):
        """
        Example of a script
        Args:
            name (optional): name of script, if empty same as class name
            settings (optional): settings for this script, if empty same as default settings
        """
        Script.__init__(self, name, settings, log_function= log_function, data_path = data_path)


    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in the settings property
        will be overwritten in the __init__
        """
        import time
        time.sleep(self.settings['execution_time'])
Exemple #6
0
class AttoStepXY(AttoStep):
    _DEFAULT_SETTINGS = [
        Parameter('num_steps', [
            Parameter('x', 0, int, 'num steps along x-axis'),
            Parameter('y', 0, int, 'num steps along y-axis'),
        ])
    ]

    _INSTRUMENTS = {'attocube': ANC300}
Exemple #7
0
class UEyeCamera(Instrument):
    """
    This class implements a UEye-compatable camera.
    """

    _DEFAULT_SETTINGS = Parameter([
        Parameter('width', 800, int, 'width in pixels of image'),
        Parameter('height', 600, int, 'height in pixels of image')
    ])

    _PROBES = {}

    def __init__(self, name=None, settings=None):
        super(UEyeCamera, self).__init__(name, settings)
        # the following assumes only one camera is connected to the computer, since it will connect to camera number 0.
        self.cam = cv2.VideoCapture(0)
        assert self.cam.isOpened(), "Could not open camera!"
        self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, self.settings['height'])
        self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, self.settings['width'])

    def update(self, settings):
        """
        Updates internal settings, as well as the pixel width and height on the physical device
        Args:
            settings: A dictionary in the form of settings as seen in default settings
        """
        super(UEyeCamera, self).update(settings)
        for key, value in settings.items():
            if key == 'width':
                self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, self.settings['width'])
            elif key == 'height':
                self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT,
                             self.settings['height'])

    def read_probes(self, key):
        pass

    def get_frame(self):
        """
        Reads and returns a single frame from the camera.

        Returns:
            A 2d numpy array containing the image
        """

        is_successful, bgr_image = self.cam.read()

        # try again if we get a completely black image
        if np.count_nonzero(bgr_image) == 0:
            is_successful, bgr_image = self.cam.read()

        if is_successful:
            return cv2.cvtColor(bgr_image, cv2.COLOR_BGR2GRAY)
        else:
            raise EnvironmentError(
                "Could not successfully take image from camera.")
Exemple #8
0
class ChamberPressureGauge(PressureGauge):
    """
    This class implements the AGC100 pressure gauge. The class communicates with the device over RS232 using pyserial.
    """

    _possible_com_ports = ['COM' + str(i) for i in range(0, 256)]

    _DEFAULT_SETTINGS = Parameter([
            Parameter('port', 'COM7', _possible_com_ports, 'com port to which the gauge controller is connected'),
            Parameter('timeout', 1.0, float, 'amount of time to wait for a response '
                                             'from the gauge controller for each query'),
            Parameter('baudrate', 9600, int, 'baudrate of serial communication with gauge')
        ])
Exemple #9
0
class T1_double_init_many_NVs(Script):
    _DEFAULT_SETTINGS = [
        Parameter('esr_peak', 'upper', ['upper', 'lower', 'both'], 'if ESR fits two peaks, defines which one to use')
    ]
    _INSTRUMENTS = {}
    _SCRIPTS = {'select_NVs': SelectPoints, 'ESR': ESR, 'Rabi': Rabi, 'T1': T1}

    def _function(self):
        for num, nv_loc in enumerate(self.scripts['select_NVs'].data['nv_locations']):
            if self._abort:
                break
            find_NV_rabi = self.scripts['Rabi'].scripts['find_nv']
            find_NV_rabi.settings['initial_point']['x'] = nv_loc[0]
            find_NV_rabi.settings['initial_point']['y'] = nv_loc[1]
            find_NV_rabi.run()
            self.scripts['ESR'].settings['tag'] = 'esr_NV' + str(num)
            self.scripts['ESR'].run()
            fit_params = self.scripts['ESR'].data['fit_params']
            if fit_params is None:
                continue
            if len(fit_params) == 4:
                freqs = [fit_params[2]]
            elif len(fit_params == 6):
                if self.settings['esr_peak'] == 'lower':
                    freqs = [fit_params[4]]
                elif self.settings['esr_peak'] == 'upper':
                    freqs = [fit_params[5]]
                elif self.settings['esr_peak'] == 'both':
                    freqs = [fit_params[4], fit_params[5]]
            for freq in freqs:
                if self._abort:
                    break
                rabi = self.scripts['Rabi']
                rabi.settings['tag'] = 'rabi_NV' + str(num)
                rabi.settings['mw_pulses']['mw_frequency'] = float(freq)
                rabi.run()
                rabi_fit = rabi.data['fits']
                if rabi_fit is None:
                    continue
                pi_time = abs((np.pi - rabi_fit[2])/rabi_fit[1])
                pi_time = min(max(np.round(pi_time / 2.5) * 2.5, 15.), 300.) #round to nearest 2.5 ns
                find_NV_T1 = self.scripts['T1'].scripts['find_nv']
                find_NV_T1.settings['initial_point']['x'] = find_NV_rabi.data['maximum_point']['x']
                find_NV_T1.settings['initial_point']['y'] = find_NV_rabi.data['maximum_point']['y']
                T1 = self.scripts['T1']
                T1.settings['mw_pulse']['mw_frequency'] = float(freq)
                T1.settings['mw_pulse']['pi_time'] = float(pi_time)
                T1.settings['tag'] = 't1_' + '_NV' + str(num)
                T1.run()

    def plot(self, figure_list):
        if self._current_subscript_stage is not None:
            if self._current_subscript_stage['current_subscript'] is not None:
                self._current_subscript_stage['current_subscript'].plot(figure_list)


    def skip_next(self):
        for script in self.scripts.values():
            script.stop()
Exemple #10
0
class AttoStep(Script):
    # COMMENT_ME
    # _DEFAULT_SETTINGS = [
    #     Parameter('axis', 'z', ['x', 'y', 'z'], 'Axis to step on'),
    #     Parameter('direction', 'Up', ['Up', 'Down'], 'step direction, up or down in voltage (or on physical switch)')
    # ]

    _DEFAULT_SETTINGS = [
        Parameter('num_steps', [
            Parameter('x', 0, int, 'num steps along x-axis'),
            Parameter('y', 0, int, 'num steps along y-axis'),
            Parameter('z', 0, int, 'num steps along z-axis')
        ])
    ]

    _INSTRUMENTS = {'attocube': ANC300}
    _SCRIPTS = {}

    def __init__(self,
                 instruments=None,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """
        Default script initialization
        """

        Script.__init__(self,
                        name,
                        settings=settings,
                        instruments=instruments,
                        log_function=log_function,
                        data_path=data_path)

    def _function(self):
        """
        Performs a single attocube step with the voltage and frequency, and in the direction, specified in settings
        """
        for axis in self.settings['num_steps']:
            self.instruments['attocube']['instance'].multistep(
                axis, self.settings['num_steps'][axis])
Exemple #11
0
class CameraOn(Script):
    # COMMENT_ME

    _DEFAULT_SETTINGS = [Parameter('On', True, bool, '')]

    _INSTRUMENTS = {'light_control': MaestroLightControl}
    _SCRIPTS = {}

    def __init__(self,
                 instruments,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """
        Example of a script that makes use of an instrument
        Args:
            instruments: instruments the script will make use of
            name (optional): name of script, if empty same as class name
            settings (optional): settings for this script, if empty same as default settings
        """

        # call init of superclass
        Script.__init__(self,
                        name,
                        settings,
                        instruments,
                        log_function=log_function,
                        data_path=data_path)

    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in _DEFAULT_SETTINGS
        for this dummy example we just implement a counter
        """

        if self.settings['On'] == True:
            # fluorescence filter
            self.instruments['white_light'].update({'open': False})
            self.instruments['filter_wheel'].update(
                {'current_position': 'position_3'})
            self.instruments['block_ir'].update({'open': True})
            self.instruments['block_green'].update({'open': True})

            self.log('camera on')
        else:
            # high ND
            self.instruments['filter_wheel'].update(
                {'current_position': 'position_1'})
            self.instruments['block_ir'].update({'open': False})
            self.instruments['block_green'].update({'open': False})
            self.instruments['white_light'].update({'open': True})

            self.log('camera off')
 def value(self, value):
     if Parameter.is_valid(value, self.valid_values):
         self._value = value
         # check if there is a special case for setting such as a checkbox or combobox
         if self.ui_type == 'checkbox':
             self.checkbox.setChecked(value)
         elif self.ui_type == 'combo_box':
             self.combo_box.setCurrentIndex(self.combo_box.findText(str(self.value)))
         else:  # for standard values
             self.setData(1, 0, value)
     else:
         if value is not None:
             raise TypeError("wrong type {:s}, expected {:s}".format(str(type(value)), str(self.valid_values)))
Exemple #13
0
class TestUsbPbPulseSequence(PulsedExperimentBaseScript):

    _DEFAULT_SETTINGS = [
        Parameter('num_averages', 100000, int, 'number of averages'),
    ]

    def _create_pulse_sequences(self):
        tau = 50.
        measurement_time = 250.
        laser_off_time = 1000.
        nv_reset_time = 1750.
        delay_readout = 30.
        delay_mw_readout = 100.

        pulse_collection = [[
            Pulse('laser', laser_off_time + tau + 2 * 40, nv_reset_time),
            Pulse('apd_readout', laser_off_time + tau + 2 * 40 + delay_readout,
                  measurement_time),
            Pulse(
                'microwave_q',
                laser_off_time + tau + 2 * 40 + nv_reset_time + laser_off_time,
                tau),
            Pulse(
                'laser', laser_off_time + tau + 2 * 40 + nv_reset_time +
                laser_off_time + tau + 2 * 40 + delay_mw_readout,
                nv_reset_time),
            Pulse(
                'apd_readout', laser_off_time + tau + 2 * 40 + nv_reset_time +
                laser_off_time + tau + 2 * 40 + delay_mw_readout +
                delay_readout, measurement_time)
        ]]

        print(pulse_collection)

        # pulse_collection = [[Pulse('laser', laser_off_time + tau + 2*40, nv_reset_time),
        #                      Pulse('apd_readout', laser_off_time + tau + 2*40 + 20, measurement_time),
        #                      Pulse('microwave_q',
        #                            laser_off_time + tau + 2 * 40 + nv_reset_time + laser_off_time,
        #                            tau),
        #                      Pulse('laser',
        #                            laser_off_time + tau + 2 * 40 + nv_reset_time + laser_off_time + tau + 2 * 40 + delay_mw_readout,
        #                            nv_reset_time),
        #                      Pulse('apd_readout',
        #                            laser_off_time + tau + 2 * 40 + nv_reset_time + laser_off_time + tau + 2 * 40 + delay_mw_readout + delay_readout,
        #                            measurement_time)
        #                      ]]

        tau_list = [tau]

        return pulse_collection, tau_list, measurement_time
class Plant(Instrument):
    _DEFAULT_SETTINGS = Parameter([
        Parameter('set_point', 0.0, float, 'setpoint to which to stabilize'),
        Parameter('gains', [
            Parameter('proportional', 0.0, float, 'proportional gain'),
            Parameter('integral', 0.0, float, 'integral gain')
        ]),
        Parameter('time_step', 1.0, float, 'time_step of loop'),
        Parameter('output_range', [
            Parameter('min', -10000, float,
                      'min allowed value for PI-loop output'),
            Parameter('max', 10000, float,
                      'max allowed value for PI-loop output')
        ]),
    ])
    _PROBES = {}

    def __init__(self, name=None, settings=None):
        super(PIControler, self).__init__(name, settings)
        self.reset()

    def update(self, settings):
        super(PIControler, self).update(settings)
Exemple #15
0
    def test_info(self):
        p0 = Parameter('test', 0, int, 'some info')
        self.assertEqual(p0.info['test'], 'some info')

        parameters = Parameter([
            Parameter('test1', 0, int, 'test parameter (int)'),
            Parameter('test2' ,
                      [Parameter('test2_1', 'string', str, 'test parameter (str)'),
                       Parameter('test2_2', 0.0, float, 'test parameter (float)')
                       ])
        ])

        print((parameters.info))
        print((parameters['test1'].info))

        self.assertEqual(parameters.info['test2'], {'test2_1': 'test parameter (str)', 'test2_2': 'test parameter (float)'})
        self.assertEqual(parameters.info['test1'], 'test parameter (int)')
        self.assertEqual(parameters.info['test2']['test2_1'], 'test parameter (str)')
Exemple #16
0
class SetLaser_cDAQ(SetLaser):
    """
This script points the laser to a point
    """

    _DEFAULT_SETTINGS = [
        Parameter('point', [
            Parameter('x', -0.4, float, 'x-coordinate'),
            Parameter('y', -0.4, float, 'y-coordinate')
        ]),
        Parameter('DAQ_channels', [
            Parameter('x_ao_channel', 'ao0', ['ao0', 'ao1', 'ao2', 'ao3'],
                      'Daq channel used for x voltage analog output'),
            Parameter('y_ao_channel', 'ao3', ['ao0', 'ao1', 'ao2', 'ao3'],
                      'Daq channel used for y voltage analog output')
        ])
    ]

    _INSTRUMENTS = {'daq_out': NI9263}

    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in the settings property
        will be overwritten in the __init__
        """
        pt = (self.settings['point']['x'], self.settings['point']['y'])

        # daq API only accepts either one point and one channel or multiple points and multiple channels
        pt = np.transpose(np.column_stack((pt[0], pt[1])))
        pt = (np.repeat(pt, 2, axis=1))

        task = self.instruments['daq_out']['instance'].setup_AO([
            self.settings['DAQ_channels']['x_ao_channel'],
            self.settings['DAQ_channels']['y_ao_channel']
        ], pt)
        self.instruments['daq_out']['instance'].run(task)
        self.instruments['daq_out']['instance'].waitToFinish(task)
        self.instruments['daq_out']['instance'].stop(task)
        self.log('laser set to Vx={:.4}, Vy={:.4}'.format(
            self.settings['point']['x'], self.settings['point']['y']))
Exemple #17
0
    def get_default_settings(sub_scripts, script_order, script_execution_freq,
                             iterator_type):
        """
        assigning the actual script settings depending on the iterator type
        Args:
            sub_scripts: dictionary with the subscripts
            script_order: execution order of subscripts
            script_execution_freq: execution frequency of subscripts

        Returns:
            the default setting for the iterator

        """

        if iterator_type in ('iter nvs', 'iter points'):
            script_default_settings = [
                Parameter('script_order', script_order),
                Parameter('script_execution_freq', script_execution_freq),
                Parameter(
                    'run_all_first', True, bool,
                    'Run all scripts with nonzero frequency in first pass')
            ]

        elif iterator_type == 'test':
            script_default_settings = [
                Parameter('script_order', script_order),
                Parameter('script_execution_freq', script_execution_freq),
                Parameter(
                    'run_all_first', True, bool,
                    'Run all scripts with nonzero frequency in first pass')
            ]
        else:
            script_default_settings = ScriptIterator.get_default_settings(
                sub_scripts=sub_scripts,
                script_order=script_order,
                script_execution_freq=script_execution_freq,
                iterator_type=iterator_type)

        return script_default_settings
Exemple #18
0
class SpectrumAnalyzer(Instrument):
    """
    This class provides a python implementation of the Keysight N9320B 9kHz-3.0GHz spectrum analyzer
    with trigger generator.
    """

    _INSTRUMENT_IDENTIFIER = 'Keysight Technologies,N9320B,CN0323B356,0B.03.58'
    # String returned by spectrum analyzer upon querying it with '*IDN?'

    _DEFAULT_SETTINGS = Parameter([
        Parameter(
            'visa_resource', 'USB0::0x0957::0xFFEF::CN0323B356::INSTR', (str),
            'pyVisa instrument identifier, to make a connection using the pyVisa package.'
        ),
        Parameter('start_frequency', 9e3, float,
                  'start frequency of spectrum analyzer frequency range'),
        Parameter(
            'mode', 'SpectrumAnalyzer',
            ['SpectrumAnalyzer', 'TrackingGenerator'],
            'switches between normal spectrum analyzer mode or spectrum analyzer PLUS output, '
            'i.e., tracking generator'),
        Parameter('stop_frequency', 3e9, float,
                  'stop frequency of spectrum analyzer frequency range'),
        # Parameter('frequency_step', 10e6, float, 'frequency interval of spectrum analyzer frequency range'),
        Parameter('output_on', False, bool, 'toggles the tracking generator'),
        Parameter(
            'connection_timeout', 1000, int, 'the time to wait for a response '
            'from the spectrum analyzer with each query (units??)'),
        Parameter('output_power', -20.0, float,
                  'the output power (in dBm) of the tracking generator')
    ])

    _PROBES = {
        'start_frequency': 'the lower bound of the frequency sweep',
        'stop_frequency': 'the upper bound of the frequency sweep',
        'trace': 'the frequency sweep of the input signal',
        'tracking_generator': 'checks if the tracking generator is on',
        'bandwidth': 'the curent bandwidth of the spectrum analyzer',
        'output_power': 'the power of the tracking generator',
        'mode': 'Spectrum Analyzer Mode or Tracking Generator Mode'
    }

    def __init__(self, name='SpectrumAnalyzer', settings={}):
        """

        Args:
            name (str): optional name of instance of class
            settings (list): list of other values to initialize class with

        """

        super(SpectrumAnalyzer, self).__init__(name, settings)

        self._last_update_time = time.time()

        rm = visa.ResourceManager()

        # todo: JG 20170623 implement proper error handling when instrument is not connected.
        # try:
        self.spec_anal = rm.open_resource(self.settings['visa_resource'])
        self.spec_anal.read_termination = '\n'
        self.spec_anal.timeout = self.settings['connection_timeout']
        self.spec_anal.write(
            '*RST'
        )  #Places the oscilloscope in the factory default setup state.
        self._wait_for_spec_anal()
        self.update({'mode': 'SpectrumAnalyzer'})
        # except:
        #     raise

    def update(self, settings):
        """
        updates the instrument parameters

        Args:
            settings: dictionary that contains the parameter indentifiers (keys) and the new parameters values (value)

        """
        super(SpectrumAnalyzer, self).update(settings)

        # set mode first
        if 'mode' in settings:
            self._wait_for_spec_anal()
            self._set_mode(settings['mode'])
            print(('mode', settings['mode']))
            # since changes in the output_power are not applied to the instruments when in SpectrumAnalyzer mode, we make sure that is is updated once switched back to TrackingAnalyzer
            if settings['mode'] == 'TrackingGenerator':
                self.update({'output_power': self.settings['output_power']})

        if 'start_frequency' in settings:
            assert 9e3 <= settings[
                'start_frequency'] <= 3e9, "start frequency must be between 0 and 3e9, you tried to set it to {0}!".format(
                    settings['start_frequency'])
            self._wait_for_spec_anal()
            self._set_start_frequency(settings['start_frequency'])

        # if 'frequency_step' in settings:
        #     self._wait_for_osci()
        #     self._set_frequency_step(settings['frequency_step'])

        if 'stop_frequency' in settings:
            assert 9e3 <= settings[
                'stop_frequency'] <= 3e9, "start frequency must be between 0 and 3e9, you tried to set it to {0}!".format(
                    settings['stop_frequency'])
            self._wait_for_spec_anal()
            self._set_stop_frequency(settings['stop_frequency'])

        if 'output_on' in settings:
            self._wait_for_spec_anal()
            self._toggle_output(settings['output_on'])

            # todo: JG 201700623 check if the power is set properly (see comment in "if 'mode' in settings:" (~ line 87)
            print(
                'warning! output turned on.\ncheck if the output power is corresponds to the value in the settings!\nCheck code for more details'
            )

        if 'output_power' in settings and self.settings['output_on']:
            self._wait_for_spec_anal()
            self._set_output_power(settings['output_power'])

        # for key, value in settings.iteritems():
        # if key == 'start_frequency':
        #     assert 0.0 < value < 3e9, \
        #         "start frequency must be between 0 and 3e9, you tried to set it to {0}!".format(value)
        #     self._set_start_frequency(value)
        # elif key == 'stop_frequency':
        #     assert 0.0 < value < 3e9, \
        #         "stop frequency must be between 0 and 3e9, you tried to set it to {0}!".format(value)
        #     self._set_stop_frequency(value)
        # elif key == 'output_on':
        #     self._toggle_output(value)
        # elif key == 'output_power':
        #     self._set_output_power(value)
        # elif key == 'mode':
        #     self._set_mode(value)
        # else:
        #     message = '{0} is not a parameter of {1}'.format(key, self.name)

    def read_probes(self, probe_name):
        self._wait_for_spec_anal()

        if probe_name == 'start_frequency':
            return self._get_start_frequency()
        elif probe_name == 'stop_frequency':
            return self._get_stop_frequency()
        elif probe_name == 'trace':
            return self._get_trace()
        elif probe_name == 'output_on':
            return self._is_output_on()
        elif probe_name == 'bandwidth':
            return self._get_bandwidth()
        elif probe_name == 'output_power':
            return self._get_output_power()
        elif probe_name == 'mode':
            return self._get_mode()
        elif probe_name == 'peak':
            return self._get_peak()
        else:
            message = 'no probe with that name exists!'
            raise AttributeError(message)

    def is_connected(self):
        """
        Checks if the instrument is connected.
        Returns: True if connected, False otherwise.

        """
        identification = self.spec_anal.query('*IDN?')
        return identification == self._INSTRUMENT_IDENTIFIER

    def _set_start_frequency(self, start_freq):
        #COMMENT_ME
        self.spec_anal.write('SENS:FREQ:START ' + str(start_freq))

    def _set_frequency_span(self, frequency_step):
        #COMMENT_ME
        self.spec_anal.write('SENS:FREQ:SPAN ' + str(frequency_step))

    def _get_start_frequency(self):
        #COMMENT_ME
        return float(self.spec_anal.query('SENS:FREQ:START?\n'))

    def _set_stop_frequency(self, stop_freq):
        #COMMENT_ME
        self.spec_anal.write('SENS:FREQ:STOP ' + str(stop_freq))

    def _get_stop_frequency(self):
        #COMMENT_ME
        return float(self.spec_anal.query('SENS:FREQ:STOP?\n'))

    def _toggle_output(self, state):
        #COMMENT_ME

        if state:
            assert self._get_mode(
            ) == 'TrackingGenerator', "output can't be on while in SpectrumAnalyzer mode"
            self.spec_anal.write('OUTPUT 1')
        elif not state:
            self.spec_anal.write('OUTPUT 0')

    def _is_output_on(self):
        #COMMENT_ME
        if self.mode == 'SpectrumAnalyzer':
            return False
        elif self.mode == 'TrackingGenerator':
            return bool(int(self.spec_anal.query('OUTPUT:STATE?')))

    def _get_mode(self):
        #COMMENT_ME
        mode_response = str(self.spec_anal.query('CONFIGURE?')).strip()
        if mode_response == 'SAN':
            return 'SpectrumAnalyzer'
        elif mode_response == 'TGEN':
            return 'TrackingGenerator'

    def _set_mode(self, mode):
        #COMMENT_ME
        if mode == 'TrackingGenerator':
            self.spec_anal.write('CONFIGURE:TGENERATOR')
        elif mode == 'SpectrumAnalyzer':
            self.output_on = False
            self.spec_anal.write('CONFIGURE:SANALYZER')

    def _get_trace(self):
        #COMMENT_ME
        amplitudes = [
            float(i)
            for i in str(self.spec_anal.query(
                'TRACE:DATA? TRACE1' + ';*OPC?')).rstrip(';1').split(',')
        ]
        num_points = len(amplitudes)
        frequencies = np.linspace(start=self.start_frequency,
                                  stop=self.stop_frequency,
                                  num=num_points).tolist()
        # return [(frequencies[i], amplitudes[i])for i in range(num_points)]

        return {'frequencies': frequencies, 'amplitudes': amplitudes}

    def _get_bandwidth(self):
        #COMMENT_ME
        return float(self.spec_anal.query('BANDWIDTH?'))

    def _get_output_power(self):
        #COMMENT_ME
        return float(self.spec_anal.query('SOURCE:POWER?'))

    def _get_peak(self):
        # return the frequency that comes out of 'peak search'
        self.spec_anal.write('CALC:MARK:MAX')
        return float(self.spec_anal.query('CALC:MARK:X?'))

    def _set_output_power(self, power):
        #COMMENT_ME
        assert self.mode == 'TrackingGenerator', "mode need to be 'TrackingGenerator' to change power"

        return self.spec_anal.write('SOURCE:POWER ' + str(power))

    def __del__(self):
        #COMMENT_ME
        self._wait_for_spec_anal()
        self._set_mode('SpectrumAnalyzer')
        self.spec_anal.close()

    def _wait_for_spec_anal(self):
        #COMMENT_ME

        if self._last_update_time - time.time() < 1.0:
            time.sleep(1)

        self._last_update_time = time.time()
class SelectPoints(Script):
    """
Script to select points on an image. The selected points are saved and can be used in a superscript to iterate over.
    """
    _DEFAULT_SETTINGS = [
        Parameter('patch_size', 0.003),
        Parameter('type', 'free', ['free', 'square', 'line', 'ring']),
        Parameter(
            'Nx', 5, int,
            'number of points along x (type: square) along line (type: line)'),
        Parameter('Ny', 5, int, 'number of points along y (type: square)')
    ]

    _INSTRUMENTS = {}
    _SCRIPTS = {}

    def __init__(self,
                 instruments=None,
                 scripts=None,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """
        Select points by clicking on an image
        """
        Script.__init__(self,
                        name,
                        settings=settings,
                        instruments=instruments,
                        scripts=scripts,
                        log_function=log_function,
                        data_path=data_path)

        self.patches = []
        self.plot_settings = {}

    def _function(self):
        """
        Waits until stopped to keep script live. Gui must handle calling of Toggle_NV function on mouse click.
        """

        self.data = {'nv_locations': [], 'image_data': None, 'extent': None}

        self.progress = 50
        self.updateProgress.emit(self.progress)
        # keep script alive while NVs are selected
        while not self._abort:
            time.sleep(1)

    def plot(self, figure_list):
        '''
        Plots a dot on top of each selected NV, with a corresponding number denoting the order in which the NVs are
        listed.
        Precondition: must have an existing image in figure_list[0] to plot over
        Args:
            figure_list:
        '''
        # if there is not image data get it from the current plot
        if not self.data == {} and self.data['image_data'] is None:
            axes = figure_list[0].axes[0]
            if len(axes.images) > 0:
                self.data['image_data'] = np.array(axes.images[0].get_array())
                self.data['extent'] = np.array(axes.images[0].get_extent())
                self.plot_settings['cmap'] = axes.images[0].get_cmap().name
                self.plot_settings['xlabel'] = axes.get_xlabel()
                self.plot_settings['ylabel'] = axes.get_ylabel()
                self.plot_settings['title'] = axes.get_title()
                self.plot_settings['interpol'] = axes.images[
                    0].get_interpolation()

        Script.plot(self, figure_list)

    #must be passed figure with galvo plot on first axis
    def _plot(self, axes_list):
        '''
        Plots a dot on top of each selected NV, with a corresponding number denoting the order in which the NVs are
        listed.
        Precondition: must have an existing image in figure_list[0] to plot over
        Args:
            figure_list:
        '''

        axes = axes_list[0]

        if self.plot_settings:
            axes.imshow(self.data['image_data'],
                        cmap=self.plot_settings['cmap'],
                        interpolation=self.plot_settings['interpol'],
                        extent=self.data['extent'])
            axes.set_xlabel(self.plot_settings['xlabel'])
            axes.set_ylabel(self.plot_settings['ylabel'])
            axes.set_title(self.plot_settings['title'])

        self._update(axes_list)

    def _update(self, axes_list):

        axes = axes_list[0]

        patch_size = self.settings['patch_size']

        #first clear all old patches (circles and numbers), then redraw all
        if not self.patches == []:
            try:  #catch case where plot has been cleared, so old patches no longer exist. Then skip clearing step.
                for patch in self.patches:
                    patch.remove()
            except ValueError:
                pass

        self.patches = []

        for index, pt in enumerate(self.data['nv_locations']):
            # axes.plot(pt, fc='b')

            circ = patches.Circle((pt[0], pt[1]), patch_size, fc='b')
            axes.add_patch(circ)
            self.patches.append(circ)

            text = axes.text(pt[0],
                             pt[1],
                             '{:d}'.format(index),
                             horizontalalignment='center',
                             verticalalignment='center',
                             color='white')
            self.patches.append(text)

    def toggle_NV(self, pt):
        '''
        If there is not currently a selected NV within self.settings[patch_size] of pt, adds it to the selected list. If
        there is, removes that point from the selected list.
        Args:
            pt: the point to add or remove from the selected list

        Poststate: updates selected list

        '''

        if not self.data[
                'nv_locations']:  #if self.data is empty so this is the first point
            self.data['nv_locations'].append(pt)
            self.data['image_data'] = None  # clear image data

        else:
            # use KDTree to find NV closest to mouse click
            tree = scipy.spatial.KDTree(self.data['nv_locations'])
            #does a search with k=1, that is a search for the nearest neighbor, within distance_upper_bound
            d, i = tree.query(pt,
                              k=1,
                              distance_upper_bound=self.settings['patch_size'])

            # removes NV if previously selected
            if d is not np.inf:
                self.data['nv_locations'].pop(i)
            # adds NV if not previously selected
            else:
                self.data['nv_locations'].append(pt)

        # if type is not free we calculate the total points of locations from the first selected points
        if self.settings['type'] == 'square' and len(
                self.data['nv_locations']) > 1:
            # here we create a rectangular grid, where pts a and be define the top left and bottom right corner of the rectangle
            Nx, Ny = self.settings['Nx'], self.settings['Ny']
            pta = self.data['nv_locations'][0]
            ptb = self.data['nv_locations'][1]
            tmp = np.array([[[
                pta[0] + 1.0 * i * (ptb[0] - pta[0]) / (Nx - 1),
                pta[1] + 1.0 * j * (ptb[1] - pta[1]) / (Ny - 1)
            ] for i in range(Nx)] for j in range(Ny)])
            self.data['nv_locations'] = np.reshape(tmp, (Nx * Ny, 2))
            self.stop()

        elif self.settings['type'] == 'line' and len(
                self.data['nv_locations']) > 1:
            # here we create a straight line between points a and b
            N = self.settings['Nx']
            pta = self.data['nv_locations'][0]
            ptb = self.data['nv_locations'][1]
            self.data['nv_locations'] = [
                np.array([
                    pta[0] + 1.0 * i * (ptb[0] - pta[0]) / (N - 1),
                    pta[1] + 1.0 * i * (ptb[1] - pta[1]) / (N - 1)
                ]) for i in range(N)
            ]
            self.stop()

        elif self.settings['type'] == 'ring' and len(
                self.data['nv_locations']) > 1:
            # here we create a circular grid, where pts a and be define the center and the outermost ring
            Nx, Ny = self.settings['Nx'], self.settings['Ny']

            pta = self.data['nv_locations'][0]  # center
            ptb = self.data['nv_locations'][1]  # outermost ring

            # radius of outermost ring:
            rmax = np.sqrt((pta[0] - ptb[0])**2 + (pta[1] - ptb[1])**2)

            # create points on rings
            tmp = []
            for r in np.linspace(rmax, 0, Ny + 1)[0:-1]:
                for theta in np.linspace(0, 2 * np.pi, Nx + 1)[0:-1]:
                    tmp += [[
                        r * np.sin(theta) + pta[0], r * np.cos(theta) + pta[1]
                    ]]

            self.data['nv_locations'] = np.array(tmp)
            self.stop()
Exemple #20
0
class ApplyLightControlSettings(Script):
    """
script that toggles between two different light control settings.
If parameter on is set to true applies the settings from the script. If false it resets to the previous settings.
    """

    _DEFAULT_SETTINGS = [
        Parameter(
            'On', True, bool,
            'True: apply the settings from the script. False: reset to the previous settings.'
        )
    ]

    _INSTRUMENTS = {'light_control': MaestroLightControl}
    _SCRIPTS = {}

    def __init__(self,
                 instruments,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """
        Example of a script that makes use of an instrument
        Args:
            instruments: instruments the script will make use of
            name (optional): name of script, if empty same as class name
            settings (optional): settings for this script, if empty same as default settings
        """

        # call init of superclass
        Script.__init__(self,
                        name,
                        settings,
                        instruments,
                        log_function=log_function,
                        data_path=data_path)

        self._original_settings = {}

    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in _DEFAULT_SETTINGS
        for this dummy example we just implement a counter
        """
        instr = self.instruments['light_control']

        if self.settings['On'] == True:

            self._original_settings = deepcopy(
                self.instruments['light_control']['instance'].settings)
            dictator = self.instruments['light_control']['settings']
            # dictator = {
            #     'white light': {'open': False},
            #     'block IR': {'open': False},
            #     'block green': {'open': False}
            # }

            # self.instruments['white_light'].update({'open': False})
            # self.instruments['block_ir'].update({'open': False})
            # self.instruments['block_green'].update({'open': True})

            self.log('applies lightcontrol settings')
        else:

            dictator = self._original_settings
            #
            # self.instruments['block_ir'].update({'open': False})
            # self.instruments['block_green'].update({'open': False})
            # self.instruments['white_light'].update({'open': True})

            self.log('reset lightcontrol settings')

        print(('SETTINGS: ', dictator))
        self.instruments['light_control']['instance'].update(dictator)
class GalvoScanZI3D(Script):
    _DEFAULT_SETTINGS = [
        Parameter('z_axis_center_position', 6000, float,
                  'center point of autofocus sweep'),
        Parameter(
            'scan_width', 5, float,
            'distance (in V or mm) between the minimum and maximum points of the range'
        ),
        Parameter('num_sweep_points', 10, int,
                  'number of values to sweep between min and max voltage'),
    ]

    _SCRIPTS = {'galvoscanZI': GalvoScanZI}
    _INSTRUMENTS = {'z_driver': SMC100}

    def __init__(self,
                 scripts,
                 instruments=None,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """

        """
        Script.__init__(self,
                        name,
                        settings,
                        instruments,
                        scripts,
                        log_function=log_function,
                        data_path=data_path)

    def _function(self):
        min_pos = self.settings[
            'z_axis_center_position'] - self.settings['scan_width'] / 2.0
        max_pos = self.settings[
            'z_axis_center_position'] + self.settings['scan_width'] / 2.0

        z_pos_list = np.linspace(min_pos, max_pos,
                                 self.settings['num_sweep_points'])
        self.data['sweep_voltages'] = z_pos_list
        for z_pos in z_pos_list:
            print(('zpos', z_pos))
            self._step_piezo(z_pos)
            self.scripts['galvoscanZI'].run()

    def _step_piezo(self, position):
        """
        steps the piezo.  Has to be overwritten specifically for each different hardware realization
        voltage: target piezo voltage
        wait_time: settle time after voltage step
        """
        z_driver = self.instruments['z_driver']['instance']
        # set the voltage on the piezo
        try:
            z_driver.position = float(position)
        except ValueError:
            raise
            self.log(
                'requested value not permitted. Did not set value to {:0.3f} um'
                .format(position))

    def plot(self, figure_list):
        self.scripts['galvoscanZI'].plot(figure_list)
Exemple #22
0
class KDC001(Instrument):
    """
    Class to control the thorlabs KDC001 servo. Note that ALL DLL FUNCTIONS TAKING NUMERIC INPUT REQUIRE A SYSTEM.DECIMAL
    VALUE. Check help doc at C:\Program Files\Thorlabs\Kinesis\Thorlabs.MotionControl.DotNet_API for the DLL api.
    The class communicates with the device over USB.
    """

    _DEFAULT_SETTINGS = Parameter([
        Parameter('serial_number', 27501971, int,
                  'serial number written on device'),
        Parameter('position', 25.0, float, 'servo position (0 to 25) [mm]'),
        Parameter('velocity', 0.0, float,
                  'servo maximum velocity (0 to 2.6) [mm/s]')
    ])

    def __init__(self, name=None, settings=None):
        super().__init__(name, settings)
        self.servo_library = ctypes.cdll.LoadLibrary(
            'C:\\Program Files\\Thorlabs\\Kinesis\\Thorlabs.MotionControl.KCube.DCServo.dll'
        )
        self.position_encoder2mm_conversion_factor = 34304
        #TODO: figure out what the conversion factor is for the velocity!
        # self.velocity_encoder2mm_conversion_factor =
        self.manually_set_library_inputs_and_outputs()
        self._connect()

    def manually_set_library_inputs_and_outputs(self):
        """
        Sets the input and output types for each servo library call we make.

        """
        self.servo_library.TLI_BuildDeviceList.restypes = ctypes.c_short

        self.servo_library.TLI_GetDeviceListSize.restypes = ctypes.c_short

        self.servo_library.TLI_GetDeviceListByTypeExt.argtypes = [
            ctypes.POINTER(ctypes.c_char), ctypes.c_ulong, ctypes.c_int
        ]
        self.servo_library.TLI_GetDeviceListByTypeExt.restypes = ctypes.c_short

        self.servo_library.TLI_GetDeviceInfo.argtypes = [
            ctypes.POINTER(ctypes.c_char),
            ctypes.POINTER(TLI_DeviceInfo)
        ]
        self.servo_library.TLI_GetDeviceInfo.restypes = ctypes.c_short

        self.servo_library.CC_StartPolling.argtypes = [
            ctypes.POINTER(ctypes.c_char), ctypes.c_int
        ]
        self.servo_library.CC_StartPolling.restypes = ctypes.c_bool

        self.servo_library.CC_Open.argtypes = [ctypes.POINTER(ctypes.c_char)]
        self.servo_library.CC_Open.restypes = ctypes.c_short

        self.servo_library.CC_Close.argtypes = [ctypes.POINTER(ctypes.c_char)]
        self.servo_library.CC_Close.restypes = ctypes.c_short

        self.servo_library.CC_ClearMessageQueue.argtypes = [
            ctypes.POINTER(ctypes.c_char)
        ]
        self.servo_library.CC_ClearMessageQueue.restypes = ctypes.c_short

        self.servo_library.CC_WaitForMessage.argtypes = [
            ctypes.POINTER(ctypes.c_char),
            ctypes.POINTER(ctypes.c_ushort),
            ctypes.POINTER(ctypes.c_ushort),
            ctypes.POINTER(ctypes.c_ulong)
        ]
        self.servo_library.CC_WaitForMessage.restypes = ctypes.c_bool

        self.servo_library.CC_GetVelParams.argtypes = [
            ctypes.POINTER(ctypes.c_char),
            ctypes.POINTER(ctypes.c_int),
            ctypes.POINTER(ctypes.c_int)
        ]
        self.servo_library.CC_GetVelParams.restypes = ctypes.c_short

        self.servo_library.CC_MoveToPosition.argtypes = [
            ctypes.POINTER(ctypes.c_char), ctypes.c_int
        ]
        self.servo_library.CC_MoveToPosition.restypes = ctypes.c_short

        self.servo_library.CC_SetVelParams.argtypes = [
            ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.c_int
        ]
        self.servo_library.CC_SetVelParams.restypes = ctypes.c_short

        self.servo_library.CC_StopPolling.argtypes = [
            ctypes.POINTER(ctypes.c_char)
        ]

        self.servo_library.CC_Home.argtypes = [ctypes.POINTER(ctypes.c_char)]
        self.servo_library.CC_Home.restypes = ctypes.c_short

        self.servo_library.CC_GetPosition.argtypes = [
            ctypes.POINTER(ctypes.c_char)
        ]
        self.servo_library.CC_GetPosition.restypes = ctypes.c_int

    def _connect(self, verbose=True):

        # this version of the serial number is useful
        serial_num = ctypes.c_char_p(
            bytes(str(self.settings['serial_number']), "utf-8"))

        if self.servo_library.TLI_BuildDeviceList() == 0:
            num_devices = self.servo_library.TLI_GetDeviceListSize()
            if verbose:
                print('Number of devices detected: {0}'.format(num_devices))

            # The servo library fills in a byte string of connected device serial numbers, separated by a comma.
            # The length of this string will be the length of the serial number (8 bytes), plus a byte for a comma,
            # for each connected device. We subtract 1 since the last entry does not have a comma.
            # Here, we first pre-allocate the string, then send it to the library function, and then examine it.

            connected_devices_string_length = num_devices * 9 - 1
            string_of_serial_nums = ctypes.create_string_buffer(
                connected_devices_string_length)
            self.servo_library.TLI_GetDeviceListByTypeExt(
                string_of_serial_nums,
                ctypes.c_ulong(connected_devices_string_length),
                ctypes.c_int(27))

            list_of_connected_serial_nums = string_of_serial_nums.raw.decode(
                "utf-8").split(',')
            if str(self.settings['serial_number']
                   ) not in list_of_connected_serial_nums:
                error_msg = 'No servo with the given serial number was detected.\n'
                error_msg += 'Connected Devices: {0}\n'.format(
                    list_of_connected_serial_nums)
                error_msg += 'Given Serial Number: {0}'.format(
                    self.settings['serial_number'])
                raise AttributeError(error_msg)

            elif verbose:
                print('Found device with matching serial number.')
                device_info = TLI_DeviceInfo()
                self.servo_library.TLI_GetDeviceInfo(serial_num,
                                                     ctypes.byref(device_info))
                print("Description: ", device_info.description)
                print("Serial No: ", device_info.serialNo)
                print("USB PID: ", device_info.PID)

            if not self.servo_library.CC_Open(serial_num):
                if verbose:
                    print('Starting to poll')
                milliseconds = ctypes.c_int(200)
                self.servo_library.CC_StartPolling(serial_num, milliseconds)
                time.sleep(3)
                self.servo_library.CC_ClearMessageQueue(serial_num)
                self.servo_library.CC_Home(serial_num)
                if verbose:
                    print('Device is homing.')

                message_type = ctypes.c_ushort(0)
                message_id = ctypes.c_ushort(0)
                message_data = ctypes.c_ulong(0)
                self.servo_library.CC_WaitForMessage(serial_num, message_type,
                                                     message_id, message_data)
                if verbose:
                    print(
                        'printing all messages from K Cube while we wait for finished homing indicator'
                    )
                while message_type.value != 2 or message_id.value != 0:
                    if verbose:
                        print('message_type: {0}'.format(message_type))
                        print('message id: {0}'.format(message_id))
                        print('message data: {0}\n'.format(message_data))
                    self.servo_library.CC_WaitForMessage(
                        serial_num, message_type, message_id, message_data)

                if verbose:
                    print('message_type: {0}'.format(message_type))
                    print('message id: {0}'.format(message_id))
                    print('message data: {0}\n'.format(message_data))
                    print('Device finished homing.')

                # if self.settings['velocity'] > 0:
                #     self.servo_library.CC_SetVelParams(serial_num, ctypes.c)

                self.servo_library.CC_ClearMessageQueue(serial_num)
                position = ctypes.c_int(
                    int(self.position_encoder2mm_conversion_factor *
                        self.settings['position']))
                self.servo_library.CC_MoveToPosition(serial_num, position)
                if verbose:
                    print('Device is moving to indicated position ({0} mm).'.
                          format(self.settings['position']))

                self.servo_library.CC_WaitForMessage(serial_num, message_type,
                                                     message_id, message_data)
                while message_type.value != 2 or message_id.value != 1:
                    print('message_type: {0}'.format(message_type))
                    print('message id: {0}'.format(message_id))
                    print('message data: {0}\n'.format(message_data))
                    self.servo_library.CC_WaitForMessage(
                        serial_num, message_type, message_id, message_data)

                if verbose:
                    position = self.servo_library.CC_GetPosition(
                        serial_num
                    ) / self.position_encoder2mm_conversion_factor
                    print('Device now at position {0}'.format(position))

                self.servo_library.CC_StopPolling(serial_num)
                self.servo_library.CC_Close(serial_num)
Exemple #23
0
class PressureGauge(Instrument):
    """
    This class implements the AGC100 pressure gauge. The class communicates with the device over RS232 using pyserial.
    """

    # Translations of the controller's status messages
    MEASUREMENT_STATUS = {
        '0': 'Measurement data okay',
        '1': 'Underrange',
        '2': 'Overrange',
        '3': 'Sensor error',
        '4': 'Sensor off',
        '5': 'No sensor',
        '6': 'Identification error',
        '7': 'Error FRG-720, FRG-730'
    }

    # Translation of the controller's units check  messages
    MEASUREMENT_UNITS = {
        '0': 'mbar/bar',
        '1': 'Torr',
        '2': 'Pascal',
        '3': 'Micron'
    }

    # ASCII Characters used for controller communication
    ETX = chr(3)
    CR = chr(13)
    LF = chr(10)
    ENQ = chr(5)
    ACK = chr(6)
    NAK = chr(21)

    _possible_com_ports = ['COM' + str(i) for i in range(0, 256)]

    _DEFAULT_SETTINGS = Parameter([
            Parameter('port', 'COM7', _possible_com_ports, 'com port to which the gauge controller is connected'),
            Parameter('timeout', 1.0, float, 'amount of time to wait for a response '
                                             'from the gauge controller for each query'),
            Parameter('baudrate', 9600, int, 'baudrate of serial communication with gauge')
        ])

    #serial_connection = serial.Serial(port=_DEFAULT_SETTINGS['port'], baudrate=_DEFAULT_SETTINGS['baudrate'],
    #                                           timeout=_DEFAULT_SETTINGS['timeout'])
    def __init__(self, name='PressureGauge', settings=None):
        """
        The serial connection should be setup with the following parameters:
        1 start bit, 8 data bits, No parity bit, 1 stop bit, no hardware
        handshake. These are all default for Serial and therefore not input
        below
        """

        super(PressureGauge, self).__init__(name, settings)
        self.serial_connection = serial.Serial(port=self.settings['port'], baudrate=self.settings['baudrate'],
                                               timeout=self.settings['timeout'])

    @property
    def _PROBES(self):
        """

        Returns: A dictionary of key-value string-string pairs. keys: probe names, values: probe descriptions

        """
        return {
            'pressure': 'numerical pressure read from Pressure Gauge',
            'units': 'Units used by pressure gauge',
            'model': 'Model of the pressure gauge'
        }

    def update(self, settings):
        super(PressureGauge, self).update(settings)

    def read_probes(self, probe_name):
        """
        Args:
            probe_name: Name of the probe to get the value of from the Pressure Gauge (e.g., 'pressure')

        Returns:
            value of the probe from the Pressure Gauge
        """

        probe_name = probe_name.lower()  # making sure the probe is lowercase

        if probe_name == 'pressure':
            return self._get_pressure()
        elif probe_name == 'units':
            return self._get_units()
        elif probe_name == 'model':
            return self._get_model()
        else:
            message = '\'{0}\' not found as a probe in the class. ' \
                      'Expected either \'pressure\', \'units\', or \'model\''.format(probe_name)
            raise AttributeError(message)

    def _check_acknowledgement(self, response):
        """
        _check_acknowledgement raises an error if the response passed in indicates an negatice response from the guage.

        :param response: the string response from the Guage Controller
        """

        if response == self.NAK + self.CR + self.LF:
            message = 'Serial communication returned negative acknowledge (NAK). ' \
                      'Check AGC100 documentation for more details.'
            raise IOError(message)

        elif response != self.ACK + self.CR + self.LF:
            message = 'Serial communication returned unknown response:\n{}' \
                ''.format(repr(response))
            raise AssertionError(message)

    def _get_pressure(self):
        """
        Returns the pressure currently read by the guage controller.

        :return: pressure
        """
        assert self.serial_connection.isOpen()

        self.serial_connection.write('PR1' + self.CR + self.LF)
        acknowledgement = self.serial_connection.readline()
        self._check_acknowledgement(acknowledgement)

        self.serial_connection.write(self.ENQ)
        err_msg_and_pressure = self.serial_connection.readline().rstrip(self.LF).rstrip(self.CR)

        err_msg = err_msg_and_pressure[0]
        pressure = float(err_msg_and_pressure[3:])

        if err_msg != '0':
            print(('xx', err_msg, pressure))
            message = 'Pressure query resulted in an error: ' + self.MEASUREMENT_STATUS[err_msg]
            # raise IOError(message) # JG: don't raise the error because this crashes the programm, rather we want to return an invalid value

        self.serial_connection.write(self.CR + self.LF)
        return pressure

    def _get_model(self):
        """
        Returns the model of the connected gauge controller.
        :return: model name
        """
        assert self.serial_connection.isOpen()

        self.serial_connection.write('TID' + self.CR + self.LF)
        acknowledgement = self.serial_connection.readline(25)
        self._check_acknowledgement(acknowledgement)

        self.serial_connection.write(self.ENQ)
        model = self.serial_connection.readline().rstrip(self.LF).rstrip(self.CR)

        self.serial_connection.write(self.CR + self.LF)

        return model

    def _get_units(self):
        """
        Returns the units that are in use by the guage controller.

        :return: gauge units (either bar, Torr, Pascal, or Micron)
        """
        #assert self.ser.isOpen()

        self.serial_connection.write('UNI' + self.CR + self.LF)
        acknowledgement = self.serial_connection.readline()
        self._check_acknowledgement(acknowledgement)

        self.serial_connection.write(self.ENQ)
        unit = self.MEASUREMENT_UNITS[self.serial_connection.readline().rstrip(self.LF).rstrip(self.CR)]

        self.serial_connection.write(self.CR + self.LF)

        return unit

    def is_connected(self):
        """
        checks if serial connection is still open with instrument.

        :return: boolean connection status
        """
        return self.serial_connection.isOpen()

    def __del__(self):
        """
        Destructor, to close the serial connection when the instance is this class is garbage collected
        """
        self.serial_connection.close()
Exemple #24
0
class AWG(Instrument):  # Emma Rosenfeld 20170822
    """
    This class implements the Tektronix AFG3022C. The class commuicates with the
    device over GPIB using pyvisa.
    """

    _DEFAULT_SETTINGS = Parameter([
        #  Parameter('connection_type', 'GPIB', ['GPIB', 'RS232'], 'type of connection to open to controller'),
        #Parameter('port', 11, list(range(0,31)), 'GPIB port on which to connect'),
        Parameter('USB_num', 0, int, 'GPIB device on which to connect'),
        Parameter('enable_output_ch1', False, bool, 'enable output CH1'),
        Parameter('enable_output_ch2', False, bool, 'enable output CH2'),

        # type of AWG function: should be Arb for
        Parameter('function_ch1', 'Arb', [
            'Sine', 'Square', 'Ramp', 'Pulse', 'Arb', 'Sin(x)/x', 'Noise',
            'DC', 'Gaussian', 'Lorentz', 'Exponential Rise',
            'Exponential Decay', 'Haversine'
        ], 'Function CH1'),
        Parameter('function_ch2', 'Arb', [
            'Sine', 'Square', 'Ramp', 'Pulse', 'Arb', 'Sin(x)/x', 'Noise',
            'DC', 'Gaussian', 'Lorentz', 'Exponential Rise',
            'Exponential Decay', 'Haversine'
        ], 'Function CH2'),

        # if a non-arb function is selected, program properties of the waveform
        Parameter('default_waveform_ch1', [
            Parameter('frequency', 1e6, float, 'frequency in Hz'),
            Parameter('amplitude', 0.5, float,
                      'output amplitude peak to peak in volts'),
            Parameter('phase', 0, float, 'output phase, in degrees'),
            Parameter('offset', 0, float, 'DC offset of waveform, in volts')
        ]),
        Parameter('default_waveform_ch2', [
            Parameter('frequency', 1e6, float, 'frequency in Hz'),
            Parameter('amplitude', 0.5, float,
                      'output amplitude peak to peak in volts'),
            Parameter('phase', 0, float, 'output phase'),
            Parameter('offset', 0, float, 'DC offset of waveform, in volts')
        ]),

        # # Arbitrary waveforms
        # Parameter('arbitrary_waveform_ch1', [
        #     Parameter('time', np.zeros([1]), np.ndarray, '1D array of time values in seconds'),
        #     Parameter('amplitude', np.zeros([1]), np.ndarray, '1D array of amplitude values in volts')
        # ]),
        #
        # Parameter('arbitrary_waveform_ch2', [
        #     Parameter('time', np.zeros([1]), np.ndarray, '1D array of time values in seconds'),
        #     Parameter('amplitude', np.zeros([1]), np.ndarray, '1D array of amplitude values in volts')
        # ]),

        # Parameter('pulse_sequence_ch1', [
        #     Parameter('period', 1e6, float, 'time between  pulses in seconds'),
        #     Parameter('pi_pulse_width', 1e6, float, 'pulse width in seconds for pi pulse'),
        #     Parameter('half_pi_pulse_width', 1e6, float, 'pulse width in seconds for half pi pulse'),
        #     Parameter('x_amplitude', 0.5, float, 'pulse height in volts for x pulse'),
        #     Parameter('y_amplitude', 0.5, float, 'pulse height in volts for y pulse'),
        #     Parameter('sequence', [], )
        # ])

        # Parameter('run_mode_ch1', 'Burst', ['Continuous', 'Burst'], 'Run Mode: continuous or burst once'),
        # Parameter('run_mode_ch2', 'Burst', ['Continuous', 'Burst'], 'Run Mode: continuous or burst once'),
    ])

    MANUFACTURER_ID = '0x0699'
    MODEL_CODE = '0x034A'
    SERIAL_NUMBER = 'C020007'

    SIGNAL_MAX = 16382
    POINTS_MAX = 131027

    def __init__(self,
                 name=None,
                 settings=None,
                 pulse_sequence_ch1=None,
                 pulse_sequence_ch2=None):

        if pulse_sequence_ch1 is not None:
            self.pulse_sequence_ch1 = pulse_sequence_ch1
        if pulse_sequence_ch2 is not None:
            self.pulse_sequence_ch2 = pulse_sequence_ch2
        super(AWG, self).__init__(name, settings)

        #===========================================
        # Issue where visa.ResourceManager() takes 4 minutes no longer happens after using pdb to debug (??? not sure why???)
        try:
            self._connect()
        except pyvisa.errors.VisaIOError:
            print(
                'No AWG Detected!. Check that you are using the correct communication type'
            )
            raise
        except Exception as e:
            raise (e)
        #===========================================

        #self.update(self.settings)

    def _connect(self):
        rm = visa.ResourceManager()
        self.awg = rm.open_resource('::'.join([
            'USB' + str(self.settings['USB_num']), self.MANUFACTURER_ID,
            self.MODEL_CODE, self.SERIAL_NUMBER, 'INSTR'
        ]))
        self.awg.query('*IDN?')
        self.awg.write('*RST')  # reset AWG
        self.awg.write('SOUR1:BURS:MODE TRIG')
        self.awg.write('SOUR2:BURS:MODE TRIG')
        self.awg.write('SOUR1:BURS:NCYC 1')  # prepare settings for burst mode
        self.awg.write('SOUR2:BURS:NCYC 1')
        self.awg.write('OUTP1:TRIG:MODE EXT')
        self.awg.write('OUTP2:TRIG:MODE EXT')
        self.awg.write('SOUR1:VOLT:UNIT VPP')
        self.awg.write('SOUR2:VOLT:UNIT VPP')
        self.awg.write('SOUR1:VOLT:LEV:IMM:AMPL 1V')
        self.awg.write('SOUR2:VOLT:LEV:IMM:AMPL 1V')
        # at this point the analog output channels are still not enabled

    def update(self, settings):
        """
        Updates the internal settings of the MicrowaveGenerator, and then also updates physical parameters such as
        frequency, amplitude, modulation type, etc in the hardware
        Args:
            settings: a dictionary in the standard settings format
        """
        super(AWG, self).update(settings)
        # ===========================================
        for key, value in settings.items():
            if key != 'USB_num' and type(value) is not dict:
                if self.settings.valid_values[
                        key] == bool:  #converts booleans, which are more natural to store for on/off, to
                    value = int(
                        value)  #the integers used internally in the SRS
                elif (key == 'function_ch1' or key == 'function_ch2'):
                    if value == 'Arb':
                        self._waveform_to_memory(int(key[-1]))
                    value = self._func_type_to_internal(value, key)
                elif (key == 'run_mode_ch1' or key == 'run_mode_ch2'):
                    value = self._run_mode_type_to_internal(key)
                elif key == 'amplitude':
                    if value > RANGE_MAX or value < RANGE_MIN:
                        raise ValueError(
                            "Invalid amplitude. All amplitudes must be between -0.5 and +0.5V."
                        )
                key = self._param_to_internal(key)
                # only send update to instrument if connection to instrument has been established
                if self._settings_initialized:
                    self.awg.write(
                        key + ' ' + str(value)
                    )  # frequency change operation timed using timeit.timeit and
                    # completion confirmed by query('*OPC?'), found delay of <10ms
            elif type(value) is dict:  # if nested dictionary keep iterating
                for key2, value2 in value.items():
                    if key == 'default_waveform_ch1' or key == 'default_waveform_ch2':
                        if key2 == 'phase':
                            value2 = np.deg2rad(np.mod(value2, 360) - 180)
                        elif key2 == 'amplitude':
                            if value2 > RANGE_MAX or value2 < RANGE_MIN:
                                raise ValueError(
                                    "Invalid amplitude. All amplitudes must be between -0.5 and +0.5V."
                                )
                        elif key2 == 'offset':
                            if (value2 > 0.5 or value2 < -0.5):
                                raise ValueError(
                                    "All voltages programmed on the AWG must be between -0.5 and +0.5V"
                                )

                    key2 = self._param_to_internal(key2, key)
                    # only send update to instrument if connection to instrument has been established
                    if self._settings_initialized:
                        self.awg.write(
                            key2 + ' ' + str(value2)
                        )  # frequency change operation timed using timeit.timeit and
                        # completion confirmed by query('*OPC?'), found delay of <10ms

                        # print(self.awg.query('*OPC?'))
                    # elif (key == 'arbitrary_waveform_ch1' and settings['function_ch1'] == 'Arb') \
                    #         or (key == 'arbitrary_waveform_ch2' and settings['function_ch2'] == 'Arb'):
                    #     if key2 == 'time' or key2 == 'amplitude' and (type(value2) is not np.ndarray or len(value2.shape) != 1):
                    #         raise ValueError('Time is not a 1D array.')

        # ===========================================

    @property
    def _PROBES(self):
        return {}

    def read_probes(self, key):

        # assert hasattr(self, 'awg') #will cause read_probes to fail if connection not yet established, such as when called in init
        assert (
            self._settings_initialized
        )  #will cause read_probes to fail if settings (and thus also connection) not yet initialized
        assert key in list(self._PROBES.keys())

        #query always returns string, need to cast to proper return type
        if key in ['enable_output_ch1']:
            key_internal = self._param_to_internal(key, 0)
            value = int(self.awg.query(key_internal + '?'))
            if value == 1:
                value = True
            elif value == 0:
                value = False
        else:
            key_internal = self._param_to_internal(key, 0)
            value = float(self.awg.query(key_internal + '?'))

        return value

    @property
    def is_connected(self):
        try:
            self.awg.query(
                '*IDN?'
            )  # arbitrary call to check connection, throws exception on failure to get response
            return True
        except pyvisa.errors.VisaIOError:
            return False

    def _func_type_to_internal(self, param, key0=0):
        if (key0 == 'function_ch1' and param == 'Arb'):
            return 'USER1'
        elif (key0 == 'function_ch2' and param == 'Arb'):
            return 'USER2'
        elif param == 'Sine':
            return 'SIN'
        elif param == 'Square':
            return 'SQU'
        elif param == 'Pulse':
            return 'PULS'
        elif param == 'Sin(x)/x':
            return 'SINC'
        elif param == 'Noise':
            return 'PRN'
        elif param == 'DC':
            return 'DC'
        elif param == 'Gaussian':
            return 'GAUS'
        elif param == 'Lorentz':
            return 'LOR'
        elif param == 'Exponential Rise':
            return 'ERIS'
        elif param == 'Exponential Decay':
            return 'EDEC'
        elif param == 'Haversine':
            return 'HAV'

    def _run_mode_type_to_internal(self, param):
        if param == 'run_mode_ch1':
            return 'SOUR1:BURS:STAT'
        elif param == 'run_mode_ch2':
            return 'SOUR2:BURS:STAT'

    def _param_to_internal(self, param, key0=0):
        """
        Converts settings parameters to the corresponding key used for GPIB commands in the SRS.
        Args:
            param: settings parameter, ex. enable_output

        Returns: GPIB command, ex. ENBR

        """
        if param == 'enable_output_ch1':
            return 'OUT1:STAT'
        elif param == 'enable_output_ch2':
            return 'OUT2:STAT'
        elif param == 'function_ch1':
            return 'SOUR1:FUNC:SHAP'
        elif param == 'function_ch2':
            return 'SOUR1:FUNC:SHAP'
        elif param == 'frequency':
            if key0 == 'default_waveform_ch1':
                return 'SOUR1:FREQ'
            elif key0 == 'default_waveform_ch2':
                return 'SOUR2:FREQ'
        elif param == 'amplitude':
            if key0 == 'default_waveform_ch1':
                return 'SOUR1:VOLT:LEV:IMM:AMPL'
            elif key0 == 'default_waveform_ch2':
                return 'SOUR2:VOLT:LEV:IMM:AMPL'
        elif param == 'phase':
            if key0 == 'default_waveform_ch1':
                return 'SOUR1:PHAS:ADJ'
            elif key0 == 'default_waveform_ch2':
                return 'SOUR2:PHAS:ADJ'
        elif param == 'offset':
            if key0 == 'default_waveform_ch1':
                return 'SOUR1:VOLT:LEV:IMM:OFFS'
            elif key0 == 'default_waveform_ch2':
                return 'SOUR2:VOLT:LEV:IMM:OFFS'

        # Arbitrary waveform
        else:
            raise KeyError

    def _waveform_to_memory(self, channel):
        if self._settings_initialized:
            if channel == 1:
                pulse_sequence = self.__pulse_sequence_ch1
            elif channel == 2:
                pulse_sequence = self.__pulse_sequence_ch2
            else:
                raise ValueError(
                    'There are only two channels for the Tektronix AFG3022C.')

            self.awg.write('DATA:DEF EMEM,' +
                           str(self.POINTS_MAX))  # Reset edit memory
            for i in range(1, pulse_sequence.shape[1]):
                self.awg.write('DATA:DATA:LINE EMEM,' + ','.join([
                    str(el) for el in [
                        pulse_sequence[0, i - 1], pulse_sequence[1, i - 1],
                        pulse_sequence[0, i], pulse_sequence[1, i]
                    ]
                ]))

            self.awg.write('DATA:COPY USER' + str(channel) + ',EMEM')

    def _pulse_to_points(self, pulse_sequence):
        time = np.array([0.])
        amplitude = np.array([0.])

        for pulse in pulse_sequence:
            if type(pulse) is not Pulse:
                raise ValueError('List is not a sequence of Pulse objects.')
            if pulse.amplitude is None:
                raise ValueError('Amplitude not defined.')
            time = np.append(time, [
                pulse.start_time, pulse.start_time, pulse.end_time,
                pulse.end_time
            ])
            amplitude = np.append(amplitude,
                                  [0., pulse.amplitude, pulse.amplitude, 0.])

        amplitude *= self.SIGNAL_MAX / np.max(amplitude)
        time *= self.POINTS_MAX / np.max(time)

        return np.array(
            [np.floor(time).astype(int),
             np.floor(amplitude).astype(int)])

    @property
    def pulse_sequence_ch1(self):
        return self.__pulse_sequence_ch1

    @pulse_sequence_ch1.setter
    def pulse_sequence_ch1(self, pulse_sequence):
        self.__pulse_sequence_ch1 = self._pulse_to_points(pulse_sequence)

    @property
    def pulse_sequence_ch2(self):
        return self.__pulse_sequence_ch2

    @pulse_sequence_ch2.setter
    def pulse_sequence_ch2(self, pulse_sequence):
        self.__pulse_sequence_ch2 = self._pulse_to_points(pulse_sequence)
Exemple #25
0
class SimplePiezoSweep(Script):
    """
SimplePiezoSweep: Reads analog input (e.g. from photodiode) at different piezo voltages
    """

    _DEFAULT_SETTINGS = [
        Parameter('voltages', [
            Parameter('min', 0., float, 'min voltage'),
            Parameter('max', 100, float, 'max voltage'),
            Parameter('N', 25, int, 'voltage steps')
        ]),
        Parameter('dt', 0.1, float, 'time between voltage steps (seconds)'),
        Parameter('daq_type', 'cDAQ', ['PCI', 'cDAQ'],
                  'Type of daq to use for scan'),
        Parameter('ai_channel', 'ai0', ['ai0', 'ai1', 'ai2', 'ai3'],
                  'Daq channel used for voltage analog input')
    ]

    _SCRIPTS = {}

    _INSTRUMENTS = {
        'NI6259': NI6259,
        'z_piezo': PiezoController,
        'NI9263': NI9263,
        'NI9402': NI9402
    }

    def __init__(self,
                 instruments,
                 scripts=None,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """
        Example of a script that emits a QT signal for the gui
        Args:
            name (optional): name of script, if empty same as class name
            settings (optional): settings for this script, if empty same as default settings
        """
        Script.__init__(self,
                        name,
                        settings,
                        instruments,
                        scripts,
                        log_function=log_function,
                        data_path=data_path)

        # defines which daqs contain the input and output based on user selection of daq interface
        if self.settings['daq_type'] == 'PCI':
            self.daq_in = self.instruments['NI6259']['instance']
            self.daq_out = self.instruments['NI6259']['instance']
        elif self.settings['daq_type'] == 'cDAQ':
            self.daq_in = self.instruments['NI9402']['instance']
            self.daq_out = self.instruments['NI9263']['instance']

    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in the settings property
        will be overwritten in the __init__
        """
        self.data['signal'] = []
        self.data['voltages'] = []

        dt = self.settings['dt']
        sweep_voltages = np.linspace(self.settings['voltages']['min'],
                                     self.settings['voltages']['max'],
                                     self.settings['voltages']['N'])

        for index, voltage in enumerate(sweep_voltages):
            self._step_piezo(voltage, dt)
            signal = self._get_voltage()
            self.data['signal'].append(signal)

            self.data['voltages'].append(voltage)
            self.progress = 100. * index / len(sweep_voltages)
            self.updateProgress.emit(
                self.progress if self.progress < 100 else 99)

    def _step_piezo(self, voltage, wait_time):
        """
        steps the piezo.  Has to be overwritten specifically for each different hardware realization
        voltage: target piezo voltage
        wait_time: settle time after voltage step
        """
        z_piezo = self.instruments['z_piezo']['instance']
        # set the voltage on the piezo
        z_piezo.voltage = float(voltage)
        time.sleep(wait_time)

    def _get_voltage(self):
        """
        returns the current position of the galvo
        Returns: list with two floats, which give the x and y position of the galvo mirror

        """

        voltage = self.daq_in.get_analog_voltages(self.settings['ao_channel'])
        voltage = self.instruments['daq']['instance'].get_analog_voltages(
            [self.settings['ai_channel']])

        return voltage

    def _plot(self, axes_list, data=None):
        # fit the data and set piezo to focus spot
        if data is None:
            data = self.data

        axis_focus, axis_image = axes_list

        axis_focus.plot(data['voltages'], data['signal'], 'o-')

        axis_focus.hold(False)

        axis_focus.set_xlabel('Piezo Voltage [V]')
        axis_focus.set_ylabel('Detector Voltage [V]')
class Daq_Read_Counter(Script):
    """
This script reads the Counter input from the DAQ and plots it.

WARNING: Only implemented either for the PCI DAQ (NI6259) or cDAQ (NI9402) !!!!

If you want to use it make sure that the right instrument is defined in _INSTRUMENTS = {'daq': NI9402} in the python code.

    """
    _DEFAULT_SETTINGS = [
        Parameter('integration_time', .25, float, 'Time per data point (s)'),
        Parameter('counter_channel', 'ctr0', ['ctr0', 'ctr1'],
                  'Daq channel used for counter'),
        Parameter(
            'total_int_time', 3.0, float,
            'Total time to integrate (s) (if -1 then it will go indefinitely)'
        ),  # added by ER 20180606
        Parameter('track_laser_power_photodiode1', [
            Parameter(
                'on/off', False, bool,
                'If true, measure and normalize out laser power drifts during daq_read_counter'
            ),
            Parameter(
                'ai_channel', 'ai2', ['ai0', 'ai1', 'ai2', 'ai3', 'ai4'],
                'channel to use for analog input, to which the photodiode is connected'
            )
        ]),
        Parameter('track_laser_power_photodiode2', [
            Parameter(
                'on/off', False, bool,
                'If true, measure and save laser power drifts during daq_read_counter on this photodiode. Cant use both simultaneously'
            ),
            Parameter(
                'ai_channel', 'ai4', ['ai0', 'ai1', 'ai2', 'ai3', 'ai4'],
                'channel to use for photodiode 2, cant be the same as the track_laser_power photodiode'
            )
        ])
    ]

    _INSTRUMENTS = {'daq': NI9402}

    _SCRIPTS = {}

    def __init__(self,
                 instruments,
                 scripts=None,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """
        Example of a script that emits a QT signal for the gui
        Args:
            name (optional): name of script, if empty same as class name
            settings (optional): settings for this script, if empty same as default settings
        """
        Script.__init__(self,
                        name,
                        settings=settings,
                        scripts=scripts,
                        instruments=instruments,
                        log_function=log_function,
                        data_path=data_path)

        self.data = {
            'counts': deque(),
            'laser_power': deque(),
            'normalized_counts': deque(),
            'laser_power2': deque()
        }

    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in the settings property
        will be overwritten in the __init__
        """

        if self.settings['track_laser_power_photodiode1'][
                'on/off'] and self.settings['track_laser_power_photodiode2'][
                    'on/off']:
            print(
                'cant use both photodiodes at the same time - only use one AI channel at a time, unfortunately :-('
            )
            return

        sample_rate = float(2) / self.settings['integration_time']
        normalization = self.settings['integration_time'] / .001
        self.instruments['daq']['instance'].settings['digital_input'][
            self.settings['counter_channel']]['sample_rate'] = sample_rate
        self.data = {
            'counts': deque(),
            'laser_power': deque(),
            'normalized_counts': deque(),
            'laser_power2': deque()
        }
        self.last_value = 0
        sample_num = 2

        task = self.instruments['daq']['instance'].setup_counter(
            "ctr0", sample_num, continuous_acquisition=True)

        if self.settings['track_laser_power_photodiode1']['on/off'] == True:
            aitask = self.instruments['daq']['instance'].setup_AI(
                self.settings['track_laser_power_photodiode1']['ai_channel'],
                sample_num,
                continuous=
                True,  # continuous sampling still reads every clock tick, here set to the clock of the counter
                clk_source=task)

        if self.settings['track_laser_power_photodiode2']['on/off'] == True:
            aitask2 = self.instruments['daq']['instance'].setup_AI(
                self.settings['track_laser_power_photodiode2']['ai_channel'],
                sample_num,
                continuous=
                True,  # continuous sampling still reads every clock tick, here set to the clock of the counter
                clk_source=task)
            print('aitask2: ', aitask2)

        # maximum number of samples if total_int_time > 0
        if self.settings['total_int_time'] > 0:
            max_samples = np.floor(self.settings['total_int_time'] /
                                   self.settings['integration_time'])

        # start counter and scanning sequence
        if (self.settings['track_laser_power_photodiode1']['on/off'] and
                not self.settings['track_laser_power_photodiode2']['on/off']):
            self.instruments['daq']['instance'].run(aitask)
        elif (self.settings['track_laser_power_photodiode2']['on/off'] and
              not self.settings['track_laser_power_photodiode1']['on/off']):
            self.instruments['daq']['instance'].run(aitask2)

        self.instruments['daq']['instance'].run(task)

        # ER 20180827 wait for at least one clock tick to go by to start with a full clock tick of acquisition time for the first bin
        time.sleep(self.settings['integration_time'])

        sample_index = 0  # keep track of samples made to know when to stop if finite integration time

        while True:
            if self._abort:
                break

            # TODO: this is currently a nonblocking read so we add a time.sleep at the end so it doesn't read faster
            # than it acquires, this should be replaced with a blocking read in the future
            if self.settings['track_laser_power_photodiode1'][
                    'on/off'] == True:
                raw_data_laser, num_read_laser = self.instruments['daq'][
                    'instance'].read(aitask)
            if self.settings['track_laser_power_photodiode2'][
                    'on/off'] == True:
                raw_data_laser2, num_read_laser2 = self.instruments['daq'][
                    'instance'].read(aitask2)

            raw_data, num_read = self.instruments['daq']['instance'].read(task)
            #skip first read, which gives an anomolous value
            if num_read.value == 1:
                self.last_value = raw_data[
                    0]  #update running value to last measured value to prevent count spikes
                time.sleep(2.0 / sample_rate)
                continue

            tmp_count = 0
            for value in raw_data:
                new_val = ((float(value) - self.last_value) / normalization)
                self.data['counts'].append(new_val)
                self.last_value = value
                if self.settings['track_laser_power_photodiode1'][
                        'on/off'] == True:
                    self.data['laser_power'].append(raw_data_laser[tmp_count])
                if self.settings['track_laser_power_photodiode2'][
                        'on/off'] == True:
                    self.data['laser_power2'].append(
                        raw_data_laser2[tmp_count])

                tmp_count = tmp_count + 1

            if self.settings['total_int_time'] > 0:
                self.progress = sample_index / max_samples
            else:
                self.progress = 50.
            self.updateProgress.emit(int(self.progress))

            time.sleep(2.0 / sample_rate)
            sample_index = sample_index + 1
            if self.settings[
                    'total_int_time'] > 0. and sample_index >= max_samples:  # if the maximum integration time is hit
                self._abort = True  # tell the script to abort

        # clean up APD tasks
        self.instruments['daq']['instance'].stop(task)
        if self.settings['track_laser_power_photodiode1']['on/off'] == True:
            self.instruments['daq']['instance'].stop(aitask)
        if self.settings['track_laser_power_photodiode2']['on/off'] == True:
            self.instruments['daq']['instance'].stop(aitask2)

        self.data['counts'] = list(self.data['counts'])

        if self.settings['track_laser_power_photodiode1']['on/off'] == True:
            self.data['laser_power'] = list(self.data['laser_power'])
            self.data['normalized_counts'] = list(
                np.divide(
                    np.multiply(self.data['counts'],
                                np.mean(self.data['laser_power'])),
                    self.data['laser_power']))
        if self.settings['track_laser_power_photodiode2']['on/off'] == True:
            self.data['laser_power2'] = list(self.data['laser_power2'])

    def plot(self, figure_list):
        super(Daq_Read_Counter, self).plot([figure_list[1]])

    def _plot(self, axes_list, data=None):
        # COMMENT_ME

        if data is None:
            data = self.data

        if len(data['counts']) > 0:
            if self.settings['track_laser_power_photodiode1'][
                    'on/off'] == True:
                array_to_plot = np.delete(
                    np.divide(
                        np.multiply(self.data['counts'],
                                    np.mean(self.data['laser_power'])),
                        self.data['laser_power']), 0)
            else:
                array_to_plot = np.delete(data['counts'], 0)

            plot_counts(axes_list[0], array_to_plot)

    def _update_plot(self, axes_list, data=None):
        if data is None:
            data = self.data

        if data:
            if self.settings['track_laser_power_photodiode1'][
                    'on/off'] == True:
                array_to_plot = np.delete(
                    np.divide(
                        np.multiply(self.data['counts'],
                                    np.mean(self.data['laser_power'])),
                        self.data['laser_power']), 0)
            else:
                array_to_plot = np.delete(data['counts'], 0)

            update_counts_vs_pos(
                axes_list[0], array_to_plot,
                np.linspace(0, len(array_to_plot), len(array_to_plot)))
class DAQ_Timetrace(Script):
    """
SimplePiezoSweep: Reads analog input (e.g. from photodiode) at different piezo voltages
    """

    _DEFAULT_SETTINGS = [
        Parameter('sample_rate', 1000, float, 'sample acquisition rate (Hz)'),
        Parameter('ai_channel', 'ai2', ['ai0', 'ai1', 'ai2', 'ai3'],
                  'Daq channel used for voltage analog input'),
        Parameter('acquisition_time', 10, float, 'time to acquire (s)')
    ]

    _SCRIPTS = {}

    _INSTRUMENTS = {'daq': NI6259}

    def __init__(self,
                 instruments,
                 scripts=None,
                 name=None,
                 settings=None,
                 log_function=None,
                 data_path=None):
        """
        Example of a script that emits a QT signal for the gui
        Args:
            name (optional): name of script, if empty same as class name
            settings (optional): settings for this script, if empty same as default settings
        """
        Script.__init__(self,
                        name,
                        settings,
                        instruments,
                        scripts,
                        log_function=log_function,
                        data_path=data_path)

    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in the settings property
        will be overwritten in the __init__
        """
        self.data['times'] = np.linspace(
            0, self.settings['acquisition_time'] * 1e9,
            self.settings['acquisition_time'] * self.settings['sample_rate'])
        self.data['voltages'] = []

        sample_rate = self.settings['sample_rate']
        channel = self.settings['ai_channel']
        self.instruments['daq']['instance'].settings['analog_input'][channel][
            'sample_rate'] = sample_rate

        task = self.instruments['daq']['instance'].setup_AI(channel,
                                                            1000,
                                                            continuous=True)
        self.instruments['daq']['instance'].run(task)

        start_time = time.time()
        while ((time.time() - start_time) < self.settings['acquisition_time']):
            if self._abort:
                break

            raw_data, num_read = self.instruments['daq']['instance'].read(task)
            self.data['voltages'] = self.data['voltages'] + list(raw_data)

            self.progress = 50.
            self.updateProgress.emit(int(self.progress))

        # clean up APD tasks
        self.instruments['daq']['instance'].stop(task)

    def _plot(self, axes_list, data=None):
        # COMMENT_ME

        if data is None:
            data = self.data

        axes_list[0].hold(False)

        if data:
            # plot_1d_simple_timetrace_ns(axes_list[0], self.data['times'][0:len(data['voltages'])], np.array(data['voltages']))
            axes_list[0].plot(data['voltages'])
Exemple #28
0
class OptotuneLens(Instrument):
    """
    Instrument class to control an Optotune Lens Driver 4. Tested with an EL-10-30-TC, but any elens compatable with
    the Lens Driver 4 should work.
    """

    _DEFAULT_SETTINGS = Parameter([
        Parameter('port', 'COM10', str, 'serial port on which to connect'),
        Parameter('baudrate', 115200, int, 'baudrate of connection'),
        Parameter('timeout', .1, float, 'connection timeout'),
        Parameter('current', 0.0, float, 'current applied to lens')
    ])

    def __init__(self, name=None, settings=None):
        """
        Initializes connection to optotune lens. If none found, raises exception.
        Args:
            name: instrument name
            settings: dictionary of settings to override defaults
        """
        super(OptotuneLens, self).__init__(name, settings)
        self._is_connected = False
        try:
            self.connect(port=self.settings['port'],
                         baudrate=self.settings['baudrate'],
                         timeout=self.settings['timeout'])
        except Exception:
            print('No ELens Detected')
            raise

    def connect(self, port, baudrate, timeout):
        """
        Connects to elens using the serial port
        Args:
            port: COM port on which to connect
            baudrate: baud rate of connection. Check value required by device
            timeout: time to wait before abandoning communication

        Poststate: self._is_connected is True

        """
        self.ser = serial.Serial(port=port, baudrate=baudrate, timeout=timeout)
        self._is_connected = True

    def update(self, settings):
        """
        Updates internal settings, and sets ELens current on hardware if that is changed
        Args:
            settings: dictionary of settings to update

        Poststate: changes current on ELens if it is updated

        """
        super(OptotuneLens, self).update(settings)
        for key, value in settings.items():
            if self._settings_initialized:
                if key == 'current':
                    self.set_current(value)

    @property
    def _PROBES(self):
        """
        Returns: a dictionary that contains the values that can be read from the instrument
        the key is the name of the value and the value of the dictionary is an info
        """
        return {'current': 'the current applied to the ELens'}

    def read_probes(self, key):
        """
        requests value from the instrument and returns it
        Args:
            key: name of requested value

        Returns: reads values from instrument

        """
        assert key in list(self._PROBES.keys())
        assert isinstance(key, str)

        if key in ['current']:
            self.ser.write(bytearray.fromhex('41 72 00 00 b4 27'))
            response = bytearray(self.ser.readline())
            current_scaled = int.from_bytes(response[1:3], 'big')
            return ((current_scaled / 4096) * 292.84)

    @property
    def is_connected(self):
        """

        Returns: True if currently connected to piezo controller, False otherwise

        """
        try:
            self.current
            return True
        except serial.serialutil.SerialTimeoutException:
            return False

    def __del__(self):
        """
        Ensures that connection to hardware is closed on deletion of PiezoController object, to prevent a stale
        connection from a closed object from blocking further connections from new objects
        """
        if self._is_connected:
            self.ser.close()

    def set_current(self, current_mA):
        """
        Sets the voltage on the piezo.
        Args:
            voltage: voltage (in V) to set

        """
        # self.ser.write((self.settings['axis'] + 'voltage=' + str(voltage) + '\r').encode('ascii'))
        if (current_mA > MAX_CURRENT):
            self.log('cannot set current above ' + str(MAX_CURRENT))
            return
        current_scaled = int(current_mA / MAX_CURRENT * 4096)
        writebytes = bytearray((b'Aw' + current_scaled.to_bytes(2, 'big')))
        checksum = CRC16IBM().compute_checksum_bytes(writebytes)
        self.ser.write(writebytes + checksum)
        error = self.ser.readline()
        if error:
            self.log('setting of current failed with error code ' + error)
class ExampleScript(Script):
    """
Example Script that has all different types of parameters (integer, str, fload, point, list of parameters). Plots 1D and 2D data.
    """

    _DEFAULT_SETTINGS = [
        Parameter('count', 3, int),
        Parameter('name', 'this is a counter'),
        Parameter('wait_time', 0.1, float),
        Parameter('point2',
                  [Parameter('x', 0.1, float, 'x-coordinate'),
                   Parameter('y', 0.1, float, 'y-coordinate')
                  ]),
        Parameter('plot_style', 'main', ['main', 'aux', '2D', 'two'])
    ]

    _INSTRUMENTS = {}
    _SCRIPTS = {}

    def __init__(self, name=None, settings=None, log_function = None, data_path = None):
        """
        Example of a script
        Args:
            name (optional): name of script, if empty same as class name
            settings (optional): settings for this script, if empty same as default settings
        """
        Script.__init__(self, name, settings, log_function= log_function, data_path = data_path)



    def _function(self):
        """
        This is the actual function that will be executed. It uses only information that is provided in the settings property
        will be overwritten in the __init__
        """

        # some generic function
        import time
        import random
        self.data['random data'] = None
        self.data['image data'] = None
        count = self.settings['count']
        name = self.settings['name']
        wait_time = self.settings['wait_time']

        data = []
        self.log('I ({:s}) am a test function counting to {:d} and creating random values'.format(self.name, count))
        for i in range(count):
            time.sleep(wait_time)
            self.log('{:s} count {:02d}'.format(self.name, i))
            data.append(random.random())
            self.data = {'random data': data}
            self.progress = 100. * (i + 1) / count
            self.updateProgress.emit(self.progress)


        self.data = {'random data':data}


        # create image data
        Nx = int(np.sqrt(len(self.data['random data'])))
        img = np.array(self.data['random data'][0:Nx ** 2])
        img = img.reshape((Nx, Nx))
        self.data.update({'image data': img})


    def _plot(self, axes_list, data = None):
        """
        plots the data only the axes objects that are provided in axes_list
        Args:
            axes_list: a list of axes objects, this should be implemented in each subscript
            data: data to be plotted if empty take self.data
        Returns: None

        """

        plot_type = self.settings['plot_style']
        if data is None:
            data = self.data

        if data is not None and data is not {}:
            if plot_type in ('main', 'two'):
                if not data['random data'] is None:
                    axes_list[0].plot(data['random data'])
                    axes_list[0].hold(False)
            if plot_type in ('aux', 'two', '2D'):
                if not data['random data'] is None:
                    axes_list[1].plot(data['random data'])
                    axes_list[1].hold(False)
            if plot_type == '2D':
                if 'image data' in data and not data['image data'] is None:
                    fig = axes_list[0].get_figure()
                    implot = axes_list[0].imshow(data['image data'], cmap='pink', interpolation="nearest", extent=[-1,1,1,-1])
                    fig.colorbar(implot, label='kcounts/sec')

    def _update(self, axes_list):
        """
        updates the data in already existing plots. the axes objects are provided in axes_list
        Args:
            axes_list: a list of axes objects, this should be implemented in each subscript

        Returns: None

        """
        plot_type = self.settings['plot_style']
        if plot_type == '2D':
            # we expect exactely one image in the axes object (see ScriptDummy.plot)
            implot = axes_list[1].get_images()[0]
            # now update the data
            implot.set_data(self.data['random data'])

            colorbar = implot.colorbar

            if not colorbar is None:
                colorbar.update_bruteforce(implot)

        else:
            # fall back to default behaviour
            Script._update(self, axes_list)
Exemple #30
0
class ThorlabsServo(Instrument):
    dll_path = get_config_value(
        'KINESIS_DLL_PATH',
        os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.txt'))
    if dll_path:
        sys.path.insert(0, dll_path)
        # makes each dll, corresponding to a namespace, avaliable to python at runtime
        try:
            clr.AddReference('ThorLabs.MotionControl.DeviceManagerCLI')
            clr.AddReference('Thorlabs.MotionControl.TCube.DCServoCLI')
            clr.AddReference('Thorlabs.MotionControl.KCube.DCServoCLI')
            clr.AddReference('System')
            # imports classes from the namespaces. All read as unresolved references because python doesn't know about the dlls
            # until runtime
            # adds .NET stuctures corresponding to primitives
            from System import Decimal, Double, String
            # names will be in red due to the fact that pycharm can't see the thorlabs library until runtime
            from Thorlabs.MotionControl.DeviceManagerCLI import DeviceManagerCLI
            from Thorlabs.MotionControl.TCube.DCServoCLI import TCubeDCServo
            from Thorlabs.MotionControl.KCube.DCServoCLI import KCubeDCServo
        except Exception as exception_details:
            print("Could not load Thorlabs dll's to control Thorlabs servos.")
            print("exception details " + str(exception_details))
            DeviceManagerCLI = None
            TCubeDCServo = None
            KCubeDCServo = None
    else:
        print(
            "Could not import Thorlabs dll's --- will not be able to initialize object."
        )
        DeviceManagerCLI = None
        TCubeDCServo = None
        KCubeDCServo = None

    _DEFAULT_SETTINGS = Parameter([
        Parameter('serial_number', 27501971, int,
                  'serial number written on device'),
        Parameter('position', 0, float, 'servo position (from 0 to 6 in mm)'),
        Parameter('velocity', 0, float, 'servo maximum velocity in mm/s')
    ])

    def __init__(self, name=None, settings=None):
        """
        Initializes and connects to motors. Must define a variable self.Servo that aliases the correct device, ex.
        self.Servo = KCubeDCServo, and must call self._connect at some point.
        Args:
            name:
            settings:
        """
        raise NotImplementedError

    def _connect(self):
        """
        Connects to the servo using parameters defined elsewhere in the code.
        """
        raise NotImplementedError

    def update(self, settings):
        '''
        Updates internal settings, as well as the position and velocity set on the physical device. If the instrument's
        serial number is changed, will disconnect from the current device (if any) and reconnect to the new device.
        Args:
            settings: A dictionary in the form of settings as seen in default settings
        '''
        super(ThorlabsServo, self).update(settings)
        #update will usually trigger in super.__init__, which is generally the first line of self.__init__. At this
        #point, the servo won't be connected, so don't make changes in hardware at this point. After the end of the
        #__init__ when initialization is complete and the servo is connected, we can then have changes update hardware
        if self._settings_initialized:
            for key, value in settings.items():
                if key == 'position':
                    self._move_servo(value)
                elif key == 'velocity':
                    self._set_velocity(value)
                elif key == 'serial_number':
                    #if it exists, disconnect from previous device
                    try:
                        self.device.StopPolling()
                        self.device.Disconnect()
                    #either the old connection was severed or there was no old connection, so now (re)connect
                    finally:
                        self._connect()

    @property
    def _PROBES(self):
        return {
            'position': 'servo position in mm',
            'velocity': 'servo velocity in mm/s'
        }

    def read_probes(self, key):
        assert key in self._PROBES.keys()
        assert isinstance(key, str)
        #query always returns string, need to cast to proper return type
        if key in ['position']:
            return self._get_position()
        elif key in ['velocity']:
            return self._get_velocity()

    @property
    def is_connected(self):
        return self.connected
        # DeviceManagerCLI.BuildDeviceList()
        # return(str(self.settings['serial_number']) in DeviceManagerCLI.GetDeviceList(self.Servo.DevicePrefix))

    # def __del__(self):
    #     '''
    #     Cleans up TDC001 connection
    #     :PostState: TDC001 is disconnected
    #     '''
    #     self.device.StopPolling()
    #     self.device.Disconnect()
    def goto_home(self):
        '''
        Recenters device at the home position. Raises an exception on failure.
        '''
        try:
            self.device.Home(60000)
        except Exception:
            print("Failed to move to position")
            raise

    def _move_servo(self, position, velocity=0):
        '''
        Move servo to given position with given maximum velocity. Raises an exception on failure.
        :param position: position in mm, ranges from 0-6
        :param velocity: maximum velocity in mm/s, ranges from 0-2.5
        :PostState: servo has moved
        '''
        try:
            if (velocity != 0):
                self._set_velocity(velocity)
            # print("Moving Device to " + str(position))
            print('initialized3', self.device.IsSettingsInitialized())
            self.device.MoveTo(self._Py_Decimal(position), 60000)
        except Exception:
            print("Failed to move to position")
            raise

    def _get_position(self):
        '''
        :return: position of servo
        '''
        if (not self.connected):
            return -1
        else:
            return self._Undo_Decimal(self.device.Position)

    def _set_velocity(self, velocity):
        '''
        :param maximum velocity in mm/s, ranges from 0-2.5
        :PostState: velocity changed in hardware
        '''
        if (velocity != 0):
            velPars = self.device.GetVelocityParams()
            velPars.MaxVelocity = self._Py_Decimal(velocity)
            self.device.SetVelocityParams(velPars)

    def _get_velocity(self):
        '''
        :return: maximum velocity setting
        '''
        if (not self.connected):
            return -1
        else:
            return self._Undo_Decimal(
                self.device.GetVelocityParams().MaxVelocity)

    def _Py_Decimal(self, value):
        '''
        Casting a python double to System.Decimal results in the Decimal having only integer values, likely due to an
        improper selection of the overloaded Decimal function. Casting it first to System.Double, which always maintains
        precision, then from Double to Decimal, where the proper overloaded function is clear, bypasses this issue
        :param value: a python double
        :return: the input as a System.Decimal
        '''
        return Decimal(Double(value))

    def _Undo_Decimal(self, value):
        '''
        Casting back from System.Decimal to a python float fails due to overloading issues, but one can successfully
        cast back to a string. Thus, we use a two-part cast to return to python numeric types
        :param value: a System.Decimal
        :return: the input as a python float
        '''
        return float(str(value))