class ScriptDummy(Script): #This is the signal that will be emitted during the processing. #By including int as an argument, it lets the signal know to expect #an integer argument when emitting. # updateProgress = QtCore.Signal(int) _DEFAULT_SETTINGS = Parameter([ Parameter('path', 'C:\Users\Experiment\Desktop\\tmp_data', str, 'path for data'), Parameter('tag', 'dummy_tag', str, 'tag for data'), Parameter('save', True, bool, 'save data on/off'), 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') ]) ]) _INSTRUMENTS = {} _SCRIPTS = {} def __init__(self, name=None, settings=None, log_output=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_output=log_output) 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 count = self.settings['count'] name = self.settings['name'] wait_time = self.settings['wait_time'] data = [] self.log( 'I am a test function counting to {:d} and creating random values'. format(count)) for i in range(count): time.sleep(wait_time) self.log('count {:02d}'.format(i)) data.append(random.random()) self.data = {'random data': data} if self.settings['save']: self.save()
def _parameters_default(self): ''' returns the default parameter_list of the instrument :return: ''' parameters_default = Parameter([ Parameter('port', 'COM5', ['COM5', 'COM3'], 'com port to which maestro controler is connected') ]) return parameters_default
def parameters_default(self): ''' returns the default parameter_list of the instrument :return: ''' parameter_list_default = [ Parameter('start', 1.8e6, (float, int), 'start value of sweep'), Parameter('stop', 1.9e6, (float, int), 'end value of sweep'), Parameter('samplecount', 101, int, 'number of data points'), Parameter('gridnode', 'oscs/0/freq', ['oscs/0/freq', 'oscs/1/freq'], 'start value of sweep'), Parameter('xmapping', 0, [0, 1], 'mapping 0 = linear, 1 = logarithmic'), Parameter('bandwidthcontrol', 2, [2], '2 = automatic bandwidth control'), Parameter( 'scan', 0, [0, 1, 2], 'scan direction 0 = sequential, 1 = binary (non-sequential, each point once), 2 = bidirecctional (forward then reverse)' ), Parameter('loopcount', 1, int, 'number of times it sweeps'), Parameter('averaging/sample', 1, int, 'number of samples to average over') ] return parameter_list_default
def _settings_default(self): ''' returns the default settings of the script settings contain Parameters, Instruments and Scripts :return: ''' settings_default = Parameter([ Parameter('parameter', 1), Parameter('file_path', './some/path'), Parameter('instrument', Instrument()) ]) return settings_default
def _parameters_default(self): ''' returns the default parameter_list of the instrument this function should be over written in any subclass ''' parameters_default = 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)') ]) ]) return parameters_default
class ScriptDummyWithQtSignal(Script, QThread): # NOTE THAT THE ORDER OF Script and QThread IS IMPORTANT!! _DEFAULT_SETTINGS = Parameter([ Parameter('count', 10, int), Parameter('name', 'this is a counter'), Parameter('wait_time', 0.1, float) ]) _INSTRUMENTS = {} _SCRIPTS = {} #This is the signal that will be emitted during the processing. #By including int as an argument, it lets the signal know to expect #an integer argument when emitting. updateProgress = Signal(int) def __init__(self, name=None, settings=None, log_output=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, log_output=log_output) # QtCore.QThread.__init__(self) QThread.__init__(self) 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 count = self.settings['count'] name = self.settings['name'] wait_time = self.settings['wait_time'] self.log('I am a test function counting to {:d}...'.format(count)) data = [] for i in range(count): time.sleep(wait_time) progress = int(100 * (i + 1) / count) self.updateProgress.emit(progress) data.append(random.random()) self.data = {'random data': data}
def parameters_default(self): """ returns the default parameter_list of the instrument :return: """ possible_com_ports = ['COM' + str(i) for i in range(0, 256)] parameter_list_default = [ Parameter('port', 'COM4', 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') ] return parameter_list_default
class NI7845RReadAnalogIO(Instrument): import src.labview_fpga_lib.read_ai_ao.read_ai_ao as FPGAlib _DEFAULT_SETTINGS = Parameter([ Parameter('AO0', 0.0, float, 'analog output channel 0 in volt'), Parameter('AO1', 0.0, float, 'analog output channel 1 in volt'), Parameter('AO2', 0.0, float, 'analog output channel 2 in volt'), Parameter('AO3', 0.0, float, 'analog output channel 3 in volt'), Parameter('AO4', 0.0, float, 'analog output channel 4 in volt'), Parameter('AO5', 0.0, float, 'analog output channel 5 in volt'), Parameter('AO6', 0.0, float, 'analog output channel 6 in volt'), Parameter('AO7', 0.0, float, 'analog output channel 7 in volt') ]) _PROBES = { 'AI0': 'analog input channel 0 in bit', 'AI1': 'analog input channel 1 in bit', 'AI2': 'analog input channel 2 in bit', 'AI3': 'analog input channel 3 in bit', 'AI4': 'analog input channel 4 in bit', 'AI5': 'analog input channel 5 in bit', 'AI6': 'analog input channel 6 in bit', 'AI7': 'analog input channel 7 in bit' } def __init__(self, name=None, settings=None): super(NI7845RReadAnalogIO, self).__init__(name, settings) # start fpga self.fpga = self.FPGAlib.NI7845R() self.fpga.start() self.update(self.settings) def __del__(self): self.fpga.stop() def read_probes(self, key): assert key in self._PROBES.keys(), "key assertion failed %s" % str(key) value = getattr(self.FPGAlib, 'read_{:s}'.format(key))(self.fpga.session, self.fpga.status) return value def update(self, settings): super(NI7845RReadAnalogIO, self).update(settings) for key, value in settings.iteritems(): if key in ['AO0', 'AO1', 'AO2', 'AO3', 'AO4', 'AO5', 'AO6', 'AO7']: print('SGL_to_U32(value)', volt_2_bit(value)) getattr(self.FPGAlib, 'set_{:s}'.format(key))(volt_2_bit(value), self.fpga.session, self.fpga.status)
def Ttest_dynamic_setter(self): test = Instrument() new_val = 30 #test.test1 = 30 test.update_parameters(Parameter('test1', 30)) if get_elemet('test1', test.parameters).value != test.test1: #print(test.parameters) self.fail('setter function doesn\'t work')
def Ttest_QString(self): from PyQt4 import QtCore test = Instrument() test.update_parameters( Parameter('test1', QtCore.QString(unicode('10')))) test.update_parameters({'test1': QtCore.QString(unicode('10'))})
class ScriptDummyWithInstrument(Script): _DEFAULT_SETTINGS = Parameter([ Parameter('count', 0, int), Parameter('name', 'this is a counter'), Parameter('wait_time', 0.1, float) ]) _INSTRUMENTS = {'dummy_instrument': DummyInstrument} _SCRIPTS = {} def __init__(self, instruments, name=None, settings=None, log_output=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_output=log_output) 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 """ import time count = self.settings['count'] name = self.settings['name'] wait_time = self.settings['wait_time'] self.log('I am a test function counting to {:d}...'.format(count)) for i in range(count): self.log('signal from dummy instrument {:s}: {:0.3f}'.format( name, self.instruments['dummy_instrument'].value1)) time.sleep(wait_time)
class ScriptDummyWithSubScript(Script): _DEFAULT_SETTINGS = Parameter([ Parameter('repetitions', 0, int, 'times the subscript will be executed') ]) _INSTRUMENTS = {} _SCRIPTS = {'sub_script': ScriptDummy} def __init__(self, scripts, name=None, settings=None, log_output=None): """ Example of a script that makes use of an instrument Args: scripts: suscript that will be excecuted by this script 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, scripts=scripts, log_output=log_output) 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 """ import time script = self.scripts['sub_script'] N = self.settings['repetitions'] self.log( 'I am a test function runnning suscript {:s} {:d} times'.format( script.name, N)) for i in range(N): self.log('run number {:d} / {:d}'.format(i + 1, N)) script.run()
def parameters_default(self): """ parameters_default lists the default Parameters used by the Spectrum Analyzer Returns: a list of Parameter objects for the parameters associated with the instrument. """ parameter_list_default = [ Parameter( 'visa_resource', 'USB0::0x0957::0xFFEF::CN0323B356::INSTR', (str), 'pyVisa instrument identifier, ' 'to make a connection using the pyVisa package.'), Parameter('start_frequency', 1.5e9, (int), 'start frequency of spectrum analyzer frequency range'), Parameter('stop_frequency', 3e9, (int), 'stop frequency of spectrum analyzer frequency range') ] return parameter_list_default
def test_create_widget_parameters(self): 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)'), Parameter('test2_3', 'a', ['a', 'b', 'c'], 'test parameter (list)'), Parameter('test2_4', False, bool, 'test parameter (bool)') ]), Parameter('test3', 'aa', ['aa', 'bb', 'cc'], 'test parameter (list)'), Parameter('test4', False, bool, 'test parameter (bool)') ]) app = QtGui.QApplication(sys.argv) ex = UI(parameters)
def check_settings_list(settings): ''' check if settings is a list of settings or a dictionary if it is a dictionary we create a settings list from it ''' if isinstance(settings, dict): settings_new = [] for key, value in settings.iteritems(): settings_new.append(Parameter(key, value)) elif isinstance(settings, list): # if list element is not a parameter, instrument or script cast it into a parameter settings_new = [ element if isinstance(element, (Parameter, Instrument, Script)) else Parameter(element) for element in settings ] else: raise TypeError( 'settings should be a valid list or dictionary!') return settings_new
def _parameters_default(self): ''' returns the default parameter_list of the instrument :return: ''' parameters_default = Parameter([ Parameter('channel', 0, int, 'channel to which motor is connected'), Parameter('open', True, bool, 'beam block open or closed'), Parameter('settle_time', 0.2, float,'settling time'), Parameter('position_open', 4*1900, int,'position corresponding to open'), Parameter('position_closed', 4*950, int,'position corresponding to closed') ]) return parameters_default
def settings_default(self): ''' returns the default settings of the script settings contain Parameters, Instruments and Scripts :return: ''' settings_default = [ Parameter('a', 0, [0, 1]), Parameter('txt', 'a', ['a', 'b']), Parameter('param', [Parameter('a', 0, [0, 1]), Parameter('b', 2, [2, 3])]), Parameter({'b': 0.1}), Parameter({'b': True}), Instrument_Dummy('dummy inst') ] return settings_default
class SpectrumAnalyzer(Instrument): """ This class provides a python implementation of the Keysight N9320B 9kHz-3.0GHz spectrum analyzer with trigger generator. """ _INSTRUMENT_IDENTIFIER = u'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', 0.0, 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('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'), 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 inputted 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) rm = visa.ResourceManager() 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') self._last_update_time = time.time() self._wait_for_spec_anal() self.update({'mode': 'SpectrumAnalyzer'}) def update(self, settings): super(SpectrumAnalyzer, self).update(settings) self._wait_for_spec_anal() 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() 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): self.spec_anal.write('SENS:FREQ:START ' + str(start_freq)) def _get_start_frequency(self): return float(self.spec_anal.query('SENS:FREQ:START?\n')) def _set_stop_frequency(self, stop_freq): self.spec_anal.write('SENS:FREQ:STOP ' + str(stop_freq)) def _get_stop_frequency(self): return float(self.spec_anal.query('SENS:FREQ:STOP?\n')) def _toggle_output(self, state): 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): if self.mode == 'SpectrumAnalyzer': return False elif self.mode == 'TrackingGenerator': return bool(int(self.spec_anal.query('OUTPUT:STATE?'))) def _get_mode(self): 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): 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): 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)] def _get_bandwidth(self): return float(self.spec_anal.query('BANDWIDTH?')) def _get_output_power(self): return float(self.spec_anal.query('SOURCE:POWER?')) def _set_output_power(self, power): assert self.mode == 'TrackingGenerator', "mode need to be 'TrackingGenerator' to change power" return self.spec_anal.write('SOURCE:POWER ' + str(power)) def __del__(self): self.wait_for_spec_anal() self._set_mode('SpectrumAnalyzer') self.spec_anal.close() def _wait_for_spec_anal(self): if self._last_update_time - time.time() < 1.0: time.sleep(1) self._last_update_time = time.time()
class DummyInstrument(Instrument): _DEFAULT_SETTINGS = Parameter([ Parameter('test1', 0, int, 'some int parameter'), Parameter('output probe2', 0, int, 'return value of probe 2 (int)'), Parameter('test2', [ Parameter('test2_1', 'string', str, 'test parameter (str)'), Parameter('test2_2', 0.0, float, 'test parameter (float)') ]) ]) _PROBES = { 'value1': 'this is some value from the instrument', 'value2': 'this is another', 'internal': 'gives the internal state variable', 'deep_internal': 'gives another internal state variable' } def __init__(self, name=None, settings=None): super(DummyInstrument, self).__init__(name, settings) self._internal_state = None self._internal_state_deep = None def update(self, settings): ''' updates the internal dictionary and sends changed values to instrument Args: settings: parameters to be set # mabe in the future: # Returns: boolean that is true if update successful ''' Instrument.update(self, settings) for key, value in settings.iteritems(): if key == 'test1': self._internal_state = value def read_probes(self, key): """ requestes value from the instrument and returns it Args: key: name of requested value Returns: reads values from instrument """ assert key in self._PROBES.keys() import random if key == 'value1': value = random.random() elif key == 'value2': value = self.settings['output probe2'] elif key == 'internal': value = self._internal_state elif key == 'deep_internal': value = self._internal_state_deep return value @property def is_connected(self): ''' check if instrument is active and connected and return True in that case :return: bool ''' return self._is_connected
class GalvoScan(Script, QThread): updateProgress = Signal(int) _DEFAULT_SETTINGS = Parameter([ Parameter('path', 'C:\\Users\\Experiment\\Desktop\\tmp_data', str, 'path to folder where data is saved'), Parameter('tag', 'some_name'), Parameter('save', True, bool,'check to automatically save data'), Parameter('point_a', (0.0, 0.0), tuple, 'top left corner point of scan region'), Parameter('point_b', (0.1, -0.1), tuple, 'bottom right corner point of scan region'), Parameter('num_points', (120,120), tuple, 'number of points to scan in x,y'), Parameter('time_per_pt', .001, float, 'time in s to measure at each point') Parameter('settle_time', .0002, float, 'wait time between points to allow galvo to settle') ]) _INSTRUMENTS = {'daq': NIDAQ} _SCRIPTS = {} def __init__(self, instruments = None, name = None, settings = None, log_output = None, timeout = 1000000000): self._recording = False self._timeout = timeout self.clockAdjust = int((self.settings['time_per_pt'] + self.settings['settle_time'] ) / self.settings['settle_time'] ) Script.__init__(self, name, settings=settings, instruments=instruments, log_output=log_output) QThread.__init__(self) xVmin = min(self.settings['point_a'][0], self.settings['point_b'][0]) xVmax = max(self.settings['point_a'][0], self.settings['point_b'][0]) yVmin = min(self.settings['point_a'][1], self.settings['point_b'][1]) yVmax = max(self.settings['point_a'][1], self.settings['point_b'][1]) self.x_array = np.repeat(np.linspace(xVmin, xVmax, self.settings['num_points'[0]]), self.clockAdjust) self.y_array = np.linspace(yVmin, yVmax, self.settings['num_points'[1]]) self.dt = (self.settings['time_per_pt']+self.settings['settle_time'])/self.clockAdjust self.image_data = np.zeros((self.settings['num_points'[1]],self.settings['num_points'[0]])) 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.clear() # clear data queue self.log() for yNum in xrange(0, len(self.yArray)): # if (not (queue is None) and not (queue.empty()) and (queue.get() == 'STOP')): # break # initialize APD thread readthread = APDIn.ReadAPD("Dev1/ctr0", 1 / self.dt, len(self.xArray) + 1) self.initPt = np.transpose(np.column_stack((self.xArray[0], self.yArray[yNum]))) self.initPt = (np.repeat(self.initPt, 2, axis=1)) # move galvo to first point in line pointthread = DaqOut.DaqOutputWave(self.initPt, 1 / self.dt, "Dev1/ao0:1") pointthread.run() pointthread.waitToFinish() pointthread.stop() writethread = DaqOut.DaqOutputWave(self.xArray, 1 / self.dt, "Dev1/ao0") # start counter and scanning sequence readthread.runCtr() writethread.run() writethread.waitToFinish() writethread.stop() self.xLineData,_ = readthread.read() self.diffData = np.diff(self.xLineData) self.summedData = np.zeros(len(self.xArray)/self.clockAdjust) for i in range(0,int((len(self.xArray)/self.clockAdjust))): self.summedData[i] = np.sum(self.diffData[(i*self.clockAdjust+1):(i*self.clockAdjust+self.clockAdjust-1)]) #also normalizing to kcounts/sec self.imageData[yNum] = self.summedData*(.001/self.timePerPt) # clean up APD tasks readthread.stopCtr() readthread.stopClk() if(not(self.canvas == None)): self.dispImageGui() # if self.settings['save']: # self.save() def plot(self, axes): raise NotImplementedError
class MaestroFilterWheel(Instrument): _DEFAULT_SETTINGS = Parameter([ Parameter('channel', 0, int, 'channel to which motor is connected'), Parameter('settle_time', 0.8, float, 'settling time'), Parameter('position_1', 4 * 600, int, 'position corresponding to position 1'), Parameter('position_2', 4 * 1550, int, 'position corresponding to position 2'), Parameter('position_3', 4 * 2500, int, 'position corresponding to position 3'), Parameter('current_position', 'position_1', ['position_1', 'position_2', 'position_3'], 'current position of filter wheel') ]) _PROBES = {} def __init__(self, maestro=None, name=None, settings=None): ''' :param maestro: maestro servo controler to which motor is connected :param channel: channel to which motor is connected :param position_list: dictonary that contains the target positions, a factor 4 is needed to get the same values as in the maestro control center :return: ''' if maestro is None: maestro = MaestroController() assert isinstance(maestro, MaestroController) self.maestro = maestro super(MaestroFilterWheel, self).__init__(name, settings) self.update(self.settings) def update(self, settings): # call the update_parameter_list to update the parameter list super(MaestroFilterWheel, self).update(settings) # now we actually apply these newsettings to the hardware for key, value in settings.iteritems(): if key == 'current_position': self.goto(self.settings[value]) def read_probes(self, key): ''' requestes value from the instrument and returns it Args: key: name of requested value Returns: reads values from instrument ''' assert key in self._PROBES.keys() value = None return value @property def is_connected(self): """ check if instrument is active and connected and return True in that case :return: bool """ return self.maestro._is_connected def goto(self, position): self.maestro.set_target(self.settings['channel'], position) sleep(self.settings['settle_time']) self.maestro.disable(self.settings['channel'] ) # diconnect to avoid piezo from going crazy
class NI7845RPidSimpleLoop(Instrument): import src.labview_fpga_lib.pid_loop_simple.pid_loop_simple as FPGAlib _DEFAULT_SETTINGS = Parameter([ Parameter('ElementsToWrite', 500, int, 'total elements to write to buffer'), Parameter('PiezoOut', 0.0, float, 'piezo output in volt'), Parameter('Setpoint', 0.0, float, 'set point for PID loop in volt'), Parameter('SamplePeriodsPID', int(4e5), int, 'sample period of PID loop in ticks (40 MHz)'), Parameter('SamplePeriodsAcq', 200, int, 'sample period of acquisition loop in ticks (40 MHz)'), Parameter('gains', [ Parameter('proportional', 0, int, 'proportional gain of PID loop in ??'), Parameter('integral', 0, int, 'integral gain of PID loop in ??'), ]), Parameter('PI_on', False, bool, 'turn PID loop on/off'), Parameter('Acquire', False, bool, 'data acquisition on/off'), Parameter('fifo_size', 0, int, 'size of fifo for data acquisition'), Parameter('TimeoutBuffer', 0, int, 'time after which buffer times out in clock ticks (40MHz)') ]) _PROBES = { 'AI1': 'analog input channel 1', 'AI1_filtered': 'analog input channel 1', 'AI2': 'analog input channel 2', 'DeviceTemperature': 'device temperature of fpga', 'ElementsWritten': 'elements written to DMA' } def __init__(self, name=None, settings=None): super(NI7845RPidSimpleLoop, self).__init__(name, settings) # start fpga self.fpga = self.FPGAlib.NI7845R() self.fpga.start() self.update(self.settings) def __del__(self): print('stopping fpga NI7845RPidSimpleLoop') self.fpga.stop() def read_probes(self, key): assert key in self._PROBES.keys(), "key assertion failed %s" % str(key) value = getattr(self.FPGAlib, 'read_{:s}'.format(key))(self.fpga.session, self.fpga.status) return value def update(self, settings): super(NI7845RPidSimpleLoop, self).update(settings) for key, value in settings.iteritems(): if key in ['PiezoOut']: if self.settings['PI_on'] == True: print('PI is active, manual piezo control not active!') else: getattr(self.FPGAlib, 'set_{:s}'.format(key))(volt_2_bit(value), self.fpga.session, self.fpga.status) elif key in ['Setpoint']: getattr(self.FPGAlib, 'set_{:s}'.format(key))(volt_2_bit(value), self.fpga.session, self.fpga.status) elif key in ['PI_on']: getattr(self.FPGAlib, 'set_PIDActive')(value, self.fpga.session, self.fpga.status) elif key in ['Acquire']: getattr(self.FPGAlib, 'set_AcquireData')(value, self.fpga.session, self.fpga.status) elif key in ['gains']: if 'proportional' in value: getattr(self.FPGAlib, 'set_PI_gain_prop')(value['proportional'], self.fpga.session, self.fpga.status) if 'integral' in value: getattr(self.FPGAlib, 'set_PI_gain_int')(value['integral'], self.fpga.session, self.fpga.status) elif key in [ 'ElementsToWrite', 'sample_period_PI', 'SamplePeriodsAcq', 'PI_on' ]: getattr(self.FPGAlib, 'set_{:s}'.format(key))(value, self.fpga.session, self.fpga.status) elif key in ['fifo_size']: self.FPGAlib.configure_FIFO_AI(value, self.fpga.session, self.fpga.status) def start_fifo(self): self.FPGAlib.start_FIFO_AI(self.fpga.session, self.fpga.status) def stop_fifo(self): self.FPGAlib.stop_FIFO_AI(self.fpga.session, self.fpga.status) def read_fifo(self, block_size): ''' read a block of data from the FIFO :return: data from channels AI1 and AI2 and the elements remaining in the FIFO ''' fifo_data = self.FPGAlib.read_FIFO_AI(block_size, self.fpga.session, self.fpga.status) if str(self.fpga.status) != 0: raise LabviewFPGAException(self.fpga.status) return fifo_data
def _parameters_default(self): ''' returns the default parameter_list of the instrument :return: ''' parameters_default = Parameter([ Parameter('freq', 1e6, float, 'frequency of output channel'), Parameter('sigins', [ Parameter('channel', 0, [0, 1], 'signal input channel'), Parameter('imp50', 1, [0, 1], '50Ohm impedance on (1) or off (0)'), Parameter('ac', False, bool, 'ac coupling on (1) or off (0)'), Parameter('range', 10, [0.01, 0.1, 1, 10], 'range of signal input'), Parameter('diff', False, bool, 'differential signal on (1) or off (0)') ]), Parameter('sigouts', [ Parameter('channel', 0, [0, 1], 'signal output channel'), Parameter('on', False, bool, 'output on (1) or off (0)'), Parameter('add', False, bool, 'add aux signal on (1) or off (0)'), Parameter('range', 10, [0.01, 0.1, 1, 10], 'range of signal output') ]), Parameter('demods', [ Parameter('channel', 0, [0, 1], 'demodulation channel'), Parameter('order', 4, int, 'filter order'), Parameter('rate', 10e3, [10e3], 'rate'), Parameter('harmonic', 1, int, 'harmonic at which to demodulate'), Parameter('phaseshift', 0.0, float, 'phaseshift of demodulation'), Parameter('oscselect', 0, [0, 1], 'oscillator for demodulation'), Parameter('adcselect', 0, int, 'adcselect') ]), Parameter('aux', [ Parameter('channel', 0, [0, 1], 'auxilary channel'), Parameter('offset', 1.0, float, 'offset in volts') ]) ]) return parameters_default
class MWSpectraVsPower(Script, QThread): # NOTE THAT THE ORDER OF Script and QThread IS IMPORTANT!! _DEFAULT_SETTINGS = Parameter([ Parameter('path', 'Z:/Lab/Cantilever/Measurements/', str, 'path for data'), Parameter('tag', 'dummy_tag', str, 'tag for data'), Parameter('save', True, bool, 'save data on/off'), Parameter('start_frequency', 2.7e9, float, 'start frequency of spectrum'), Parameter('end_frequency', 3e9, float, 'end frequency of spectrum'), Parameter('microwave_frequency', 3e9, float, 'frequency of microwave'), Parameter('uwave_power_min', -45.0, float, 'microwave power min (dBm)'), Parameter('uwave_power_max', -12.0, float, 'microwave power max (dBm)'), Parameter('uwave_power_step', 2.0, float, 'microwave power step (dBm)'), Parameter('wait_time', 2.0, float, 'time to wait after change in power (seconds)') ]) _INSTRUMENTS = { 'microwave_generator': MicrowaveGenerator, 'cryo_station': CryoStation, 'spectrum_analyzer': SpectrumAnalyzer } _SCRIPTS = {} #This is the signal that will be emitted during the processing. #By including int as an argument, it lets the signal know to expect #an integer argument when emitting. updateProgress = Signal(int) def __init__(self, instruments=None, name=None, settings=None, log_output=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, instruments=instruments, log_output=log_output) # QtCore.QThread.__init__(self) QThread.__init__(self) # self.data = 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__ """ def calc_progress(power): min, max = self.settings['uwave_power_min'], self.settings[ 'uwave_power_max'] progress = power - min / (max - min) * 100 return progress # set up instruments # todo: FINISH IMPLEMENTATIUON self.instruments['microwave_generator'].FREQ = self.settings[ 'microwave_frequency'] self.save(save_data=False, save_instrumets=True, save_log=False, save_settings=True) power_values = [ float(power) for power in np.arange(self.settings['uwave_power_min'], self.settings['uwave_power_max'], self.settings['uwave_power_step']) ] stage_1_temp = [] stage_2_temp = [] platform_temp = [] times = [] spectrum = [] uwave_power = [] for power in power_values: # for power in range(self.settings['uwave_power_min'], self.settings['uwave_power_max'], self.settings['uwave_power_step']): # set u-wave power self.instruments['microwave_generator'].AMPR = power time.sleep(self.settings['wait_time'] ) #since the spectrum analyzer takes a full second =) uwave_power.append(power) times.append(time.strftime('%Y_%m_%d_%H_%M_%S')) stage_1_temp.append(self.instruments['cryo_station'].platform_temp) stage_2_temp.append(self.instruments['cryo_station'].stage_1_temp) platform_temp.append(self.instruments['cryo_station'].stage_2_temp) trace = self.instruments['spectrum_analyzer'].trace freq = [item[0] for item in trace] trans = [item[1] for item in trace] spectrum.append(trans) data = { 'stage_1_temp': stage_1_temp, 'stage_2_temp': stage_2_temp, 'platform_temp': platform_temp, 'times': times, 'spectrum': spectrum, 'frequency': freq, 'uwave_power': uwave_power } self.data = data self.save(save_data=True, save_instrumets=False, save_log=False, save_settings=False) progress = calc_progress(power) self.log('current u-wave power : {:0.2f} dBm'.format(power)) self.updateProgress.emit(progress) self.save(save_data=False, save_instrumets=False, save_log=True, save_settings=False) self.instruments['microwave_generator'].AMPR = -60 def plot(self, axes): spectrum = self.data[-1]['spectrum'] freq = self.data[-1]['frequency'] axes.plot(freq, spectrum)
class ZISweeper(Script, QThread): updateProgress = Signal(int) _DEFAULT_SETTINGS = Parameter([ Parameter('path', 'C:\\Users\\Experiment\\Desktop\\tmp_data', str, 'path to folder where data is saved'), Parameter('tag', 'some_name'), Parameter('save', True, bool, 'check to automatically save data'), Parameter('start', 1.8e6, float, 'start value of sweep'), Parameter('stop', 1.9e6, float, 'end value of sweep'), Parameter('samplecount', 101, int, 'number of data points'), Parameter('gridnode', 'oscs/0/freq', ['oscs/0/freq', 'oscs/1/freq'], 'output channel =not 100% sure, double check='), Parameter('xmapping', 0, [0, 1], 'mapping 0 = linear, 1 = logarithmic'), Parameter('bandwidthcontrol', 2, [2], '2 = automatic bandwidth control'), Parameter( 'scan', 0, [0, 1, 2], 'scan direction 0 = sequential, 1 = binary (non-sequential, each point once), 2 = bidirecctional (forward then reverse)' ), Parameter('loopcount', 1, int, 'number of times it sweeps'), Parameter('averaging/sample', 1, int, 'number of samples to average over') ]) _INSTRUMENTS = {'zihf2': ZIHF2} _SCRIPTS = {} def __init__(self, instruments, name=None, settings=None, log_output=None, timeout=1000000000): self._recording = False self._timeout = timeout Script.__init__(self, name, settings, instruments, log_output=log_output) QThread.__init__(self) self.sweeper = self.instruments['zihf2'].daq.sweep(self._timeout) self.sweeper.set('sweep/device', self.instruments['zihf2'].device) self.data = deque() # todo: clean this up! and plot data in gui! self._sweep_values = { 'frequency': [], 'x': [], 'y': [], 'phase': [], 'r': [] }.keys() def settings_to_commands(self, settings): ''' converts dictionary to list of setting, which can then be passed to the zi controler :param dictionary = dictionary that contains the commands :return: commands = list of commands, which can then be passed to the zi controler ''' # create list that is passed to the ZI controler commands = [] for key, val in settings.iteritems(): if isinstance(val, dict) and 'value' in val: commands.append(['sweep/%s' % (key), val['value']]) elif key in ('start', 'stop', 'samplecount', 'gridnode', 'xmapping', 'bandwidthcontrol', 'scan', 'loopcount', 'averaging/sample'): commands.append(['sweep/%s' % (key), val]) return commands 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.clear() # clear data queue commands = self.settings_to_commands(self.settings) self.sweeper.set(commands) path = '/%s/demods/%d/sample' % ( self.instruments['zihf2'].device, self.instruments['zihf2'].settings['demods']['channel']) self.sweeper.subscribe(path) self.sweeper.execute() while not self.sweeper.finished(): time.sleep(1) progress = int(100 * self.sweeper.progress()) print('progress', progress) data = self.sweeper.read(True) # True: flattened dictionary # ensures that first point has completed before attempting to read data if path not in data: continue data = data[path][0][ 0] # the data is nested, we remove the outer brackets with [0][0] # now we only want a subset of the data porvided by ZI data = {k: data[k] for k in self._sweep_values} start = time.time() self.data.append(data) if (time.time() - start) > self._timeout: # If for some reason the sweep is blocking, force the end of the # measurement print("\nSweep still not finished, forcing finish...") self.sweeper.finish() self._recording = False print("Individual sweep %.2f%% complete. \n" % (progress)) self.updateProgress.emit(progress) print('len data: ', len(self.data)) if self.sweeper.finished(): self._recording = False progress = 100 # make sure that progess is set 1o 100 because we check that in the old_gui if self.settings['save']: self.save() def plot(self, axes): r = self.data[-1]['r'] freq = self.data[-1]['frequency'] freq = freq[np.isfinite(r)] r = r[np.isfinite(r)] plotting.plot_psd(freq, r, axes)
class ZISweeperHighResolution(Script, QThread): updateProgress = Signal(int) _DEFAULT_SETTINGS = Parameter([ Parameter('path', 'C:\\Users\\Experiment\\Desktop\\tmp_data\\fast', str, 'path to folder where data is saved'), Parameter('tag', 'some_name'), Parameter('save', True, bool, 'check to automatically save data'), Parameter('high_res_df', 1000, float, 'frequency step of high res. scan'), Parameter('high_res_N', 21, int, 'number of data points of high res. scan'), ]) _INSTRUMENTS = {} _SCRIPTS = {'zi sweep': ZISweeper} def __init__(self, scripts, name=None, settings=None, log_output=None, timeout=1000000000): self._recording = False self._timeout = timeout Script.__init__(self, name, settings, scripts=scripts, log_output=log_output) QThread.__init__(self) self.data = deque() # todo: clean this up! and plot data in gui! self._sweep_values = { 'frequency': [], 'x': [], 'y': [], 'phase': [], 'r': [] }.keys() def _receive_signal(self, progess_sub_script): # calculate progress of this script based on progress in subscript if self.current_subscript == 'quick scan': progress = int(self.weights['quick scan'] * progess_sub_script) elif self.current_subscript == 'high res scan': progress = int(self.weights['quick scan'] * 100 + self.weights['high res scan'] * progess_sub_script) else: progress = None # if calculated progress is 100 force it to 99, because we still have to save before script is finished if progress >= 100: progress = 99 if progress is not None: self.updateProgress.emit(progress) if progess_sub_script == 100: self.current_subscript = None 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__ """ def calculate_weights(): """ calculate a weight inversely proportional to the expected to duration of the two steps in the script Returns: weights as a dictionary for the two steps """ weights = {} # estimate run time of step 1 (fast sweep) f_range = sweeper_script.settings[ 'stop'] - sweeper_script.settings['start'] N_samples = sweeper_script.settings['samplecount'] df = f_range / N_samples t = N_samples / df weights['quick scan'] = t # estimate run time of step 2 (high res sweep) df = self.settings['high_res_df'] N_samples = self.settings['high_res_N'] t = N_samples / df weights['high res scan'] = t total_time = sum([v for k, v in weights.iteritems()]) weights = {k: v / total_time for k, v in weights.iteritems()} print('weights', weights) return weights def run_scan(name): self.current_subscript = name sweeper_script.start() while self.current_subscript is name: time.sleep(0.1) def calc_new_range(): df = self.settings['high_res_df'] N = self.settings['high_res_N'] r = sweeper_script.data[-1]['r'] freq = sweeper_script.data[-1]['frequency'] freq = freq[np.isfinite(r)] r = r[np.isfinite(r)] fo = freq[np.argmax(r)] f_start, f_end = fo - N / 2 * df, fo + N / 2 * df # make sure that we convert back to native python types (numpy file types don't pass the Parameter validation) return float(f_start), float(f_end), int(N) sweeper_script = self.scripts['zi sweep'] #save initial settings, so that we can rest at the end of the script initial_settings = deepcopy(sweeper_script.settings) self.weights = calculate_weights() # take the signal from the subscript and route it to a function that takes care of it sweeper_script.updateProgress.connect(self._receive_signal) print('====== start quick scan ============') run_scan('quick scan') print('====== calculate new scan range ====') f_start, f_stop, N = calc_new_range() print('f_start, f_stop, N', f_start, f_stop, N) print('====== update sweeper ==============') sweeper_script.update({ 'start': f_start, 'stop': f_stop, 'samplecount': N }) print('====== start high res scan =========') # print(sweeper_script.sweeper.finished()) # print(sweeper_script.sweeper.progress()) run_scan('high res scan') sweeper_script.updateProgress.disconnect() self.data = sweeper_script.data[-1] self._recording = False if self.settings['save']: self.save() # set the sweeper script back to initial settings sweeper_script.update(initial_settings) # make sure that progess is set 1o 100 because we check that in the old_gui self.updateProgress.emit(100) def plot(self, axes): if self.current_subscript == 'quick scan' and self.scripts[ 'zi sweep'].data: self.scripts['zi sweep'].plot(axes) elif self.current_subscript in ('high res scan', None) and self.data: r = self.data['r'] freq = self.data['frequency'] freq = freq[np.isfinite(r)] r = r[np.isfinite(r)] plotting.plot_psd(freq, r, axes, False)
class KeysightGetSpectrum(Script): # NOTE THAT THE ORDER OF Script and QThread IS IMPORTANT!! _DEFAULT_SETTINGS = Parameter([ Parameter('path', 'Z:/Lab/Cantilever/Measurements/----data_tmp_default----', str, 'path for data'), Parameter('tag', 'dummy_tag', str, 'tag for data'), Parameter('save', True, bool, 'save data on/off'), Parameter('start_frequency', 2.7e9, float, 'start frequency of spectrum'), Parameter('stop_frequency', 3e9, float, 'end frequency of spectrum'), Parameter('output_power', 0.0, float, 'output power (dBm)'), Parameter('output_on', True, bool, 'enable output'), ]) _INSTRUMENTS = {'spectrum_analyzer': SpectrumAnalyzer} _SCRIPTS = {} def __init__(self, instruments=None, name=None, settings=None, log_output=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, instruments=instruments, log_output=log_output) 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__ """ def setup_instrument(): print('self.settings', self.settings) inst = self.instruments['spectrum_analyzer'] if inst.settings['start_frequency'] != self.settings[ 'start_frequency']: inst.start_frequency = self.settings['start_frequency'] if inst.settings['stop_frequency'] != self.settings[ 'stop_frequency']: inst.stop_frequency = self.settings['stop_frequency'] if self.settings['output_on']: if inst.settings['mode'] != 'TrackingGenerator': inst.mode = 'TrackingGenerator' if inst.settings['output_power'] != self.settings[ 'output_power']: inst.output_power = self.settings['output_power'] if inst.settings['output_on'] != self.settings['output_on']: inst.output_on = self.settings['output_on'] setup_instrument() trace = self.instruments['spectrum_analyzer'].trace self.data = { 'spectrum': [item[1] for item in trace], 'frequency': [item[0] for item in trace] } if self.settings['save']: self.save() def plot(self, axes): spectrum = self.data['spectrum'] freq = self.data['frequency'] axes.plot(freq, spectrum)
class MaestroController(Instrument): # When connected via USB, the Maestro creates two virtual serial ports # /dev/ttyACM0 for commands and /dev/ttyACM1 for communications. # Be sure the Maestro is configured for "USB Dual Port" serial mode. # "USB Chained Mode" may work as well, but hasn't been tested. # # Pololu protocol allows for multiple Maestros to be connected to a single # communication channel. Each connected device is then indexed by number. # This device number defaults to 0x0C (or 12 in decimal), which this module # assumes. If two or more controllers are connected to different serial # ports, then you can specify the port number when intiating a controller # object. Ports will typically start at 0 and count by twos. So with two # controllers ports 0 and 2 would be used. import serial _DEFAULT_SETTINGS = Parameter([ Parameter('port', 'COM5', ['COM5', 'COM3'], 'com port to which maestro controler is connected') ]) _PROBES = {} def __init__(self, name=None, settings=None): self.usb = None super(MaestroController, self).__init__(name, settings) self.update(self.settings) # Open the command port # self.usb = self.serial.Serial(port) # Command lead-in and device 12 are sent for each Pololu serial commands. self.PololuCmd = chr(0xaa) + chr(0xc) # Track target position for each servo. The function isMoving() will # use the Target vs Current servo position to determine if movement is # occuring. Upto 24 servos on a Maestro, (0-23). Targets start at 0. self.Targets = [0] * 24 # Servo minimum and maximum targets can be restricted to protect components. self.Mins = [0] * 24 self.Maxs = [0] * 24 def update(self, settings): # call the update_parameter_list to update the parameter list super(MaestroController, self).update(settings) # now we actually apply these newsettings to the hardware for key, value in settings.iteritems(): if key == 'port': try: self.usb = self.serial.Serial(value) except OSError: print('Couln\'t connect to maestro controler at port {:s}'. format(value)) def read_probes(self, key): ''' requestes value from the instrument and returns it Args: key: name of requested value Returns: reads values from instrument ''' # todo: replace getter old_functions with this function assert key in self._PROBES.keys() value = None return value @property def is_connected(self): ''' check if instrument is active and connected and return True in that case :return: bool ''' if self.usb is None: self._is_connected = False else: self._is_connected = True #todo: implement check return self._is_connected # Cleanup by closing USB serial port def __del__(self): if not self.usb == None: self.usb.close() # Set channels min and max value range. Use this as a safety to protect # from accidentally moving outside known safe parameters. A setting of 0 # allows unrestricted movement. # # ***Note that the Maestro itself is configured to limit the range of servo travel # which has precedence over these values. Use the Maestro Control Center to configure # ranges that are saved to the controller. Use setRange for software controllable ranges. def set_range(self, chan, min, max): self.Mins[chan] = min self.Maxs[chan] = max # Return Minimum channel range value def get_min(self, chan): return self.Mins[chan] # Return Minimum channel range value def get_max(self, chan): return self.Maxs[chan] # Set channel to a specified target value. Servo will begin moving based # on Speed and Acceleration parameters previously set. # Target values will be constrained within Min and Max range, if set. # For servos, target represents the pulse width in of quarter-microseconds # Servo center is at 1500 microseconds, or 6000 quarter-microseconds # Typcially valid servo range is 3000 to 9000 quarter-microseconds # If channel is configured for digital output, values < 6000 = Low ouput def set_target(self, chan, target): # if Min is defined and Target is below, force to Min if self.Mins[chan] > 0 and target < self.Mins[chan]: target = self.Mins[chan] # if Max is defined and Target is above, force to Max if self.Maxs[chan] > 0 and target > self.Maxs[chan]: target = self.Maxs[chan] # lsb = target & 0x7f #7 bits for least significant byte msb = (target >> 7) & 0x7f #shift 7 and take next 7 bits for msb # Send Pololu intro, device number, command, channel, and target lsb/msb cmd = self.PololuCmd + chr(0x04) + chr(chan) + chr(lsb) + chr(msb) self.usb.write(cmd) # Record Target value self.Targets[chan] = target def disable(self, chan): target = 0 # lsb = target & 0x7f #7 bits for least significant byte msb = (target >> 7) & 0x7f #shift 7 and take next 7 bits for msb # Send Pololu intro, device number, command, channel, and target lsb/msb cmd = self.PololuCmd + chr(0x04) + chr(chan) + chr(lsb) + chr(msb) self.usb.write(cmd) # Record Target value self.Targets[chan] = target # Set speed of channel # Speed is measured as 0.25microseconds/10milliseconds # For the standard 1ms pulse width change to move a servo between extremes, a speed # of 1 will take 1 minute, and a speed of 60 would take 1 second. # Speed of 0 is unrestricted. def set_speed(self, chan, speed): lsb = speed & 0x7f #7 bits for least significant byte msb = (speed >> 7) & 0x7f #shift 7 and take next 7 bits for msb # Send Pololu intro, device number, command, channel, speed lsb, speed msb cmd = self.PololuCmd + chr(0x07) + chr(chan) + chr(lsb) + chr(msb) self.usb.write(cmd) # Set acceleration of channel # This provide soft starts and finishes when servo moves to target position. # Valid values are from 0 to 255. 0=unrestricted, 1 is slowest start. # A value of 1 will take the servo about 3s to move between 1ms to 2ms range. def set_accel(self, chan, accel): lsb = accel & 0x7f #7 bits for least significant byte msb = (accel >> 7) & 0x7f #shift 7 and take next 7 bits for msb # Send Pololu intro, device number, command, channel, accel lsb, accel msb cmd = self.PololuCmd + chr(0x09) + chr(chan) + chr(lsb) + chr(msb) self.usb.write(cmd) # Get the current position of the device on the specified channel # The result is returned in a measure of quarter-microseconds, which mirrors # the Target parameter of setTarget. # This is not reading the true servo position, but the last target position sent # to the servo. If the Speed is set to below the top speed of the servo, then # the position result will align well with the acutal servo position, assuming # it is not stalled or slowed. def get_position(self, chan): cmd = self.PololuCmd + chr(0x10) + chr(chan) self.usb.write(cmd) lsb = ord(self.usb.read()) msb = ord(self.usb.read()) return (msb << 8) + lsb # # Test to see if a servo has reached its target position. This only provides # # useful results if the Speed parameter is set slower than the maximum speed of # # the servo. # # ***Note if target position goes outside of Maestro's allowable range for the # # channel, then the target can never be reached, so it will appear to allows be # # moving to the target. See setRange comment. # def isMoving(self, chan): # if self.Targets[chan] > 0: # if self.getPosition(chan) <> self.Targets[chan]: # return True # return False # # Have all servo outputs reached their targets? This is useful only if Speed and/or # # Acceleration have been set on one or more of the channels. Returns True or False. # def getMovingState(self): # cmd = self.PololuCmd + chr(0x13) # self.usb.write(cmd) # if self.usb.read() == chr(0): # return False # else: # return True # # Run a Maestro Script subroutine in the currently active script. Scripts can # # have multiple subroutines, which get numbered sequentially from 0 on up. Code your # # Maestro subroutine to either infinitely loop, or just end (return is not valid). # def runScriptSub(self, subNumber): # cmd = self.PololuCmd + chr(0x27) + chr(subNumber) # # can pass a param with comman 0x28 # # cmd = self.PololuCmd + chr(0x28) + chr(subNumber) + chr(lsb) + chr(msb) # self.usb.write(cmd) # # # Stop the current Maestro Script # def stopScript(self): # cmd = self.PololuCmd + chr(0x24) # self.usb.write(cmd) # Stop the current Maestro Script def go_home(self): cmd = self.PololuCmd + chr(0x22) self.usb.write(cmd)
class CryoStation(Instrument): _DEFAULT_SETTINGS = Parameter( Parameter('path', 'C:/Cryostation/Temperature Data/', str, 'path to log file of cryostation'), ) ''' instrument class to talk to get infos from Montana Cryostation Now this doesn't actually communicate with the Cryostation but only reads data from a log-file ''' def __init__(self, name=None, settings=None): super(CryoStation, self).__init__(name, settings) # apply all settings to instrument self.update(self.settings) try: # create available probes dynamically from headers of logfile filepath = "{:s}/MI_DiagnosticsDataLog {:s}.csv".format( self.settings['path'], time.strftime('%m_%d_%Y')) data = pd.read_csv(filepath) self._dynamic_probes = { elem.lstrip().lower().replace(' ', '_').replace( '.', '').replace(')', '').replace('(', '').replace('/', '-'): elem for elem in data.columns } self._is_connected = True except IOError: self._is_connected = False except: raise def update(self, settings): ''' updates the internal dictionary, just call function of super class ''' super(CryoStation, self).update(settings) @property def is_connected(self): ''' check if instrument is active and connected and return True in that case :return: bool ''' return self._is_connected @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 ''' user_specific_probes = { 'Platform_Temp': 'temperature of platform', 'Stage_1_Temp': 'temperature of stage 1', 'Stage_2_Temp': 'temperature of stage 2' } user_specific_probes.update(self._dynamic_probes) return user_specific_probes 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 self._PROBES.keys(), "key assertion failed {:s}".format( str(key)) # catch change of date time_tag = datetime.datetime.now() filepath = "{:s}/MI_DiagnosticsDataLog {:s}.csv".format( self.settings['path'], time_tag.strftime('%m_%d_%Y')) while os.path.exists(filepath) == False: time_tag -= datetime.timedelta(hours=1) filepath = "{:s}/MI_DiagnosticsDataLog {:s}.csv".format( self.settings['path'], time_tag.strftime('%m_%d_%Y')) data = pd.read_csv(filepath) # create dictionary with last row as values data = dict(data.iloc[-1]) # since we striped some characters when defining the probes we have to find the right key, # which is give by the valeu in self._dynamic_probes key = self._dynamic_probes[key] return data[key]
class LabviewFpgaTimetrace(Script, QThread): updateProgress = Signal(int) _DEFAULT_SETTINGS = Parameter([ Parameter('path', 'C:\\Users\\Experiment\\Desktop\\tmp_data', str, 'path to folder where data is saved'), Parameter('tag', 'some_name'), Parameter('save', True, bool,'check to automatically save data'), Parameter('dt', 200, int, 'sample period of acquisition loop in ticks (40 MHz)'), Parameter('N', 2000, int, 'numer of samples'), Parameter('TimeoutBuffer', 0, int, 'time after which buffer times out in clock ticks (40MHz)'), Parameter('BlockSize', 1000, int, 'block size of chunks that are read from FPGA'), ]) _INSTRUMENTS = {'fpga' : NI7845RPidSimpleLoop} _SCRIPTS = {} def __init__(self, instruments, name = None, settings = None, log_output = None): self._recording = False Script.__init__(self, name, settings, instruments, log_output = log_output) QThread.__init__(self) self.data = 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__ """ def calculate_progress(loop_index): progress = int(100.0 * loop_index / number_of_reads) self._recording = True self.data.clear() # clear data queue # reset FIFO block_size = self.settings['BlockSize'] self.instruments['fpga'].update({'fifo_size' :block_size * 2}) time.sleep(0.1) self.instruments['fpga'].start_fifo() time.sleep(0.1) number_of_reads = int(np.ceil(1.0 * self.settings['N'] / self.settings['BlockSize'])) print('number_of_reads', number_of_reads) N_actual = number_of_reads * block_size if N_actual!=self.settings['N']: self.log('warning blocksize not comensurate with number of datapoints, set N = {:d}'.format(N_actual)) self.settings.update({'N' : N_actual}) time.sleep(0.1) # apply settings to instrument instr_settings = { 'SamplePeriodsAcq' : self.settings['dt'], 'ElementsToWrite' : self.settings['N'], 'TimeoutBuffer' : self.settings['TimeoutBuffer'] } self.instruments['fpga'].update(instr_settings) time.sleep(0.1) self.instruments['fpga'].update({'Acquire' : True}) time.sleep(1) print('ElementsWritten: ', self.instruments['fpga'].ElementsWritten) ai1 = np.zeros(N_actual) for i in range(number_of_reads): data = self.instruments['fpga'].read_fifo(block_size) print(i, 'ddd', data) print('block_size', block_size) ai1[i* block_size:(i+1)*block_size] = deepcopy(data['AI1']) # append data to queue self.data.append({ 'AI1' : ai1 }) progress = calculate_progress(i) self.updateProgress.emit(progress) self._recording = False progress = 100 # make sure that progess is set 1o 100 because we check that in the old_gui if self.settings['save']: self.save() def plot(self, axes): r = self.data[-1]['AI1'] axes.plot(r)