From 8fe801bae343a7d75b5c37dbdcf1480acd4b78c3 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Wed, 25 Jan 2017 11:40:02 +0100 Subject: [PATCH 01/16] feat: Add a driver for ZI UHF-LI First commit providing a driver with a working sweeper. Needs refactorising the sweeper into a MultiParameter --- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 696 +++++++++++++++++++++++ qcodes/instrument_drivers/ZI/__init__.py | 1 + 2 files changed, 697 insertions(+) create mode 100644 qcodes/instrument_drivers/ZI/ZIUHFLI.py create mode 100644 qcodes/instrument_drivers/ZI/__init__.py diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py new file mode 100644 index 00000000000..11b803ea993 --- /dev/null +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -0,0 +1,696 @@ +import time +import logging +import zhinst.utils + +import numpy as np +import zhinst.ziPython as zipy + +from functools import partial +from qcodes.instrument.parameter import Parameter, ManualParameter +from qcodes.instrument.base import Instrument +from qcodes.utils import validators as vals + +log = logging.getLogger(__name__) + + +class Sweep(Parameter): + """ + Parameter class for the ZIUHFLI instrument class for the sweeper. + + The get method returns a tuple of arrays, where each array contains the + values of a signal added to the sweep (e.g. demodulator 4 phase). + """ + pass + + +class ZIUHFLI(Instrument): + """ + QCoDeS driver for ZI UHF LI + """ + + def __init__(self, name, device_ID, api_level=5): + + super().__init__(name) + + (self.daq, self.device, self.props) = zhinst.utils.create_api_session(device_ID, + api_level) + + # @William: + # the sweeper has to be declared here, otherwise I do not know how to bind to the + # right getters and setters + + self.sweeper = self.daq.sweep() + self.sweeper.set('sweep/device', self.device) + + ######################################## + # INSTRUMENT PARAMETERS + + # Oscillators + self.add_parameter('oscillator1_freq', + label='Frequency of oscillator 1', + unit='Hz', + set_cmd=partial(self.daq.setDouble, + '/' + device_ID + '/oscs/0/freq'), + get_cmd=partial(self.daq.getDouble, + '/' + device_ID + '/oscs/0/freq'), + vals=vals.Numbers(0, 600e6)) + + self.add_parameter('oscillator2_freq', + label='Frequency of oscillator 2', + unit='Hz', + set_cmd=partial(self.daq.setDouble, + '/' + device_ID + '/oscs/1/freq'), + get_cmd=partial(self.daq.getDouble, + '/' + device_ID + '/oscs/1/freq'), + vals=vals.Numbers(0, 600e6)) + + ######################################## + # DEMODULATOR PARAMETERS + + for demod in range(1, 9): + self.add_parameter('demod{}_order'.format(demod), + label='Filter order', + get_cmd=partial(self._demod_getter, + demod-1, 'order'), + set_cmd=partial(self._demod_setter, + 0, demod-1, 'order'), + vals=vals.Ints(1, 8) + ) + + self.add_parameter('demod{}_harmonic'.format(demod), + label=('Reference frequency multiplication' + + ' factor'), + get_cmd=partial(self._demod_getter, + demod-1, 'harmonic'), + set_cmd=partial(self._demod_setter, + 1, demod-1, 'harmonic'), + vals=vals.Ints(1, 999) + ) + + self.add_parameter('demod{}_timeconstant'.format(demod), + label='Filter time constant', + get_cmd=partial(self._demod_getter, + demod-1, 'timeconstant'), + set_cmd=partial(self._demod_setter, + 1, demod-1, 'timeconstant'), + unit='s' + ) + + self.add_parameter('demod{}_samplerate'.format(demod), + label='Sample rate', + get_cmd=partial(self._demod_getter, + demod-1, 'rate'), + set_cmd=partial(self._demod_setter, + 1, demod-1, 'rate'), + unit='Sa/s', + docstring=""" + Note: the value inserted by the user + may be approximated to the + nearest value supported by the + instrument. + """) + + + ######################################## + # SWEEPER PARAMETERS + + self.add_parameter('sweeper_BWmode', + label='Sweeper bandwidth control mode', + set_cmd=partial(self.sweeper.set, + 'sweep/bandwidthcontrol'), + get_cmd=partial(self._sweep_getter, + 'sweep/bandwidthcontrol'), + val_mapping={'auto': 2, 'fixed': 1, 'current': 0}, + docstring=""" + For each sweep point, the demodulator + filter bandwidth (time constant) may + be either set automatically, be the + current demodulator bandwidth or be + a fixed number; the sweeper_BW + parameter. + """ + ) + + self.add_parameter('sweeper_BW', + label='Fixed bandwidth sweeper bandwidth (NEP)', + set_cmd=partial(self.sweeper.set, + 'sweep/bandwidth'), + get_cmd=partial(self._sweep_getter, + 'sweep/bandwidth'), + docstring=""" + This is the NEP bandwidth used by the + sweeper if sweeper_BWmode is set to + 'fixed'. If sweeper_BWmode is either + 'auto' or 'current', this value is + ignored. + """ + ) + + self.add_parameter('sweeper_start', + label='Start value of the sweep', + set_cmd=partial(self.sweeper.set, + 'sweep/start'), + get_cmd=partial(self._sweep_getter, + 'sweep/start'), + vals=vals.Numbers(0, 600e6)) + + self.add_parameter('sweeper_stop', + label='Stop value of the sweep', + set_cmd=partial(self.sweeper.set, + 'sweep/stop'), + get_cmd=partial(self._sweep_getter, + 'sweep/stop'), + vals=vals.Numbers(0, 600e6)) + + self.add_parameter('sweeper_samplecount', + label='Length of the sweep (pts)', + set_cmd=partial(self.sweeper.set, + 'sweep/samplecount'), + get_cmd=partial(self._sweep_getter, + 'sweep/samplecount'), + vals=vals.Ints(0, 100000)) + + # helper dict used by sweeper_param parameter + sweepparams = {'Aux Out 1 Offset': 'auxouts/0/offset', + 'Aux Out 2 Offset': 'auxouts/1/offset', + 'Aux Out 3 Offset': 'auxouts/2/offset', + 'Aux Out 4 Offset': 'auxouts/3/offset', + 'Demod 1 Phase Shift': 'demods/0/phaseshift', + 'Demod 2 Phase Shift': 'demods/1/phaseshift', + 'Demod 3 Phase Shift': 'demods/2/phaseshift', + 'Demod 4 Phase Shift': 'demods/3/phaseshift', + 'Demod 5 Phase Shift': 'demods/4/phaseshift', + 'Demod 6 Phase Shift': 'demods/5/phaseshift', + 'Demod 7 Phase Shift': 'demods/6/phaseshift', + 'Demod 8 Phase Shift': 'demods/7/phaseshift', + 'Osc 1 Frequency': 'oscs/0/freq', + 'Osc 2 Frequency': 'oscs/1/freq', + 'Output 1 Amplitude 4': 'sigouts/0/amplitudes/3', + 'Output 1 Offset': 'sigouts/0/offset', + 'Output 2 Amplitude 8': 'sigouts/1/amplitudes/7', + 'Output 2 Offset': 'sigouts/1/offset' + } + + self.add_parameter('sweeper_param', + label='Parameter to sweep (sweep x-axis)', + set_cmd=partial(self.sweeper.set, + 'sweep/gridnode'), + val_mapping=sweepparams, + get_cmd=partial(self._sweep_getter, + 'sweep/gridnode'), + vals=vals.Enum(*list(sweepparams.keys())) + ) + + sweepunits = {'Aux Out 1 Offset': 'V', + 'Aux Out 2 Offset': 'V', + 'Aux Out 3 Offset': 'V', + 'Aux Out 4 Offset': 'V', + 'Demod 1 Phase Shift': 'degrees', + 'Demod 2 Phase Shift': 'degrees', + 'Demod 3 Phase Shift': 'degrees', + 'Demod 4 Phase Shift': 'degrees', + 'Demod 5 Phase Shift': 'degrees', + 'Demod 6 Phase Shift': 'degrees', + 'Demod 7 Phase Shift': 'degrees', + 'Demod 8 Phase Shift': 'degrees', + 'Osc 1 Frequency': 'Hz', + 'Osc 2 Frequency': 'Hz', + 'Output 1 Amplitude 4': 'V', + 'Output 1 Offset': 'V', + 'Output 2 Amplitude 8': 'V', + 'Output 2 Offset': 'V' + } + + self.add_parameter('sweeper_units', + label='Units of sweep x-axis', + get_cmd=self.sweeper_param.get, + get_parser=lambda x:sweepunits[x]) + + # helper dict used by sweeper_mode parameter + sweepmodes = {'Sequential': 0, + 'Binary': 1, + 'Biderectional': 2, + 'Reverse': 3} + + self.add_parameter('sweeper_mode', + label='Sweep mode', + set_cmd=partial(self.sweeper.set, + 'sweep/scan'), + get_cmd=partial(self._sweep_getter, 'sweep/scan'), + val_mapping=sweepmodes, + vals=vals.Enum(*list(sweepmodes)) + ) + + self.add_parameter('sweeper_order', + label='Sweeper filter order', + set_cmd=partial(self.sweeper.set, + 'sweep/order'), + get_cmd=partial(self._sweep_getter, + 'sweep/order'), + vals=vals.Ints(1, 8), + docstring=""" + This value is invoked only when the + sweeper_BWmode is set to 'fixed'. + """) + + self.add_parameter('sweeper_settlingtime', + label=('Minimal settling time for the ' + + 'sweeper'), + set_cmd=partial(self.sweeper.set, + 'sweep/settling/time'), + get_cmd=partial(self._sweep_getter, + 'sweep/settling/time'), + vals=vals.Numbers(0), + unit='s', + docstring=""" + This is the minimal waiting time + at each point during a sweep before the + data acquisition starts. Note that the + filter settings may result in a longer + actual waiting/settling time. + """ + ) + + self.add_parameter('sweeper_settlingtc', + label='Sweep filter settling time', + get_cmd=partial(self._sweep_getter, + 'sweep/settling/tc'), + unit='dim. less.', + docstring="""This settling time is in units of + the filter time constant.""" + ) + + self.add_parameter('sweeper_averaging_samples', + label=('Minimal no. of samples to average at ' + + 'each sweep point'), + set_cmd=partial(self.sweeper.set, + 'sweep/averaging/sample'), + get_cmd=partial(self._sweep_getter, + 'sweep/averaging/sample'), + vals=vals.Ints(1), + docstring="""The actual number of samples is the + maximum of this value and the + sweeper_averaging_time times the + relevant sample rate.""" + ) + + self.add_parameter('sweeper_averaging_time', + label=('Minimal averaging time'), + set_cmd=partial(self.sweeper.set, + 'sweep/averaging/tc'), + get_cmd=partial(self._sweep_getter, + 'sweep/averaging/tc'), + unit='s', + docstring="""The actual number of samples is the + maximum of this value times the + relevant sample rate and the + sweeper_averaging_samples.""" + ) + + self.add_parameter('sweeper_sweeptime', + label='Expected sweep time', + unit='s', + get_cmd=self._get_sweep_time) + + self.add_parameter('sweeper_timeout', + label='Sweep timeout', + unit='s', + initial_value=10, + parameter_class=ManualParameter) + + # A "manual" parameter: a list of the signals for the sweeper + # to subscribe to + self._sweeper_signals = [] + + # Set up the sweeper with a minimal number of required settings + # (since some of the factory defaults are nonsensical) + self.sweeper_BW.set(100) # anything >0 will do + self.sweeper_order.set(1) + + def _demod_setter(self, mode, demod, setting, value): + """ + General set_cmd for demodulator parameters + + This function counts demodulators in a zero-indexed way. + + Args: + mode (int): 0 means 'call setInt', 1 means 'call setDouble' + demod (int): The demodulator in question (0-8) + setting (str): The attribute to set, e.g. 'order' + value (Union[int, float]): The value to set the attribute to + """ + setstr = '/{}/demods/{}/{}'.format(self.device, demod, setting) + + if mode == 0: + self.daq.setInt(setstr, value) + if mode == 1: + self.daq.setDouble(setstr, value) + + + def _demod_getter(self, demod, setting): + """ + General get_cmd for demodulator parameters + + The built-in self.daq.get commands returns a dictionary, but we + want a single value + + This function counts demodulators in a zero-indexed way. + """ + querystr = '/{}/demods/{}/{}'.format(self.device, demod, setting) + returndict = self.daq.get(querystr) + demod = str(demod) + rawvalue = returndict[self.device]['demods'][demod][setting]['value'] + + if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: + value = rawvalue[0] + elif isinstance(rawvalue, list) and len(rawvalue) == 1: + value = rawvalue[0] + else: + value = rawvalue + + return value + + @staticmethod + def NEPBW_to_timeconstant(NEPBW, order): + """ + Helper function to translate a NEP BW and a filter order + to a filter time constant. Meant to be used when calculating + sweeper sweep times. + + Note: precise only to within a few percent. + + Args: + NEPBW (float): The NEP bandwidth in Hz + order (int): The filter order + + Returns: + float: The filter time constant in s. + """ + const = {1: 0.249, 2: 0.124, 3: 0.093, 4: 0.078, 5: 0.068, + 6: 0.061, 7: 0.056, 8: 0.052} + tau_c = const[order]/NEPBW + + return tau_c + + def _get_sweep_time(self): + """ + get_cmd for the sweeper_sweeptime parameter. + + Note: this calculation is only an estimate and not precise to more + than a few percent. + + Returns: + Union[float, None]: None if the bandwidthcontrol setting is + 'auto' (then all bets are off), otherwise a time in seconds. + + Raises: + ValueError: if no signals are added to the sweep + """ + + # Possible TO-DO: cut down on the number of instrument + # queries. + + if self._sweeper_signals == []: + raise ValueError('No signals selected! Can not find sweep time.') + + mode = self.sweeper_BWmode.get() + + # The effective time constant of the demodulator depends on the + # sweeper/bandwidthcontrol setting. + # + # If this setting is 'current', the largest current + # time constant of the involved demodulators is used + # + # If the setting is 'fixed', the NEP BW specified under + # sweep/bandwidth is used. The filter order is needed to convert + # the NEP BW to a time constant + + demods = set([sig.split('/')[3] for sig in self._sweeper_signals]) + rates = [] + for demod in demods: + rates.append(self._demod_getter(demod, 'rate')) + rate = min(rates) + + if mode == 'current': + tcs = [] + for demod in demods: + tcs.append(self._demod_getter(demod, 'timeconstant')) + + tau_c = max(tcs) + + elif mode == 'fixed': + order = self.sweeper_order() + BW = self.sweeper_BW() + + tau_c = self.NEPBW_to_timeconstant(BW, order) + + elif mode == 'auto': + return None + + settlingtime = max(self.sweeper_settlingtc.get()*tau_c, + self.sweeper_settlingtime.get()) + averagingtime = max(self.sweeper_averaging_time.get()*tau_c*rate, + self.sweeper_averaging_samples.get())/rate + + time_est = (settlingtime+averagingtime)*self.sweeper_samplecount.get() + return time_est + + def _sweep_getter(self, setting): + """ + General get_cmd for sweeper parameters + + The built-in sweeper.get command returns a dictionary, but we want + single values. + + Args: + setting (str): the path used by ZI to describe the setting, + e.g. 'sweep/settling/time' + """ + returndict = self.sweeper.get(setting) # this is a dict + + # The dict may have different 'depths' depending on the parameter. + # The depth is encoded in the setting string + keys = setting.split('/')[1:] + + while keys != []: + key = keys.pop(0) + returndict = returndict[key] + rawvalue = returndict + + if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: + value = rawvalue[0] + elif isinstance(rawvalue, list) and len(rawvalue) == 1: + value = rawvalue[0] + else: + value = rawvalue + + return value + + def add_signal_to_sweeper(self, demodulator, attribute): + """ + Add a signal to the output of the sweeper. When the sweeper sweeps, + the signals added to the sweeper are returned. + + Args: + demodulator (int): A number from 1-8 choosing the demodulator. + The same demodulator can be chosen several times for + different attributes, e.g. demod1 X, demod1 phase + attribute (str): The attribute to record, e.g. phase or Y + + Raises: + ValueError: if a demodulator outside the allowed range is + selected + ValueError: if an attribute not in the list of allowed attributes + is selected + """ + + # TO-DO: implement all returned attributes + valid_attributes = ['X', 'Y', 'R', 'phase'] + + # Validation + if demodulator not in range(1, 9): + raise ValueError('Can not select demodulator' + + ' {}. Only '.format(demodulator) + + 'demodulators 1-8 are available.') + if attribute not in valid_attributes: + raise ValueError('Can not select attribute:'+ + '{}. Only the following attributes are' + + ' available: ' + + ('{}, '*len(attributes)).format(*attributes)) + + # internally, we use strings very similar to the ones used by the + # instrument, but with the attribute added, e.g. + # '/dev2189/demods/0/sample/X' means X of demodulator 1. + signalstring = ('/' + self.device + + '/demods/{}/sample/{}'.format(demodulator-1, + attribute)) + if signalstring not in self._sweeper_signals: + self._sweeper_signals.append(signalstring) + + def remove_signal_from_sweeper(self, demodulator, attribute): + """ + Remove a signal from the output of the sweeper. If the signal + has not previously been added, a warning is logged. + + Args: + demodulator (int): A number from 1-8 choosing the demodulator. + The same demodulator can be chosen several times for + different attributes, e.g. demod1 X, demod1 phase + attribute (str): The attribute to record, e.g. phase or Y + """ + + signalstring = ('/' + self.device + + '/demods/{}/sample/{}'.format(demodulator-1, + attribute)) + if signalstring not in self._sweeper_signals: + log.warning('Can not remove signal with {} of'.format(attribute) + + 'demodulator {}, since it was'.format(demodulator) + + ' not previously added.') + else: + self._sweeper_signals.remove(signalstring) + + def print_sweeper_settings(self): + """ + Print the current settings of the sweeper. If execute sweeper + is called, the sweep described here will be performed. + """ + print('ACQUISITION') + toprint = ['sweeper_BWmode', 'sweeper_BW', 'sweeper_order', + 'sweeper_averaging_samples', 'sweeper_averaging_time'] + for paramname in toprint: + parameter = self.parameters[paramname] + print(' {}: {} ({})'.format(parameter.label, parameter.get(), + parameter.unit)) + + print('HORISONTAL') + toprint = ['sweeper_start', 'sweeper_stop', + 'sweeper_units', + 'sweeper_samplecount', + 'sweeper_param', 'sweeper_mode', + 'sweeper_timeout'] + for paramname in toprint: + parameter = self.parameters[paramname] + print(' {}: {}'.format(parameter.label, parameter.get())) + + print('VERTICAL') + count = 1 + for signal in self._sweeper_signals: + (_, _, _, dm, _, attr) = signal.split('/') + print(' Signal {}: Demodulator {}: {}'.format(count, + int(dm)+1, + attr)) + count += 1 + + features = ['timeconstant', 'order', 'samplerate'] + print('DEMODULATORS') + demods = [] + for signal in self._sweeper_signals: + demods.append(int(signal.split('/')[3])) + demods = set(demods) + for dm in demods: + for feat in features: + parameter = self.parameters['demod{:d}_{}'.format(dm+1, feat)] + fmt = (dm+1, parameter.label, parameter.get(), parameter.unit) + print(' Demodulator {}: {}: {:.6f} ({})'.format(*fmt)) + print('META') + swptime = self.sweeper_sweeptime() + if swptime is not None: + print(' Expected sweep time: {:.1f} (s)'.format(swptime)) + else: + print(' Expected sweep time: N/A in auto mode') + + def execute_sweeper(self): + """ + Execute the sweeper aka wrap the ziPython function + + Reads the internal signal list and makes the sweep. + + """ + + # self.sweeper.set('sweep/samplecount', self.sweeper.get('sweep/samplecount')['samplecount'][0] ) + # self.sweeper.set('sweep/samplecount', self.sweeper_samplecount) + #path = '/%s/demods/%d/sample' % (self.device, 0) + + if self._sweeper_signals == []: + raise ValueError('No signals selected! Can not perform sweep.') + + # we must enable the demodulators we use + # after the sweep, they should be returned to their original state + streamsettings = [] + for sigstr in self._sweeper_signals: + path = '/'.join(sigstr.split('/')[:-1]) + (_, dev, _, dmnum, _) = path.split('/') + + # if the setting has never changed, it doesn't exist + # then we assume that it's zero (factory default) + try: + toget = path.replace('sample', 'enable') + # ZI like nesting... + setting = self.daq.get(toget)[dev]['demods'][dmnum]['enable']['value'][0] + except KeyError: + setting = 0 + streamsettings.append(setting) + self.daq.setInt(path.replace('sample', 'enable'), 1) + + self.sweeper.subscribe(path) # we'll get multiple subscriptions to the same demod... problem? + + self.sweeper.execute() + + start = time.time() + timeout = self.sweeper_timeout.get() + + # @William: + # The while loop below is taken from the example, I left the print statements for now..not sure + # we need them though. + while not self.sweeper.finished(): # Wait until the sweep is complete, with timeout. + time.sleep(0.2) # Where does this value come from? + progress = self.sweeper.progress() + #print("Individual sweep progress: {:.2%}.".format(progress[0]), end="\r") + # Here we could read intermediate data via: + # data = sweeper.read(True)... + # and process it while the sweep is completing. + # if device in data: + # ... + if (time.time() - start) > timeout: + # If for some reason the sweep is blocking, force the end of the + # measurement. + print("\nSweep still not finished, forcing finish...") + # should exit function with error message instead of returning data + self.sweeper.finish() + print("") + stop = time.time() + + print('Sweep took roughly {} seconds'.format(stop-start)) + return_flat_dict = True + data = self.sweeper.read(return_flat_dict) + + self.sweeper.unsubscribe('*') + for (state, sigstr) in zip(streamsettings, self._sweeper_signals): + path = '/'.join(sigstr.split('/')[:-1]) + self.daq.setInt(path.replace('sample', 'enable'), int(state)) + + return self._parsesweepdata(data) + + def _parsesweepdata(self, sweepresult): + """ + Parse the raw result of a sweep into just the data asked for by the + added sweeper signals + """ + trans = {'X': 'x', 'Y': 'y', 'Aux Input 1': 'auxin0', + 'Aux Input 2': 'auxin1', 'R': 'r', 'phase': 'phase'} + returndata = [] + for signal in self._sweeper_signals: + path = '/'.join(signal.split('/')[:-1]) + attr = signal.split('/')[-1] + data = sweepresult[path][0][0][trans[attr]] + returndata.append(data) + + return tuple(returndata) + + def close(self): + """ + Override of the base class' close function + """ + self.daq.disconnect() + super().close() + diff --git a/qcodes/instrument_drivers/ZI/__init__.py b/qcodes/instrument_drivers/ZI/__init__.py new file mode 100644 index 00000000000..7b395efcbc5 --- /dev/null +++ b/qcodes/instrument_drivers/ZI/__init__.py @@ -0,0 +1 @@ +# emnpy __init__ file \ No newline at end of file From e4093aa3d1afd0b767fd5187330421296a526697 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Thu, 26 Jan 2017 14:33:52 +0100 Subject: [PATCH 02/16] fix: Change the sweep into a MultiParameter Change the sweep from a normal instrument method into a MultiParameter Breaking Changes: execute_sweep no longer exists. All sweep data goes through the Sweep parameter. --- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 367 ++++++++++++++++-------- 1 file changed, 250 insertions(+), 117 deletions(-) diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index 11b803ea993..32e99836c6b 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -3,35 +3,199 @@ import zhinst.utils import numpy as np -import zhinst.ziPython as zipy from functools import partial -from qcodes.instrument.parameter import Parameter, ManualParameter +from qcodes.instrument.parameter import ManualParameter +from qcodes.instrument.parameter import MultiParameter from qcodes.instrument.base import Instrument + from qcodes.utils import validators as vals log = logging.getLogger(__name__) -class Sweep(Parameter): +class Sweep(MultiParameter): """ Parameter class for the ZIUHFLI instrument class for the sweeper. The get method returns a tuple of arrays, where each array contains the values of a signal added to the sweep (e.g. demodulator 4 phase). """ - pass + def __init__(self, name, instrument): + # The __init__ requires that we supply names and shapes, + # but there is no way to know what they could be known at this time. + # They are updated via build_sweep. + super().__init__(name, names=('',), shapes=((1,),)) + self._instrument = instrument + + def build_sweep(self): + """ + (Note the difference between the sweeper and the Sweep parameter) + + This is a general function for updating the sweeper. + Every time a parameter of the sweeper is changed, this function + must be called to update the sweeper. Although such behaviour is only + strictly necessary for parameters that affect the setpoints of the + Sweep parameter, having to call this function for any parameter is + deemed more user friendly (easier to remember; when? -always). + + The function sets all (user specified) settings on the sweeper and + additionally sets names, units, and setpoints for the Sweep + parameter. + + """ + + signals = self._instrument._sweeper_signals + sweepdict = self._instrument._sweepdict + + print('This is the sweep builder') + + sigunits = {'X': 'V', 'Y': 'V', 'R': 'Vrms', 'Xrms': 'Vrms', + 'Yrms': 'Vrms', 'Rrms': 'Vrms', 'phase': 'degrees'} + names = [] + units = [] + for sig in signals: + name = sig.split('/')[-1] + names.append(name) + units.append(sigunits[name]) + self.names = tuple(names) + self.units = tuple(units) + + # TO-DO: what are good set point names? + spnamedict = {'auxouts/0/offset': 'Volts', + 'auxouts/1/offset': 'Volts', + 'auxouts/2/offset': 'Volts', + 'auxouts/3/offset': 'Volts', + 'demods/0/phaseshift': 'degrees', + 'demods/1/phaseshift': 'degrees', + 'demods/2/phaseshift': 'degrees', + 'demods/3/phaseshift': 'degrees', + 'demods/4/phaseshift': 'degrees', + 'demods/5/phaseshift': 'degrees', + 'demods/6/phaseshift': 'degrees', + 'demods/7/phaseshift': 'degrees', + 'oscs/0/freq': 'Hz', + 'oscs/1/freq': 'Hz', + 'sigouts/0/amplitudes/3': 'Volts', + 'sigouts/0/offset': 'Volts', + 'sigouts/1/amplitudes/7': 'Volts', + 'sigouts/1/offset': 'Volts' + } + sp_name = spnamedict[sweepdict['gridnode']] + + self.setpoint_names = ((sp_name,),)*len(signals) + start = sweepdict['start'] + stop = sweepdict['stop'] + npts = sweepdict['samplecount'] + # TO-DO: make sure that these setpoints are correct, i.e. actually + # matching what the UHFLI does + if sweepdict['xmapping'] == 'lin': + sw = tuple(np.linspace(start, stop, npts)) + else: + logstart = np.log10(start) + logstop = np.log10(stop) + sw = tuple(np.logspace(logstart, logstop, npts)) + self.setpoints = ((sw,),)*len(signals) + self.shapes = ((npts,),)*len(signals) + + # Now actually send the settings to the instrument + for (setting, value) in sweepdict.items(): + setting = 'sweep/' + setting + self._instrument.sweeper.set(setting, value) + + self._instrument.sweep_correctly_built = True + + def get(self): + """ + Execute the sweeper and return the data corresponding to the + subscribed signals. + """ + daq = self._instrument.daq + signals = self._instrument._sweeper_signals + sweeper = self._instrument.sweeper + + if signals == []: + raise ValueError('No signals selected! Can not perform sweep.') + + if self._instrument.sweep_correctly_built is False: + raise ValueError('The sweep has not been correctly built.' + + ' Please run Sweep.build_sweep.') + + # We must enable the demodulators we use. + # After the sweep, they should be returned to their original state + streamsettings = [] # This list keeps track of the pre-sweep settings + for sigstr in signals: + path = '/'.join(sigstr.split('/')[:-1]) + (_, dev, _, dmnum, _) = path.split('/') + + # If the setting has never changed, it doesn't exist in the UHFLI. + # In that case, we assume that it's zero (factory default) + try: + toget = path.replace('sample', 'enable') + # ZI like nesting... + setting = daq.get(toget)[dev]['demods'][dmnum]['enable']['value'][0] + except KeyError: + setting = 0 + streamsettings.append(setting) + daq.setInt(path.replace('sample', 'enable'), 1) + + # We potentially subscribe several times to the same demodulator, + # but that should not be a problem + sweeper.subscribe(path) + + + sweeper.execute() + timeout = self._instrument.sweeper_timeout.get() + start = time.time() + while not sweeper.finished(): # Wait until the sweep is complete, with timeout. + time.sleep(0.2) # Check every 200 ms whether the sweep is done + # Here we could read intermediate data via: + # data = sweeper.read(True)... + # and process it while the sweep is completing. + if (time.time() - start) > timeout: + # If for some reason the sweep is blocking, force the end of the + # measurement. + log.error("Sweep still not finished, forcing finish...") + # should exit function with error message instead of returning data + sweeper.finish() + + return_flat_dict = True + data = self.sweeper.read(return_flat_dict) + + sweeper.unsubscribe('*') + for (state, sigstr) in zip(streamsettings, signals): + path = '/'.join(sigstr.split('/')[:-1]) + self.daq.setInt(path.replace('sample', 'enable'), int(state)) + + return self._parsesweepdata(data) + + def _parsesweepdata(self, sweepresult): + """ + Parse the raw result of a sweep into just the data asked for by the + added sweeper signals. Used by Sweep.get. + """ + trans = {'X': 'x', 'Y': 'y', 'Aux Input 1': 'auxin0', + 'Aux Input 2': 'auxin1', 'R': 'r', 'phase': 'phase', + 'Xrms': 'xpwr', 'Yrms': 'ypwr', 'Rrms': 'rpwr'} + returndata = [] + print('Sweeper returning:') + for signal in self._sweeper_signals: + path = '/'.join(signal.split('/')[:-1]) + attr = signal.split('/')[-1] + data = sweepresult[path][0][0][trans[attr]] + returndata.append(data) + print(trans[attr]) + return tuple(returndata) class ZIUHFLI(Instrument): """ QCoDeS driver for ZI UHF LI """ - def __init__(self, name, device_ID, api_level=5): - - super().__init__(name) - + def __init__(self, name, device_ID, api_level=5, **kwargs): + + super().__init__(name, **kwargs) (self.daq, self.device, self.props) = zhinst.utils.create_api_session(device_ID, api_level) @@ -42,6 +206,19 @@ def __init__(self, name, device_ID, api_level=5): self.sweeper = self.daq.sweep() self.sweeper.set('sweep/device', self.device) + # this variable enforces building the sweep before using it + self._sweep_cb = False + + @property + def sweep_correctly_built(self): + return self._sweep_cd + + @sweep_correctly_built.setter + def sweep_correctly_built(self, value): + if not isinstance(value, bool): + raise ValueError('sweep_correctly_built') + self._sweep_cb = value + ######################################## # INSTRUMENT PARAMETERS @@ -116,7 +293,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_BWmode', label='Sweeper bandwidth control mode', - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/bandwidthcontrol'), get_cmd=partial(self._sweep_getter, 'sweep/bandwidthcontrol'), @@ -133,7 +310,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_BW', label='Fixed bandwidth sweeper bandwidth (NEP)', - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/bandwidth'), get_cmd=partial(self._sweep_getter, 'sweep/bandwidth'), @@ -148,7 +325,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_start', label='Start value of the sweep', - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/start'), get_cmd=partial(self._sweep_getter, 'sweep/start'), @@ -156,7 +333,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_stop', label='Stop value of the sweep', - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/stop'), get_cmd=partial(self._sweep_getter, 'sweep/stop'), @@ -164,7 +341,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_samplecount', label='Length of the sweep (pts)', - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/samplecount'), get_cmd=partial(self._sweep_getter, 'sweep/samplecount'), @@ -193,7 +370,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_param', label='Parameter to sweep (sweep x-axis)', - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/gridnode'), val_mapping=sweepparams, get_cmd=partial(self._sweep_getter, @@ -234,7 +411,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_mode', label='Sweep mode', - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/scan'), get_cmd=partial(self._sweep_getter, 'sweep/scan'), val_mapping=sweepmodes, @@ -243,7 +420,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_order', label='Sweeper filter order', - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/order'), get_cmd=partial(self._sweep_getter, 'sweep/order'), @@ -256,7 +433,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_settlingtime', label=('Minimal settling time for the ' + 'sweeper'), - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/settling/time'), get_cmd=partial(self._sweep_getter, 'sweep/settling/time'), @@ -283,7 +460,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_averaging_samples', label=('Minimal no. of samples to average at ' + 'each sweep point'), - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/averaging/sample'), get_cmd=partial(self._sweep_getter, 'sweep/averaging/sample'), @@ -296,7 +473,7 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_averaging_time', label=('Minimal averaging time'), - set_cmd=partial(self.sweeper.set, + set_cmd=partial(self._sweep_setter, 'sweep/averaging/tc'), get_cmd=partial(self._sweep_getter, 'sweep/averaging/tc'), @@ -307,6 +484,15 @@ def __init__(self, name, device_ID, api_level=5): sweeper_averaging_samples.""" ) + self.add_parameter('sweeper_xmapping', + label='Sweeper x mapping', + set_cmd=partial(self._sweep_setter, + 'sweep/xmapping'), + get_cmd=partial(self._sweep_getter, + 'sweep/xmapping'), + val_mapping={'lin': 0, 'log': 1} + ) + self.add_parameter('sweeper_sweeptime', label='Expected sweep time', unit='s', @@ -315,17 +501,37 @@ def __init__(self, name, device_ID, api_level=5): self.add_parameter('sweeper_timeout', label='Sweep timeout', unit='s', - initial_value=10, + initial_value=600, parameter_class=ManualParameter) + ######################################## + # THE SWEEP ITSELF + self.add_parameter('Sweep', + parameter_class=Sweep, + ) + # A "manual" parameter: a list of the signals for the sweeper # to subscribe to self._sweeper_signals = [] - # Set up the sweeper with a minimal number of required settings - # (since some of the factory defaults are nonsensical) - self.sweeper_BW.set(100) # anything >0 will do - self.sweeper_order.set(1) + # this is the dictionary keeping track of the sweeper settings + # These are the default settings + self._sweepdict = {'start': 1e6, + 'stop': 10e6, + 'samplecount': 25, + 'bandwidthcontrol': 1, # fixed mode + 'bandwidth': 50, + 'gridnode': 'oscs/0/freq', + 'scan': 0, # sequential scan + 'order': 1, + 'settling/time': 1e-6, + 'settling/tc': 7, + 'averaging/sample': 25, + 'averaging/tc': 100e-3, + 'xmapping': 0, # linear + } + # Set up the sweeper with the above settings + self.Sweep.build_sweep() def _demod_setter(self, mode, demod, setting, value): """ @@ -455,6 +661,18 @@ def _get_sweep_time(self): time_est = (settlingtime+averagingtime)*self.sweeper_samplecount.get() return time_est + def _sweep_setter(self, setting, value): + """ + set_cmd for all sweeper parameters. The value and setting are saved in + a dictionary which is read by the Sweep parameter's build_sweep method + and only then sent to th e instrument. + """ + key = '/'.join(setting.split('/')[1:]) + self._sweepdict[key] = value + self.sweep_correctly_built = False + print('Sweep setter here') + print('Received: {}, {}'.format(setting, value)) + def _sweep_getter(self, setting): """ General get_cmd for sweeper parameters @@ -466,10 +684,12 @@ def _sweep_getter(self, setting): setting (str): the path used by ZI to describe the setting, e.g. 'sweep/settling/time' """ + # TO-DO: Should this look up in _sweepdict rather than query the + # instrument? returndict = self.sweeper.get(setting) # this is a dict # The dict may have different 'depths' depending on the parameter. - # The depth is encoded in the setting string + # The depth is encoded in the setting string (number of '/') keys = setting.split('/')[1:] while keys != []: @@ -505,7 +725,7 @@ def add_signal_to_sweeper(self, demodulator, attribute): """ # TO-DO: implement all returned attributes - valid_attributes = ['X', 'Y', 'R', 'phase'] + valid_attributes = ['X', 'Y', 'R', 'phase', 'Xrms', 'Yrms', 'Rrms'] # Validation if demodulator not in range(1, 9): @@ -576,9 +796,8 @@ def print_sweeper_settings(self): count = 1 for signal in self._sweeper_signals: (_, _, _, dm, _, attr) = signal.split('/') - print(' Signal {}: Demodulator {}: {}'.format(count, - int(dm)+1, - attr)) + fmt = (count, int(dm)+1, attr) + print(' Signal {}: Demodulator {}: {}'.format(*fmt)) count += 1 features = ['timeconstant', 'order', 'samplerate'] @@ -598,94 +817,8 @@ def print_sweeper_settings(self): print(' Expected sweep time: {:.1f} (s)'.format(swptime)) else: print(' Expected sweep time: N/A in auto mode') - - def execute_sweeper(self): - """ - Execute the sweeper aka wrap the ziPython function - - Reads the internal signal list and makes the sweep. - - """ - - # self.sweeper.set('sweep/samplecount', self.sweeper.get('sweep/samplecount')['samplecount'][0] ) - # self.sweeper.set('sweep/samplecount', self.sweeper_samplecount) - #path = '/%s/demods/%d/sample' % (self.device, 0) - - if self._sweeper_signals == []: - raise ValueError('No signals selected! Can not perform sweep.') - - # we must enable the demodulators we use - # after the sweep, they should be returned to their original state - streamsettings = [] - for sigstr in self._sweeper_signals: - path = '/'.join(sigstr.split('/')[:-1]) - (_, dev, _, dmnum, _) = path.split('/') - - # if the setting has never changed, it doesn't exist - # then we assume that it's zero (factory default) - try: - toget = path.replace('sample', 'enable') - # ZI like nesting... - setting = self.daq.get(toget)[dev]['demods'][dmnum]['enable']['value'][0] - except KeyError: - setting = 0 - streamsettings.append(setting) - self.daq.setInt(path.replace('sample', 'enable'), 1) - - self.sweeper.subscribe(path) # we'll get multiple subscriptions to the same demod... problem? - - self.sweeper.execute() - - start = time.time() - timeout = self.sweeper_timeout.get() - - # @William: - # The while loop below is taken from the example, I left the print statements for now..not sure - # we need them though. - while not self.sweeper.finished(): # Wait until the sweep is complete, with timeout. - time.sleep(0.2) # Where does this value come from? - progress = self.sweeper.progress() - #print("Individual sweep progress: {:.2%}.".format(progress[0]), end="\r") - # Here we could read intermediate data via: - # data = sweeper.read(True)... - # and process it while the sweep is completing. - # if device in data: - # ... - if (time.time() - start) > timeout: - # If for some reason the sweep is blocking, force the end of the - # measurement. - print("\nSweep still not finished, forcing finish...") - # should exit function with error message instead of returning data - self.sweeper.finish() - print("") - stop = time.time() - - print('Sweep took roughly {} seconds'.format(stop-start)) - return_flat_dict = True - data = self.sweeper.read(return_flat_dict) - - self.sweeper.unsubscribe('*') - for (state, sigstr) in zip(streamsettings, self._sweeper_signals): - path = '/'.join(sigstr.split('/')[:-1]) - self.daq.setInt(path.replace('sample', 'enable'), int(state)) - - return self._parsesweepdata(data) - - def _parsesweepdata(self, sweepresult): - """ - Parse the raw result of a sweep into just the data asked for by the - added sweeper signals - """ - trans = {'X': 'x', 'Y': 'y', 'Aux Input 1': 'auxin0', - 'Aux Input 2': 'auxin1', 'R': 'r', 'phase': 'phase'} - returndata = [] - for signal in self._sweeper_signals: - path = '/'.join(signal.split('/')[:-1]) - attr = signal.split('/')[-1] - data = sweepresult[path][0][0][trans[attr]] - returndata.append(data) - - return tuple(returndata) + print(' Sweep timeout: {} ({})'.format(self.sweeper_timeout.get(), + 's')) def close(self): """ From 9a43451fe15530ffefc7c6d1d36e35590410bf6d Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Thu, 26 Jan 2017 17:49:25 +0100 Subject: [PATCH 03/16] fix: Tidy code Tidy code and remove whitespace --- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 181 +++++++++++++++--------- 1 file changed, 117 insertions(+), 64 deletions(-) diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index 32e99836c6b..87bf1ae75cd 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -1,14 +1,19 @@ import time import logging -import zhinst.utils - import numpy as np - from functools import partial + +try: + import zhinst.utils +except ImportError: + raise ImportError('''Could not find Zurich Instruments Lab One software. + Please refer to the Zi UHF-LI User Manual for + download and installation instructions. + ''') + from qcodes.instrument.parameter import ManualParameter from qcodes.instrument.parameter import MultiParameter from qcodes.instrument.base import Instrument - from qcodes.utils import validators as vals log = logging.getLogger(__name__) @@ -20,18 +25,32 @@ class Sweep(MultiParameter): The get method returns a tuple of arrays, where each array contains the values of a signal added to the sweep (e.g. demodulator 4 phase). + + Attributes: + names (tuple): Tuple of strings containing the names of the sweep + signals (to be measured) + units (tuple): Tuple of strings containg the units of the signals + shapes (tuple): Tuple of tuples each containing the Length of a + signal. + setpoints (tuple): Tuple of N copies of the sweep x-axis points, + where N is he number of measured signals + setpoint_names (tuple): Tuple of N identical strings with the name + of the sweep x-axis. + """ - def __init__(self, name, instrument): + def __init__(self, name, instrument, **kwargs): # The __init__ requires that we supply names and shapes, # but there is no way to know what they could be known at this time. # They are updated via build_sweep. - super().__init__(name, names=('',), shapes=((1,),)) - self._instrument = instrument - + super().__init__(name, names=('',), shapes=((1,),), **kwargs) + self._instrument = instrument + def build_sweep(self): """ - (Note the difference between the sweeper and the Sweep parameter) + Build a sweep with the current sweep settings. Must be called + before the sweep can be executed. + For developers: This is a general function for updating the sweeper. Every time a parameter of the sweeper is changed, this function must be called to update the sweeper. Although such behaviour is only @@ -41,14 +60,13 @@ def build_sweep(self): The function sets all (user specified) settings on the sweeper and additionally sets names, units, and setpoints for the Sweep - parameter. + parameter. """ - signals = self._instrument._sweeper_signals sweepdict = self._instrument._sweepdict - print('This is the sweep builder') + log.info('Built a sweep') sigunits = {'X': 'V', 'Y': 'V', 'R': 'Vrms', 'Xrms': 'Vrms', 'Yrms': 'Vrms', 'Rrms': 'Vrms', 'phase': 'degrees'} @@ -57,11 +75,11 @@ def build_sweep(self): for sig in signals: name = sig.split('/')[-1] names.append(name) - units.append(sigunits[name]) + units.append(sigunits[name]) self.names = tuple(names) self.units = tuple(units) - # TO-DO: what are good set point names? + # TODO: what are good set point names? spnamedict = {'auxouts/0/offset': 'Volts', 'auxouts/1/offset': 'Volts', 'auxouts/2/offset': 'Volts', @@ -87,8 +105,9 @@ def build_sweep(self): start = sweepdict['start'] stop = sweepdict['stop'] npts = sweepdict['samplecount'] - # TO-DO: make sure that these setpoints are correct, i.e. actually + # TODO: make sure that these setpoints are correct, i.e. actually # matching what the UHFLI does + # TODO: support non-sequential sweep mode if sweepdict['xmapping'] == 'lin': sw = tuple(np.linspace(start, stop, npts)) else: @@ -109,12 +128,22 @@ def get(self): """ Execute the sweeper and return the data corresponding to the subscribed signals. + + Returns: + + tuple: Tuple containg N numpy arrays where N is the number + of signals added to the sweep. + + Raises: + ValueError: If no signals have been added to the sweep + ValueError: If a sweep setting has been modified since + the last sweep, but Sweep.build_sweep has not been run """ daq = self._instrument.daq signals = self._instrument._sweeper_signals sweeper = self._instrument.sweeper - if signals == []: + if signals == []: raise ValueError('No signals selected! Can not perform sweep.') if self._instrument.sweep_correctly_built is False: @@ -128,22 +157,21 @@ def get(self): path = '/'.join(sigstr.split('/')[:-1]) (_, dev, _, dmnum, _) = path.split('/') - # If the setting has never changed, it doesn't exist in the UHFLI. + # If the setting has never changed, get returns an empty dict. # In that case, we assume that it's zero (factory default) try: toget = path.replace('sample', 'enable') - # ZI like nesting... + # ZI like nesting inside dicts... setting = daq.get(toget)[dev]['demods'][dmnum]['enable']['value'][0] except KeyError: setting = 0 streamsettings.append(setting) daq.setInt(path.replace('sample', 'enable'), 1) - + # We potentially subscribe several times to the same demodulator, # but that should not be a problem sweeper.subscribe(path) - sweeper.execute() timeout = self._instrument.sweeper_timeout.get() start = time.time() @@ -160,55 +188,75 @@ def get(self): sweeper.finish() return_flat_dict = True - data = self.sweeper.read(return_flat_dict) + data = sweeper.read(return_flat_dict) sweeper.unsubscribe('*') for (state, sigstr) in zip(streamsettings, signals): path = '/'.join(sigstr.split('/')[:-1]) - self.daq.setInt(path.replace('sample', 'enable'), int(state)) + daq.setInt(path.replace('sample', 'enable'), int(state)) return self._parsesweepdata(data) def _parsesweepdata(self, sweepresult): """ - Parse the raw result of a sweep into just the data asked for by the + Parse the raw result of a sweep into just the data asked for by the added sweeper signals. Used by Sweep.get. + + Args: + sweepresult (dict): The dict returned by sweeper.read + + Returns: + tuple: The requested signals in a tuple """ trans = {'X': 'x', 'Y': 'y', 'Aux Input 1': 'auxin0', 'Aux Input 2': 'auxin1', 'R': 'r', 'phase': 'phase', 'Xrms': 'xpwr', 'Yrms': 'ypwr', 'Rrms': 'rpwr'} returndata = [] - print('Sweeper returning:') - for signal in self._sweeper_signals: + + for signal in self._instrument._sweeper_signals: path = '/'.join(signal.split('/')[:-1]) attr = signal.split('/')[-1] data = sweepresult[path][0][0][trans[attr]] returndata.append(data) - print(trans[attr]) return tuple(returndata) class ZIUHFLI(Instrument): """ - QCoDeS driver for ZI UHF LI + QCoDeS driver for ZI UHF-LI. + + Currently implementing demodulator settings and the sweeper functionality. + + Requires ZI Lab One software to be installed on the computer running QCoDeS. + Furthermore, the Data Server and Web Server must be running and a connection + between the two must be made. + + TODOs: + * Add the scope + * Add zoom-FFT """ def __init__(self, name, device_ID, api_level=5, **kwargs): - + """ + Create an instance of the instrument. + + Args: + name (str): The internal QCoDeS name of the instrument + device_ID (str): The device name as listed in the web server. + api_level (int): Compatibility mode of the API interface. Must be 5 + for the UHF. + """ + super().__init__(name, **kwargs) (self.daq, self.device, self.props) = zhinst.utils.create_api_session(device_ID, api_level) - # @William: - # the sweeper has to be declared here, otherwise I do not know how to bind to the - # right getters and setters - self.sweeper = self.daq.sweep() self.sweeper.set('sweep/device', self.device) # this variable enforces building the sweep before using it self._sweep_cb = False - + @property def sweep_correctly_built(self): return self._sweep_cd @@ -287,7 +335,6 @@ def sweep_correctly_built(self, value): instrument. """) - ######################################## # SWEEPER PARAMETERS @@ -347,7 +394,7 @@ def sweep_correctly_built(self, value): 'sweep/samplecount'), vals=vals.Ints(0, 100000)) - # helper dict used by sweeper_param parameter + # val_mapping for sweeper_param parameter sweepparams = {'Aux Out 1 Offset': 'auxouts/0/offset', 'Aux Out 2 Offset': 'auxouts/1/offset', 'Aux Out 3 Offset': 'auxouts/2/offset', @@ -378,6 +425,7 @@ def sweep_correctly_built(self, value): vals=vals.Enum(*list(sweepparams.keys())) ) + # val_mapping for sweeper_units parameter sweepunits = {'Aux Out 1 Offset': 'V', 'Aux Out 2 Offset': 'V', 'Aux Out 3 Offset': 'V', @@ -403,7 +451,7 @@ def sweep_correctly_built(self, value): get_cmd=self.sweeper_param.get, get_parser=lambda x:sweepunits[x]) - # helper dict used by sweeper_mode parameter + # val_mapping for sweeper_mode parameter sweepmodes = {'Sequential': 0, 'Binary': 1, 'Biderectional': 2, @@ -466,7 +514,7 @@ def sweep_correctly_built(self, value): 'sweep/averaging/sample'), vals=vals.Ints(1), docstring="""The actual number of samples is the - maximum of this value and the + maximum of this value and the sweeper_averaging_time times the relevant sample rate.""" ) @@ -480,7 +528,7 @@ def sweep_correctly_built(self, value): unit='s', docstring="""The actual number of samples is the maximum of this value times the - relevant sample rate and the + relevant sample rate and the sweeper_averaging_samples.""" ) @@ -514,7 +562,7 @@ def sweep_correctly_built(self, value): # to subscribe to self._sweeper_signals = [] - # this is the dictionary keeping track of the sweeper settings + # This is the dictionary keeping track of the sweeper settings # These are the default settings self._sweepdict = {'start': 1e6, 'stop': 10e6, @@ -561,6 +609,10 @@ def _demod_getter(self, demod, setting): want a single value This function counts demodulators in a zero-indexed way. + + returns: + Union[int, float]: In all cases checked so far, a single value + is returned. """ querystr = '/{}/demods/{}/{}'.format(self.device, demod, setting) returndict = self.daq.get(querystr) @@ -600,20 +652,20 @@ def NEPBW_to_timeconstant(NEPBW, order): def _get_sweep_time(self): """ - get_cmd for the sweeper_sweeptime parameter. + get_cmd for the sweeper_sweeptime parameter. Note: this calculation is only an estimate and not precise to more than a few percent. Returns: - Union[float, None]: None if the bandwidthcontrol setting is + Union[float, None]: None if the bandwidthcontrol setting is 'auto' (then all bets are off), otherwise a time in seconds. Raises: ValueError: if no signals are added to the sweep """ - # Possible TO-DO: cut down on the number of instrument + # Possible TODO: cut down on the number of instrument # queries. if self._sweeper_signals == []: @@ -621,13 +673,13 @@ def _get_sweep_time(self): mode = self.sweeper_BWmode.get() - # The effective time constant of the demodulator depends on the + # The effective time constant of the demodulator depends on the # sweeper/bandwidthcontrol setting. # # If this setting is 'current', the largest current # time constant of the involved demodulators is used # - # If the setting is 'fixed', the NEP BW specified under + # If the setting is 'fixed', the NEP BW specified under # sweep/bandwidth is used. The filter order is needed to convert # the NEP BW to a time constant @@ -652,7 +704,7 @@ def _get_sweep_time(self): elif mode == 'auto': return None - + settlingtime = max(self.sweeper_settlingtc.get()*tau_c, self.sweeper_settlingtime.get()) averagingtime = max(self.sweeper_averaging_time.get()*tau_c*rate, @@ -665,13 +717,11 @@ def _sweep_setter(self, setting, value): """ set_cmd for all sweeper parameters. The value and setting are saved in a dictionary which is read by the Sweep parameter's build_sweep method - and only then sent to th e instrument. + and only then sent to the instrument. """ key = '/'.join(setting.split('/')[1:]) self._sweepdict[key] = value self.sweep_correctly_built = False - print('Sweep setter here') - print('Received: {}, {}'.format(setting, value)) def _sweep_getter(self, setting): """ @@ -681,10 +731,10 @@ def _sweep_getter(self, setting): single values. Args: - setting (str): the path used by ZI to describe the setting, + setting (str): the path used by ZI to describe the setting, e.g. 'sweep/settling/time' """ - # TO-DO: Should this look up in _sweepdict rather than query the + # TODO: Should this look up in _sweepdict rather than query the # instrument? returndict = self.sweeper.get(setting) # this is a dict @@ -695,7 +745,7 @@ def _sweep_getter(self, setting): while keys != []: key = keys.pop(0) returndict = returndict[key] - rawvalue = returndict + rawvalue = returndict if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: value = rawvalue[0] @@ -713,18 +763,18 @@ def add_signal_to_sweeper(self, demodulator, attribute): Args: demodulator (int): A number from 1-8 choosing the demodulator. - The same demodulator can be chosen several times for + The same demodulator can be chosen several times for different attributes, e.g. demod1 X, demod1 phase attribute (str): The attribute to record, e.g. phase or Y Raises: - ValueError: if a demodulator outside the allowed range is + ValueError: if a demodulator outside the allowed range is selected ValueError: if an attribute not in the list of allowed attributes is selected """ - # TO-DO: implement all returned attributes + # TODO: implement all possibly returned attributes valid_attributes = ['X', 'Y', 'R', 'phase', 'Xrms', 'Yrms', 'Rrms'] # Validation @@ -735,10 +785,10 @@ def add_signal_to_sweeper(self, demodulator, attribute): if attribute not in valid_attributes: raise ValueError('Can not select attribute:'+ '{}. Only the following attributes are' + - ' available: ' + + ' available: ' + ('{}, '*len(attributes)).format(*attributes)) - # internally, we use strings very similar to the ones used by the + # internally, we use strings very similar to the ones used by the # instrument, but with the attribute added, e.g. # '/dev2189/demods/0/sample/X' means X of demodulator 1. signalstring = ('/' + self.device + @@ -754,26 +804,27 @@ def remove_signal_from_sweeper(self, demodulator, attribute): Args: demodulator (int): A number from 1-8 choosing the demodulator. - The same demodulator can be chosen several times for + The same demodulator can be chosen several times for different attributes, e.g. demod1 X, demod1 phase attribute (str): The attribute to record, e.g. phase or Y - """ + """ signalstring = ('/' + self.device + '/demods/{}/sample/{}'.format(demodulator-1, attribute)) if signalstring not in self._sweeper_signals: log.warning('Can not remove signal with {} of'.format(attribute) + - 'demodulator {}, since it was'.format(demodulator) + + ' demodulator {}, since it was'.format(demodulator) + ' not previously added.') else: self._sweeper_signals.remove(signalstring) def print_sweeper_settings(self): """ - Print the current settings of the sweeper. If execute sweeper - is called, the sweep described here will be performed. - """ + Pretty-print the current settings of the sweeper. + If Sweep.build_sweep and Sweep.get are called, the sweep described + here will be performed. + """ print('ACQUISITION') toprint = ['sweeper_BWmode', 'sweeper_BW', 'sweeper_order', 'sweeper_averaging_samples', 'sweeper_averaging_time'] @@ -783,7 +834,7 @@ def print_sweeper_settings(self): parameter.unit)) print('HORISONTAL') - toprint = ['sweeper_start', 'sweeper_stop', + toprint = ['sweeper_start', 'sweeper_stop', 'sweeper_units', 'sweeper_samplecount', 'sweeper_param', 'sweeper_mode', @@ -791,7 +842,7 @@ def print_sweeper_settings(self): for paramname in toprint: parameter = self.parameters[paramname] print(' {}: {}'.format(parameter.label, parameter.get())) - + print('VERTICAL') count = 1 for signal in self._sweeper_signals: @@ -819,6 +870,8 @@ def print_sweeper_settings(self): print(' Expected sweep time: N/A in auto mode') print(' Sweep timeout: {} ({})'.format(self.sweeper_timeout.get(), 's')) + ready = self.Sweep.sweep_correctly_built + print(' Sweep built and ready to execute: {}'.format(ready)) def close(self): """ From 6d061e057e2f30e067d4b87853817e54a7572bcd Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Fri, 27 Jan 2017 17:13:17 +0100 Subject: [PATCH 04/16] feat: Add example notebook Add an example notebook explaining how to use the sweeper. --- .../Qcodes example with ZI UHF-LI.ipynb | 463 ++++++++++++++++++ qcodes/instrument_drivers/ZI/ZIUHFLI.py | 229 ++++++++- 2 files changed, 681 insertions(+), 11 deletions(-) create mode 100644 docs/examples/Qcodes example with ZI UHF-LI.ipynb diff --git a/docs/examples/Qcodes example with ZI UHF-LI.ipynb b/docs/examples/Qcodes example with ZI UHF-LI.ipynb new file mode 100644 index 00000000000..2101bfd2aa0 --- /dev/null +++ b/docs/examples/Qcodes example with ZI UHF-LI.ipynb @@ -0,0 +1,463 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [], + "source": [ + "import time\n", + "import logging\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "plt.ion()\n", + "\n", + "import qcodes as qc\n", + "from qcodes.instrument_drivers.ZI.ZIUHFLI import ZIUHFLI\n", + "\n", + "log = logging.getLogger(__name__)\n", + "log.setLevel(logging.INFO)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Prerequisites\n", + "\n", + "It is necessary to download and install the ZI Lab One software. Additionally, both the data server and the web server must run, and a connection to the instrument must be instantiated (this can be done via the web interface)." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": false, + "scrolled": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:get_parser is set, but will not be used (name demod1_signalin)\n", + "WARNING:root:get_parser is set, but will not be used (name demod1_sinc)\n", + "WARNING:root:get_parser is set, but will not be used (name demod1_streaming)\n", + "WARNING:root:get_parser is set, but will not be used (name demod1_trigger)\n", + "WARNING:root:get_parser is set, but will not be used (name demod2_signalin)\n", + "WARNING:root:get_parser is set, but will not be used (name demod2_sinc)\n", + "WARNING:root:get_parser is set, but will not be used (name demod2_streaming)\n", + "WARNING:root:get_parser is set, but will not be used (name demod2_trigger)\n", + "WARNING:root:get_parser is set, but will not be used (name demod3_signalin)\n", + "WARNING:root:get_parser is set, but will not be used (name demod3_sinc)\n", + "WARNING:root:get_parser is set, but will not be used (name demod3_streaming)\n", + "WARNING:root:get_parser is set, but will not be used (name demod3_trigger)\n", + "WARNING:root:get_parser is set, but will not be used (name demod4_signalin)\n", + "WARNING:root:get_parser is set, but will not be used (name demod4_sinc)\n", + "WARNING:root:get_parser is set, but will not be used (name demod4_streaming)\n", + "WARNING:root:get_parser is set, but will not be used (name demod4_trigger)\n", + "WARNING:root:get_parser is set, but will not be used (name demod5_signalin)\n", + "WARNING:root:get_parser is set, but will not be used (name demod5_sinc)\n", + "WARNING:root:get_parser is set, but will not be used (name demod5_streaming)\n", + "WARNING:root:get_parser is set, but will not be used (name demod5_trigger)\n", + "WARNING:root:get_parser is set, but will not be used (name demod6_signalin)\n", + "WARNING:root:get_parser is set, but will not be used (name demod6_sinc)\n", + "WARNING:root:get_parser is set, but will not be used (name demod6_streaming)\n", + "WARNING:root:get_parser is set, but will not be used (name demod6_trigger)\n", + "WARNING:root:get_parser is set, but will not be used (name demod7_signalin)\n", + "WARNING:root:get_parser is set, but will not be used (name demod7_sinc)\n", + "WARNING:root:get_parser is set, but will not be used (name demod7_streaming)\n", + "WARNING:root:get_parser is set, but will not be used (name demod7_trigger)\n", + "WARNING:root:get_parser is set, but will not be used (name demod8_signalin)\n", + "WARNING:root:get_parser is set, but will not be used (name demod8_sinc)\n", + "WARNING:root:get_parser is set, but will not be used (name demod8_streaming)\n", + "WARNING:root:get_parser is set, but will not be used (name demod8_trigger)\n", + "WARNING:root:get_parser is set, but will not be used (name sweeper_BWmode)\n", + "WARNING:root:get_parser is set, but will not be used (name sweeper_param)\n", + "WARNING:root:get_parser is set, but will not be used (name sweeper_units)\n", + "WARNING:root:get_parser is set, but will not be used (name sweeper_mode)\n", + "WARNING:root:get_parser is set, but will not be used (name sweeper_xmapping)\n", + "WARNING:root:get_parser is set, but will not be used (name signal_input1_AC)\n", + "WARNING:root:get_parser is set, but will not be used (name signal_input1_impedance)\n", + "WARNING:root:get_parser is set, but will not be used (name signal_input1_diff)\n", + "WARNING:root:get_parser is set, but will not be used (name signal_input2_AC)\n", + "WARNING:root:get_parser is set, but will not be used (name signal_input2_impedance)\n", + "WARNING:root:get_parser is set, but will not be used (name signal_input2_diff)\n" + ] + } + ], + "source": [ + "# Instantiate the QCoDeS instrument\n", + "zi = ZIUHFLI('ZIUHFLI', 'dev2189')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Basic Usage of the ZI UHF-LI\n", + "\n", + "Most of the \"front panel\" (i.e. the Web UI) `Lock-In` and `signal input` settings are available as parameters." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Oscillator 2 has frequency: 1050 Hz\n" + ] + } + ], + "source": [ + "zi.oscillator1_freq.set(752.1e3)\n", + "print('Oscillator 2 has frequency: {:.0f} Hz'.format(zi.oscillator2_freq.get()))\n", + "zi.signal_input1_range(1)\n", + "zi.signal_input1_scaling(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each demodulator has several settings..." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available demodulator settings:\n", + "\n", + " demod1_streaming, Data streaming ()\n", + " demod1_order, Filter order ()\n", + " demod1_timeconstant, Filter time constant (s)\n", + " demod1_phaseshift, Phase shift (degrees)\n", + " demod1_samplerate, Sample rate (Sa/s)\n", + " demod1_sinc, Sinc filter ()\n", + " demod1_signalin, Signal input ()\n", + " demod1_trigger, Trigger ()\n", + " demod1_harmonic, Reference frequency multiplication factor ()\n" + ] + } + ], + "source": [ + "print('Available demodulator settings:\\n')\n", + "for param in [p for p in zi.parameters if 'demod1' in p]:\n", + " print(' {}, {} ({})'.format(param, zi.parameters[param].label, zi.parameters[param].unit))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Using the sweeper\n", + "\n", + "The sweeper settings are configured via a bunch of parameters, all named `sweeper_XXX`.\n", + "This configures the x-axis of the sweep as well as the sweep acquisition settings. To learn more about what a certain parameter does, it is sometimes helpful to print its `__doc__` attribute.\n", + "\n", + "Which signals are **returned** by the sweeper is controlled by adding (removing) signals to (from) the sweep.\n", + "\n", + "The sweep settings can be displayed with the `print_sweeper_settings` command.\n", + "\n", + "Before the sweep can be performed, it must be built. \n", + "This is done with the Sweep parameter, which is the parameter holding the sweep data.\n", + "Note that building the sweep may change some of the time constants, and in particular change the sweep time. In case of doubt, re-run `print_sweeper_settings`." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Set up a sweep sweeping an internal oscillator frequency from 1 MHz to 5 MHz\n", + "# We want the sweeper to sweep linearly over 200 points\n", + "zi.sweeper_param('Osc 1 Frequency')\n", + "zi.sweeper_xmapping('lin')\n", + "zi.sweeper_start(1e6)\n", + "zi.sweeper_stop(5e6)\n", + "zi.sweeper_samplecount(200)\n", + "zi.sweeper_BWmode('fixed')\n", + "zi.sweeper_BW(250)\n", + "zi.sweeper_order(4)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " For each sweep point, the demodulator\n", + " filter bandwidth (time constant) may\n", + " be either set automatically, be the\n", + " current demodulator bandwidth or be\n", + " a fixed number; the sweeper_BW\n", + " parameter.\n", + " \r\n", + "\r\n", + "Parameter class:\r\n", + "\r\n", + "* `name` sweeper_BWmode\r\n", + "* `label` Sweeper bandwidth control mode\r\n", + "* `unit` \r\n", + "* `vals` \n" + ] + } + ], + "source": [ + "# I wonder what the sweeper BWmode does...\n", + "print(zi.sweeper_BWmode.__doc__)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# Add three signals to the sweep, all measured on demodulator 1\n", + "zi.add_signal_to_sweeper(1, 'Xrms')\n", + "zi.add_signal_to_sweeper(1, 'Yrms')\n", + "zi.add_signal_to_sweeper(1, 'Rrms')" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# Make sure that demodulator 1 is measuring what and as it should\n", + "zi.demod1_trigger('Continuous')\n", + "zi.demod1_signalin('Sig In 1')" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ACQUISITION\n", + " Sweeper bandwidth control mode: fixed ()\n", + " Fixed bandwidth sweeper bandwidth (NEP): 50.0 ()\n", + " Sweeper filter order: 1 ()\n", + " Minimal no. of samples to average at each sweep point: 25 ()\n", + " Minimal averaging time: 0.1 (s)\n", + " Minimal settling time for the sweeper: 1e-06 (s)\n", + " Sweep filter settling time: 4.605170185988091 (dim. less.)\n", + "HORISONTAL\n", + " Start value of the sweep: 1000000.0\n", + " Stop value of the sweep: 10000000.0\n", + " Units of sweep x-axis: Hz\n", + " Length of the sweep (pts): 25\n", + " Parameter to sweep (sweep x-axis): Osc 1 Frequency\n", + " Sweep mode: Sequential\n", + " Sweep timeout: 20\n", + "VERTICAL\n", + " Signal 1: Demodulator 1: Xrms\n", + " Signal 2: Demodulator 1: Yrms\n", + " Signal 3: Demodulator 1: Rrms\n", + "DEMODULATORS\n", + " Demodulator 1: Filter time constant: 0.000266 (s)\n", + " Demodulator 1: Filter order: 4.000000 ()\n", + " Demodulator 1: Sample rate: 1716.613770 (Sa/s)\n", + "META\n", + " Expected sweep time: 0.9 (s)\n", + " Sweep timeout: 20 (s)\n", + " Sweep built and ready to execute: False\n" + ] + } + ], + "source": [ + "# I wonder what kind of sweep we have made now...\n", + "zi.print_sweeper_settings()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ACQUISITION\n", + " Sweeper bandwidth control mode: fixed ()\n", + " Fixed bandwidth sweeper bandwidth (NEP): 78.12499999999996 ()\n", + " Sweeper filter order: 4 ()\n", + " Minimal no. of samples to average at each sweep point: 25 ()\n", + " Minimal averaging time: 0.1 (s)\n", + " Minimal settling time for the sweeper: 1e-06 (s)\n", + " Sweep filter settling time: 9.998049677807453 (dim. less.)\n", + "HORISONTAL\n", + " Start value of the sweep: 1000000.0\n", + " Stop value of the sweep: 5000000.0\n", + " Units of sweep x-axis: Hz\n", + " Length of the sweep (pts): 200\n", + " Parameter to sweep (sweep x-axis): Osc 1 Frequency\n", + " Sweep mode: Sequential\n", + " Sweep timeout: 20\n", + "VERTICAL\n", + " Signal 1: Demodulator 1: Xrms\n", + " Signal 2: Demodulator 1: Yrms\n", + " Signal 3: Demodulator 1: Rrms\n", + "DEMODULATORS\n", + " Demodulator 1: Filter time constant: 0.000266 (s)\n", + " Demodulator 1: Filter order: 4.000000 ()\n", + " Demodulator 1: Sample rate: 1716.613770 (Sa/s)\n", + "META\n", + " Expected sweep time: 4.9 (s)\n", + " Sweep timeout: 20 (s)\n", + " Sweep built and ready to execute: True\n" + ] + } + ], + "source": [ + "# Gee, that looks good! Note the last line, the sweep is NOT ready to execute.\n", + "zi.Sweep.build_sweep()\n", + "# Now it is!\n", + "zi.print_sweeper_settings()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# We can now execute the sweeper by simply invoking Sweep.get\n", + "# This returns a tuple with the signals we asked for\n", + "(X, Y, R) = zi.Sweep.get()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# This we may manually plot. " + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.figure()\n", + "plt.plot(X, lw=2, color=(0.5, 0, 0.3))\n", + "plt.plot(Y, lw=2, color=(0.5, 0.3, 0))\n", + "plt.plot(R, lw=2, color=(0.3, 0, 0.5))" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "zi.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index 87bf1ae75cd..65b573d06f9 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -335,6 +335,86 @@ def sweep_correctly_built(self, value): instrument. """) + self.add_parameter('demod{}_phaseshift'.format(demod), + label='Phase shift', + unit='degrees', + get_cmd=partial(self._demod_getter, + demod-1, 'phaseshift'), + set_cmd=partial(self._demod_setter, + 1, demod-1, 'phaseshift') + ) + + # val_mapping for the demodX_signalin parameter + dmsigins = {'Sig In 1': 0, + 'Sig In 2': 1, + 'Trigger 1': 2, + 'Trigger 2': 3, + 'Aux Out 1': 4, + 'Aux Out 2': 5, + 'Aux Out 3': 6, + 'Aux Out 4': 7, + 'Aux In 1': 8, + 'Aux In 2': 9, + 'Phi Demod 4': 10, + 'Phi Demod 8': 11} + + self.add_parameter('demod{}_signalin'.format(demod), + label='Signal input', + get_cmd=partial(self._demod_getter, + demod-1, 'adcselect'), + set_cmd=partial(self._demod_setter, + 0, demod-1, 'adcselect'), + val_mapping=dmsigins, + vals=vals.Enum(*list(dmsigins.keys())) + ) + + self.add_parameter('demod{}_sinc'.format(demod), + label='Sinc filter', + get_cmd=partial(self._demod_getter, + demod-1, 'sinc'), + set_cmd=partial(self._demod_setter, + 0, demod-1, 'sinc'), + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF') + ) + + self.add_parameter('demod{}_streaming'.format(demod), + label='Data streaming', + get_cmd=partial(self._demod_getter, + demod-1, 'enable'), + set_cmd=partial(self._demod_setter, + 0, demod-1, 'enable'), + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF') + ) + + dmtrigs = {'Continuous': 0, + 'Trigger in 3 Rise': 1, + 'Trigger in 3 Fall': 2, + 'Trigger in 3 Both': 3, + 'Trigger in 3 High': 32, + 'Trigger in 3 Low': 16, + 'Trigger in 4 Rise': 4, + 'Trigger in 4 Fall': 8, + 'Trigger in 4 Both': 12, + 'Trigger in 4 High': 128, + 'Trigger in 4 Low': 64, + 'Trigger in 3|4 Rise': 5, + 'Trigger in 3|4': 10, + 'Trigger in 3|4': 15, + 'Trigger in 3|4': 160, + 'Trigger in 3|4': 80} + + self.add_parameter('demod{}_trigger'.format(demod), + label='Trigger', + get_cmd=partial(self._demod_getter, + demod-1, 'trigger'), + set_cmd=partial(self._demod_setter, + 0, demod-1, 'trigger'), + val_mapping=dmtrigs, + vals=vals.Enum(*list(dmtrigs.keys())) + ) + ######################################## # SWEEPER PARAMETERS @@ -496,6 +576,31 @@ def sweep_correctly_built(self, value): """ ) + self.add_parameter('sweeper_inaccuracy', + label='Demodulator filter settling inaccuracy', + set_cmd=partial(self._sweep_setter, + 'sweep/settling/inaccuracy'), + docstring=""" + Demodulator filter settling inaccuracy + defining the wait time between a sweep + parameter change and recording of the + next sweep point. The settling time is + calculated as the time required to attain + the specified remaining proportion [1e-13, + 0.1] of an incoming step function. Typical + inaccuracy values: 10m for highest sweep + speed for large signals, 100u for precise + amplitude measurements, 100n for precise + noise measurements. Depending on the + order of the demodulator filter the settling + inaccuracy will define the number of filter + time constants the sweeper has to wait. The + maximum between this value and the settling + time is taken as wait time until the next + sweep point is recorded. + """ + ) + self.add_parameter('sweeper_settlingtc', label='Sweep filter settling time', get_cmd=partial(self._sweep_getter, @@ -513,10 +618,12 @@ def sweep_correctly_built(self, value): get_cmd=partial(self._sweep_getter, 'sweep/averaging/sample'), vals=vals.Ints(1), - docstring="""The actual number of samples is the - maximum of this value and the - sweeper_averaging_time times the - relevant sample rate.""" + docstring=""" + The actual number of samples is the + maximum of this value and the + sweeper_averaging_time times the + relevant sample rate. + """ ) self.add_parameter('sweeper_averaging_time', @@ -526,10 +633,11 @@ def sweep_correctly_built(self, value): get_cmd=partial(self._sweep_getter, 'sweep/averaging/tc'), unit='s', - docstring="""The actual number of samples is the - maximum of this value times the - relevant sample rate and the - sweeper_averaging_samples.""" + docstring=""" + The actual number of samples is the + maximum of this value times the + relevant sample rate and the + sweeper_averaging_samples.""" ) self.add_parameter('sweeper_xmapping', @@ -552,6 +660,58 @@ def sweep_correctly_built(self, value): initial_value=600, parameter_class=ManualParameter) + ######################################## + # SIGNAL INPUTS + + for sigin in range(1, 3): + + self.add_parameter('signal_input{}_range'.format(sigin), + label='Input range', + set_cmd=partial(self._sigin_setter, + 1, sigin-1, 'range'), + get_cmd=partial(self._sigin_getter, + sigin-1, 'range'), + unit='V') + + self.add_parameter('signal_input{}_scaling'.format(sigin), + label='Input scaling', + set_cmd=partial(self._sigin_setter, + 1, sigin-1, 'scaling'), + get_cmd=partial(self._sigin_getter, + sigin-1, 'scaling'), + ) + + self.add_parameter('signal_input{}_AC'.format(sigin), + label='AC coupling', + set_cmd=partial(self._sigin_setter, + 0, sigin-1, 'ac'), + get_cmd=partial(self._sigin_getter, + sigin-1, 'ac'), + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF') + ) + + self.add_parameter('signal_input{}_impedance'.format(sigin), + label='Input impedance', + set_cmd=partial(self._sigin_setter, + 0, sigin-1, 'imp50'), + get_cmd=partial(self._sigin_getter, + sigin-1, 'imp50'), + val_mapping={50: 1, 1000: 0}, + vals=vals.Enum(50, 1000) + ) + + sigindiffs = {'Off': 0, 'Inverted': 1, 'Input 1 - Input 2': 2, + 'Input 2 - Input 1': 3} + self.add_parameter('signal_input{}_diff'.format(sigin), + label='Input signal subtraction', + set_cmd=partial(self._sigin_setter, + 0, sigin-1, 'diff'), + get_cmd=partial(self._sigin_getter, + sigin-1, 'diff'), + val_mapping=sigindiffs, + vals=vals.Enum(*list(sigindiffs.keys()))) + ######################################## # THE SWEEP ITSELF self.add_parameter('Sweep', @@ -573,7 +733,7 @@ def sweep_correctly_built(self, value): 'scan': 0, # sequential scan 'order': 1, 'settling/time': 1e-6, - 'settling/tc': 7, + 'settling/inaccuracy': 10e-3, 'averaging/sample': 25, 'averaging/tc': 100e-3, 'xmapping': 0, # linear @@ -628,6 +788,52 @@ def _demod_getter(self, demod, setting): return value + def _sigin_setter(self, mode, sigin, setting, value): + """ + General set_cmd for signal input parameters + + This function counts signal inputs in a zero-indexed way. + + Args: + mode (int): 0 means 'call setInt', 1 means 'call setDouble' + demod (int): The signal input in question (0 or 1) + setting (str): The attribute to set, e.g. 'scaling' + value (Union[int, float]): The value to set the attribute to + """ + setstr = '/{}/sigins/{}/{}'.format(self.device, sigin, setting) + + if mode == 0: + self.daq.setInt(setstr, value) + if mode == 1: + self.daq.setDouble(setstr, value) + + def _sigin_getter(self, sigin, setting): + """ + General get_cmd for signal input parameters + + The built-in self.daq.get commands returns a dictionary, but we + want a single value + + This function counts signal inputs in a zero-indexed way. + + returns: + Union[int, float]: In all cases checked so far, a single value + is returned. + """ + querystr = '/{}/sigins/{}/{}'.format(self.device, sigin, setting) + returndict = self.daq.get(querystr) + sigin = str(sigin) + rawvalue = returndict[self.device]['sigins'][sigin][setting]['value'] + + if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: + value = rawvalue[0] + elif isinstance(rawvalue, list) and len(rawvalue) == 1: + value = rawvalue[0] + else: + value = rawvalue + + return value + @staticmethod def NEPBW_to_timeconstant(NEPBW, order): """ @@ -827,7 +1033,8 @@ def print_sweeper_settings(self): """ print('ACQUISITION') toprint = ['sweeper_BWmode', 'sweeper_BW', 'sweeper_order', - 'sweeper_averaging_samples', 'sweeper_averaging_time'] + 'sweeper_averaging_samples', 'sweeper_averaging_time', + 'sweeper_settlingtime', 'sweeper_settlingtc'] for paramname in toprint: parameter = self.parameters[paramname] print(' {}: {} ({})'.format(parameter.label, parameter.get(), @@ -870,7 +1077,7 @@ def print_sweeper_settings(self): print(' Expected sweep time: N/A in auto mode') print(' Sweep timeout: {} ({})'.format(self.sweeper_timeout.get(), 's')) - ready = self.Sweep.sweep_correctly_built + ready = self.sweep_correctly_built print(' Sweep built and ready to execute: {}'.format(ready)) def close(self): From 6f3b39561733d32cfed701b34521d10aee355c65 Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Fri, 27 Jan 2017 17:30:54 +0100 Subject: [PATCH 05/16] fix: Correct multiple dict keys Correct the multiply-defined dict keys of the demodulator trigger. --- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index 65b573d06f9..546593298b9 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -400,10 +400,10 @@ def sweep_correctly_built(self, value): 'Trigger in 4 High': 128, 'Trigger in 4 Low': 64, 'Trigger in 3|4 Rise': 5, - 'Trigger in 3|4': 10, - 'Trigger in 3|4': 15, - 'Trigger in 3|4': 160, - 'Trigger in 3|4': 80} + 'Trigger in 3|4 Fall': 10, + 'Trigger in 3|4 Both': 15, + 'Trigger in 3|4 High': 160, + 'Trigger in 3|4 Low': 80} self.add_parameter('demod{}_trigger'.format(demod), label='Trigger', From 744b54f5eaaf9c8b1c46c4f5301ac947d489ceee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20Darulov=C3=A1?= Date: Tue, 7 Feb 2017 13:53:08 +0100 Subject: [PATCH 06/16] updating ZIUHFLI.py on dummy setup --- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 336 +++++++++++++++--------- 1 file changed, 208 insertions(+), 128 deletions(-) diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index 546593298b9..6ed50585308 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -221,6 +221,22 @@ def _parsesweepdata(self, sweepresult): return tuple(returndata) +class Sweep(MultiParameter): + """ + Class similar to the Sweeper class + """ + + __init__(): + # The __init__ requires that we supply names and shapes, + # but there is no way to know what they could be known at this time. + # They are updated via build_sweep. + super().__init__(name, names=('',), shapes=((1,),), **kwargs) + self._instrument = instrument + + def build_scope(self): + + def get(self): + class ZIUHFLI(Instrument): """ QCoDeS driver for ZI UHF-LI. @@ -274,19 +290,19 @@ def sweep_correctly_built(self, value): self.add_parameter('oscillator1_freq', label='Frequency of oscillator 1', unit='Hz', - set_cmd=partial(self.daq.setDouble, - '/' + device_ID + '/oscs/0/freq'), - get_cmd=partial(self.daq.getDouble, - '/' + device_ID + '/oscs/0/freq'), + set_cmd=partial(self._setter, 'oscs', + 0, 1, 'freq'), + get_cmd=partial(self._getter, 'oscs', + 0, 1, 'freq'), vals=vals.Numbers(0, 600e6)) self.add_parameter('oscillator2_freq', label='Frequency of oscillator 2', unit='Hz', - set_cmd=partial(self.daq.setDouble, - '/' + device_ID + '/oscs/1/freq'), - get_cmd=partial(self.daq.getDouble, - '/' + device_ID + '/oscs/1/freq'), + set_cmd=partial(self._setter, 'oscs', + 1, 1, 'freq'), + get_cmd=partial(self._getter, 'oscs', + 1, 1, 'freq'), vals=vals.Numbers(0, 600e6)) ######################################## @@ -295,38 +311,38 @@ def sweep_correctly_built(self, value): for demod in range(1, 9): self.add_parameter('demod{}_order'.format(demod), label='Filter order', - get_cmd=partial(self._demod_getter, - demod-1, 'order'), - set_cmd=partial(self._demod_setter, - 0, demod-1, 'order'), + get_cmd=partial(self._getter, 'demod', + demod-1, 0, 'order'), + set_cmd=partial(self._setter, 'demod', + demod-1, 0, 'order'), vals=vals.Ints(1, 8) ) self.add_parameter('demod{}_harmonic'.format(demod), label=('Reference frequency multiplication' + ' factor'), - get_cmd=partial(self._demod_getter, - demod-1, 'harmonic'), - set_cmd=partial(self._demod_setter, - 1, demod-1, 'harmonic'), + get_cmd=partial(self._getter, 'demod', + demod-1, 1, 'harmonic'), + set_cmd=partial(self._setter, 'demod', + demod-1, 1, 'harmonic'), vals=vals.Ints(1, 999) ) self.add_parameter('demod{}_timeconstant'.format(demod), label='Filter time constant', - get_cmd=partial(self._demod_getter, - demod-1, 'timeconstant'), - set_cmd=partial(self._demod_setter, - 1, demod-1, 'timeconstant'), + get_cmd=partial(self._getter, 'demod', + demod-1, 1, 'timeconstant'), + set_cmd=partial(self._setter, + demod-1, 1, 'timeconstant'), unit='s' ) self.add_parameter('demod{}_samplerate'.format(demod), label='Sample rate', - get_cmd=partial(self._demod_getter, - demod-1, 'rate'), - set_cmd=partial(self._demod_setter, - 1, demod-1, 'rate'), + get_cmd=partial(self._getter, 'demod', + demod-1, 1, 'rate'), + set_cmd=partial(self._setter, 'demod', + demod-1, 1, 'rate'), unit='Sa/s', docstring=""" Note: the value inserted by the user @@ -338,10 +354,10 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_phaseshift'.format(demod), label='Phase shift', unit='degrees', - get_cmd=partial(self._demod_getter, - demod-1, 'phaseshift'), - set_cmd=partial(self._demod_setter, - 1, demod-1, 'phaseshift') + get_cmd=partial(self._getter, 'demod', + demod-1, 1, 'phaseshift'), + set_cmd=partial(self._setter, 'demod', + demod-1, 1, 'phaseshift') ) # val_mapping for the demodX_signalin parameter @@ -360,30 +376,30 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_signalin'.format(demod), label='Signal input', - get_cmd=partial(self._demod_getter, - demod-1, 'adcselect'), - set_cmd=partial(self._demod_setter, - 0, demod-1, 'adcselect'), + get_cmd=partial(self._getter, 'demod', + demod-1, 0,'adcselect'), + set_cmd=partial(self._setter, 'demod', + demod-1, 0, 'adcselect'), val_mapping=dmsigins, vals=vals.Enum(*list(dmsigins.keys())) ) self.add_parameter('demod{}_sinc'.format(demod), label='Sinc filter', - get_cmd=partial(self._demod_getter, - demod-1, 'sinc'), - set_cmd=partial(self._demod_setter, - 0, demod-1, 'sinc'), + get_cmd=partial(self._getter, 'demod', + demod-1, 0, 'sinc'), + set_cmd=partial(self._setter, 'demod', + demod-1, 0, 'sinc'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') ) self.add_parameter('demod{}_streaming'.format(demod), label='Data streaming', - get_cmd=partial(self._demod_getter, - demod-1, 'enable'), - set_cmd=partial(self._demod_setter, - 0, demod-1, 'enable'), + get_cmd=partial(self._getter, 'demod', + demod-1, 0, 'enable'), + set_cmd=partial(self._setter, 'demod', + demod-1, 0, 'enable'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') ) @@ -407,10 +423,10 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_trigger'.format(demod), label='Trigger', - get_cmd=partial(self._demod_getter, - demod-1, 'trigger'), - set_cmd=partial(self._demod_setter, - 0, demod-1, 'trigger'), + get_cmd=partial(self._getter, 'demod', + demod-1, 0, 'trigger'), + set_cmd=partial(self._setter, 'demod', + demod-1, 0, 'trigger'), val_mapping=dmtrigs, vals=vals.Enum(*list(dmtrigs.keys())) ) @@ -667,36 +683,36 @@ def sweep_correctly_built(self, value): self.add_parameter('signal_input{}_range'.format(sigin), label='Input range', - set_cmd=partial(self._sigin_setter, - 1, sigin-1, 'range'), - get_cmd=partial(self._sigin_getter, - sigin-1, 'range'), + set_cmd=partial(self._setter, 'sigin', + sigin-1, 1, 'range'), + get_cmd=partial(self._getter, 'sigin' + sigin-1, 1, 'range'), unit='V') self.add_parameter('signal_input{}_scaling'.format(sigin), label='Input scaling', - set_cmd=partial(self._sigin_setter, - 1, sigin-1, 'scaling'), - get_cmd=partial(self._sigin_getter, - sigin-1, 'scaling'), + set_cmd=partial(self._setter, 'sigin', + sigin-1, 1, 'scaling'), + get_cmd=partial(self._getter, 'sigin', + sigin-1, 1, 'scaling'), ) self.add_parameter('signal_input{}_AC'.format(sigin), label='AC coupling', - set_cmd=partial(self._sigin_setter, - 0, sigin-1, 'ac'), - get_cmd=partial(self._sigin_getter, - sigin-1, 'ac'), + set_cmd=partial(self._setter,'sigin', + sigin-1, 0, 'ac'), + get_cmd=partial(self._getter, 'sigin', + sigin-1, 0, 'ac'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') ) self.add_parameter('signal_input{}_impedance'.format(sigin), label='Input impedance', - set_cmd=partial(self._sigin_setter, - 0, sigin-1, 'imp50'), - get_cmd=partial(self._sigin_getter, - sigin-1, 'imp50'), + set_cmd=partial(self._setter, 'sigin', + sigin-1, 0, 'imp50'), + get_cmd=partial(self._getter, 'sigin', + sigin-1, 0, 'imp50'), val_mapping={50: 1, 1000: 0}, vals=vals.Enum(50, 1000) ) @@ -705,12 +721,26 @@ def sweep_correctly_built(self, value): 'Input 2 - Input 1': 3} self.add_parameter('signal_input{}_diff'.format(sigin), label='Input signal subtraction', - set_cmd=partial(self._sigin_setter, - 0, sigin-1, 'diff'), - get_cmd=partial(self._sigin_getter, - sigin-1, 'diff'), + set_cmd=partial(self._setter, 'sigin', + sigin-1, 0, 'diff'), + get_cmd=partial(self._getter, sigin, + sigin-1, 0, 'diff'), val_mapping=sigindiffs, vals=vals.Enum(*list(sigindiffs.keys()))) + ######################################## + # SIGNAL OUTPUTS + for sigout in range(1,3) + + self.add_parameter('signal_output{}_Amplitude'.format(sigout), + label='Signal output amplitude', + set_cmd=partial(self._sigout_setter, + 0,sigout-1,'diff') + ) + + + + + ######################################## # THE SWEEP ITSELF @@ -741,19 +771,22 @@ def sweep_correctly_built(self, value): # Set up the sweeper with the above settings self.Sweep.build_sweep() - def _demod_setter(self, mode, demod, setting, value): + def _setter(self, module, number, mode, setting, value): """ - General set_cmd for demodulator parameters + General function to set/send settings to the device. - This function counts demodulators in a zero-indexed way. + The module (e.g demodulator, input, output,..) is counted in a zero + indexed fashion. Args: - mode (int): 0 means 'call setInt', 1 means 'call setDouble' - demod (int): The demodulator in question (0-8) - setting (str): The attribute to set, e.g. 'order' - value (Union[int, float]): The value to set the attribute to + module (str): The module (eg. demodulator, input, output, ..) + to set. + number (int): Module's index + mode (bool): Indicating whether we are asking for an int or double + setting (str): The module's setting to set. + value (int/double): The value to set. """ - setstr = '/{}/demods/{}/{}'.format(self.device, demod, setting) + setstr = '/{}/{}/{}/{}'.format(self.device, module, number, setting) if mode == 0: self.daq.setInt(setstr, value) @@ -761,78 +794,125 @@ def _demod_setter(self, mode, demod, setting, value): self.daq.setDouble(setstr, value) - def _demod_getter(self, demod, setting): + def _getter(self, module, number, mode, setting): """ - General get_cmd for demodulator parameters - - The built-in self.daq.get commands returns a dictionary, but we - want a single value - - This function counts demodulators in a zero-indexed way. + General get function for all parameters (except sweeper parameters). + The module (e.g demodulator, input, output,..) is counted in a zero + indexed fashion. + + Args: + module (str): The module (eg. demodulator, input, output, ..) + we want to know the value of. + number (int): Module's index + mode (bool): Indicating whether we are asking for an int or double + setting (str): The module's setting to set. returns: - Union[int, float]: In all cases checked so far, a single value - is returned. - """ - querystr = '/{}/demods/{}/{}'.format(self.device, demod, setting) - returndict = self.daq.get(querystr) - demod = str(demod) - rawvalue = returndict[self.device]['demods'][demod][setting]['value'] + inquered value - if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: - value = rawvalue[0] - elif isinstance(rawvalue, list) and len(rawvalue) == 1: - value = rawvalue[0] - else: - value = rawvalue + """ + querystr = '/{}/{}/{}/{}'.format(self.device, module, number, setting) + if mode == 0: + value self.daq.getInt(querystr) + if mode == 1: + value = self.daq.getDouble(querystr) return value - def _sigin_setter(self, mode, sigin, setting, value): - """ - General set_cmd for signal input parameters - This function counts signal inputs in a zero-indexed way. - Args: - mode (int): 0 means 'call setInt', 1 means 'call setDouble' - demod (int): The signal input in question (0 or 1) - setting (str): The attribute to set, e.g. 'scaling' - value (Union[int, float]): The value to set the attribute to - """ - setstr = '/{}/sigins/{}/{}'.format(self.device, sigin, setting) + # def _demod_setter(self, mode, demod, setting, value): + # """ + # General set_cmd for demodulator parameters - if mode == 0: - self.daq.setInt(setstr, value) - if mode == 1: - self.daq.setDouble(setstr, value) + # This function counts demodulators in a zero-indexed way. - def _sigin_getter(self, sigin, setting): - """ - General get_cmd for signal input parameters + # Args: + # mode (int): 0 means 'call setInt', 1 means 'call setDouble' + # demod (int): The demodulator in question (0-8) + # setting (str): The attribute to set, e.g. 'order' + # value (Union[int, float]): The value to set the attribute to + # """ + # setstr = '/{}/demods/{}/{}'.format(self.device, demod, setting) - The built-in self.daq.get commands returns a dictionary, but we - want a single value + # if mode == 0: + # self.daq.setInt(setstr, value) + # if mode == 1: + # self.daq.setDouble(setstr, value) - This function counts signal inputs in a zero-indexed way. - returns: - Union[int, float]: In all cases checked so far, a single value - is returned. - """ - querystr = '/{}/sigins/{}/{}'.format(self.device, sigin, setting) - returndict = self.daq.get(querystr) - sigin = str(sigin) - rawvalue = returndict[self.device]['sigins'][sigin][setting]['value'] + # def _demod_getter(self, demod, setting): + # """ + # General get_cmd for demodulator parameters - if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: - value = rawvalue[0] - elif isinstance(rawvalue, list) and len(rawvalue) == 1: - value = rawvalue[0] - else: - value = rawvalue + # The built-in self.daq.get commands returns a dictionary, but we + # want a single value - return value + # This function counts demodulators in a zero-indexed way. + + # returns: + # Union[int, float]: In all cases checked so far, a single value + # is returned. + # """ + # querystr = '/{}/demods/{}/{}'.format(self.device, demod, setting) + # returndict = self.daq.get(querystr) + # demod = str(demod) + # rawvalue = returndict[self.device]['demods'][demod][setting]['value'] + + # if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: + # value = rawvalue[0] + # elif isinstance(rawvalue, list) and len(rawvalue) == 1: + # value = rawvalue[0] + # else: + # value = rawvalue + + # return value + + # def _sigin_setter(self, mode, sigin, setting, value): + # """ + # General set_cmd for signal input parameters + + # This function counts signal inputs in a zero-indexed way. + + # Args: + # mode (int): 0 means 'call setInt', 1 means 'call setDouble' + # demod (int): The signal input in question (0 or 1) + # setting (str): The attribute to set, e.g. 'scaling' + # value (Union[int, float]): The value to set the attribute to + # """ + # setstr = '/{}/sigins/{}/{}'.format(self.device, sigin, setting) + + # if mode == 0: + # self.daq.setInt(setstr, value) + # if mode == 1: + # self.daq.setDouble(setstr, value) + + # def _sigin_getter(self, sigin, setting): + # """ + # General get_cmd for signal input parameters + + # The built-in self.daq.get commands returns a dictionary, but we + # want a single value + + # This function counts signal inputs in a zero-indexed way. + + # returns: + # Union[int, float]: In all cases checked so far, a single value + # is returned. + # """ + # querystr = '/{}/sigins/{}/{}'.format(self.device, sigin, setting) + # returndict = self.daq.get(querystr) + # sigin = str(sigin) + # rawvalue = returndict[self.device]['sigins'][sigin][setting]['value'] + + # if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: + # value = rawvalue[0] + # elif isinstance(rawvalue, list) and len(rawvalue) == 1: + # value = rawvalue[0] + # else: + # value = rawvalue + + # return value @staticmethod def NEPBW_to_timeconstant(NEPBW, order): From 4fa14905dcb26bddc2b8df853d628f0b3002dcc0 Mon Sep 17 00:00:00 2001 From: jana-d Date: Wed, 8 Feb 2017 14:51:10 +0100 Subject: [PATCH 07/16] intermediate commit --- .../Qcodes example with ZI UHF-LI.ipynb | 403 ++++++++---------- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 391 ++++++++--------- 2 files changed, 366 insertions(+), 428 deletions(-) diff --git a/docs/examples/Qcodes example with ZI UHF-LI.ipynb b/docs/examples/Qcodes example with ZI UHF-LI.ipynb index 2101bfd2aa0..de2a728f22a 100644 --- a/docs/examples/Qcodes example with ZI UHF-LI.ipynb +++ b/docs/examples/Qcodes example with ZI UHF-LI.ipynb @@ -2,9 +2,11 @@ "cells": [ { "cell_type": "code", - "execution_count": 15, + "execution_count": 1, "metadata": { "collapsed": false, + "deletable": true, + "editable": true, "scrolled": true }, "outputs": [], @@ -24,7 +26,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "# Prerequisites\n", "\n", @@ -33,70 +38,25 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 2, "metadata": { "collapsed": false, + "deletable": true, + "editable": true, "scrolled": false }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:root:get_parser is set, but will not be used (name demod1_signalin)\n", - "WARNING:root:get_parser is set, but will not be used (name demod1_sinc)\n", - "WARNING:root:get_parser is set, but will not be used (name demod1_streaming)\n", - "WARNING:root:get_parser is set, but will not be used (name demod1_trigger)\n", - "WARNING:root:get_parser is set, but will not be used (name demod2_signalin)\n", - "WARNING:root:get_parser is set, but will not be used (name demod2_sinc)\n", - "WARNING:root:get_parser is set, but will not be used (name demod2_streaming)\n", - "WARNING:root:get_parser is set, but will not be used (name demod2_trigger)\n", - "WARNING:root:get_parser is set, but will not be used (name demod3_signalin)\n", - "WARNING:root:get_parser is set, but will not be used (name demod3_sinc)\n", - "WARNING:root:get_parser is set, but will not be used (name demod3_streaming)\n", - "WARNING:root:get_parser is set, but will not be used (name demod3_trigger)\n", - "WARNING:root:get_parser is set, but will not be used (name demod4_signalin)\n", - "WARNING:root:get_parser is set, but will not be used (name demod4_sinc)\n", - "WARNING:root:get_parser is set, but will not be used (name demod4_streaming)\n", - "WARNING:root:get_parser is set, but will not be used (name demod4_trigger)\n", - "WARNING:root:get_parser is set, but will not be used (name demod5_signalin)\n", - "WARNING:root:get_parser is set, but will not be used (name demod5_sinc)\n", - "WARNING:root:get_parser is set, but will not be used (name demod5_streaming)\n", - "WARNING:root:get_parser is set, but will not be used (name demod5_trigger)\n", - "WARNING:root:get_parser is set, but will not be used (name demod6_signalin)\n", - "WARNING:root:get_parser is set, but will not be used (name demod6_sinc)\n", - "WARNING:root:get_parser is set, but will not be used (name demod6_streaming)\n", - "WARNING:root:get_parser is set, but will not be used (name demod6_trigger)\n", - "WARNING:root:get_parser is set, but will not be used (name demod7_signalin)\n", - "WARNING:root:get_parser is set, but will not be used (name demod7_sinc)\n", - "WARNING:root:get_parser is set, but will not be used (name demod7_streaming)\n", - "WARNING:root:get_parser is set, but will not be used (name demod7_trigger)\n", - "WARNING:root:get_parser is set, but will not be used (name demod8_signalin)\n", - "WARNING:root:get_parser is set, but will not be used (name demod8_sinc)\n", - "WARNING:root:get_parser is set, but will not be used (name demod8_streaming)\n", - "WARNING:root:get_parser is set, but will not be used (name demod8_trigger)\n", - "WARNING:root:get_parser is set, but will not be used (name sweeper_BWmode)\n", - "WARNING:root:get_parser is set, but will not be used (name sweeper_param)\n", - "WARNING:root:get_parser is set, but will not be used (name sweeper_units)\n", - "WARNING:root:get_parser is set, but will not be used (name sweeper_mode)\n", - "WARNING:root:get_parser is set, but will not be used (name sweeper_xmapping)\n", - "WARNING:root:get_parser is set, but will not be used (name signal_input1_AC)\n", - "WARNING:root:get_parser is set, but will not be used (name signal_input1_impedance)\n", - "WARNING:root:get_parser is set, but will not be used (name signal_input1_diff)\n", - "WARNING:root:get_parser is set, but will not be used (name signal_input2_AC)\n", - "WARNING:root:get_parser is set, but will not be used (name signal_input2_impedance)\n", - "WARNING:root:get_parser is set, but will not be used (name signal_input2_diff)\n" - ] - } - ], + "outputs": [], "source": [ "# Instantiate the QCoDeS instrument\n", - "zi = ZIUHFLI('ZIUHFLI', 'dev2189')" + "zi = ZIUHFLI('ZIUHFLI', 'dev2235')" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "# Basic Usage of the ZI UHF-LI\n", "\n", @@ -105,19 +65,13 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Oscillator 2 has frequency: 1050 Hz\n" - ] - } - ], + "outputs": [], "source": [ "zi.oscillator1_freq.set(752.1e3)\n", "print('Oscillator 2 has frequency: {:.0f} Hz'.format(zi.oscillator2_freq.get()))\n", @@ -127,36 +81,23 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Each demodulator has several settings..." ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Available demodulator settings:\n", - "\n", - " demod1_streaming, Data streaming ()\n", - " demod1_order, Filter order ()\n", - " demod1_timeconstant, Filter time constant (s)\n", - " demod1_phaseshift, Phase shift (degrees)\n", - " demod1_samplerate, Sample rate (Sa/s)\n", - " demod1_sinc, Sinc filter ()\n", - " demod1_signalin, Signal input ()\n", - " demod1_trigger, Trigger ()\n", - " demod1_harmonic, Reference frequency multiplication factor ()\n" - ] - } - ], + "outputs": [], "source": [ "print('Available demodulator settings:\\n')\n", "for param in [p for p in zi.parameters if 'demod1' in p]:\n", @@ -165,7 +106,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "# Using the sweeper\n", "\n", @@ -183,9 +127,11 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -194,8 +140,8 @@ "zi.sweeper_param('Osc 1 Frequency')\n", "zi.sweeper_xmapping('lin')\n", "zi.sweeper_start(1e6)\n", - "zi.sweeper_stop(5e6)\n", - "zi.sweeper_samplecount(200)\n", + "zi.sweeper_stop(10e6)\n", + "zi.sweeper_samplecount(100)\n", "zi.sweeper_BWmode('fixed')\n", "zi.sweeper_BW(250)\n", "zi.sweeper_order(4)" @@ -203,33 +149,13 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - " For each sweep point, the demodulator\n", - " filter bandwidth (time constant) may\n", - " be either set automatically, be the\n", - " current demodulator bandwidth or be\n", - " a fixed number; the sweeper_BW\n", - " parameter.\n", - " \r\n", - "\r\n", - "Parameter class:\r\n", - "\r\n", - "* `name` sweeper_BWmode\r\n", - "* `label` Sweeper bandwidth control mode\r\n", - "* `unit` \r\n", - "* `vals` \n" - ] - } - ], + "outputs": [], "source": [ "# I wonder what the sweeper BWmode does...\n", "print(zi.sweeper_BWmode.__doc__)" @@ -237,9 +163,11 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -251,9 +179,11 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -264,46 +194,13 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ACQUISITION\n", - " Sweeper bandwidth control mode: fixed ()\n", - " Fixed bandwidth sweeper bandwidth (NEP): 50.0 ()\n", - " Sweeper filter order: 1 ()\n", - " Minimal no. of samples to average at each sweep point: 25 ()\n", - " Minimal averaging time: 0.1 (s)\n", - " Minimal settling time for the sweeper: 1e-06 (s)\n", - " Sweep filter settling time: 4.605170185988091 (dim. less.)\n", - "HORISONTAL\n", - " Start value of the sweep: 1000000.0\n", - " Stop value of the sweep: 10000000.0\n", - " Units of sweep x-axis: Hz\n", - " Length of the sweep (pts): 25\n", - " Parameter to sweep (sweep x-axis): Osc 1 Frequency\n", - " Sweep mode: Sequential\n", - " Sweep timeout: 20\n", - "VERTICAL\n", - " Signal 1: Demodulator 1: Xrms\n", - " Signal 2: Demodulator 1: Yrms\n", - " Signal 3: Demodulator 1: Rrms\n", - "DEMODULATORS\n", - " Demodulator 1: Filter time constant: 0.000266 (s)\n", - " Demodulator 1: Filter order: 4.000000 ()\n", - " Demodulator 1: Sample rate: 1716.613770 (Sa/s)\n", - "META\n", - " Expected sweep time: 0.9 (s)\n", - " Sweep timeout: 20 (s)\n", - " Sweep built and ready to execute: False\n" - ] - } - ], + "outputs": [], "source": [ "# I wonder what kind of sweep we have made now...\n", "zi.print_sweeper_settings()" @@ -311,46 +208,13 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ACQUISITION\n", - " Sweeper bandwidth control mode: fixed ()\n", - " Fixed bandwidth sweeper bandwidth (NEP): 78.12499999999996 ()\n", - " Sweeper filter order: 4 ()\n", - " Minimal no. of samples to average at each sweep point: 25 ()\n", - " Minimal averaging time: 0.1 (s)\n", - " Minimal settling time for the sweeper: 1e-06 (s)\n", - " Sweep filter settling time: 9.998049677807453 (dim. less.)\n", - "HORISONTAL\n", - " Start value of the sweep: 1000000.0\n", - " Stop value of the sweep: 5000000.0\n", - " Units of sweep x-axis: Hz\n", - " Length of the sweep (pts): 200\n", - " Parameter to sweep (sweep x-axis): Osc 1 Frequency\n", - " Sweep mode: Sequential\n", - " Sweep timeout: 20\n", - "VERTICAL\n", - " Signal 1: Demodulator 1: Xrms\n", - " Signal 2: Demodulator 1: Yrms\n", - " Signal 3: Demodulator 1: Rrms\n", - "DEMODULATORS\n", - " Demodulator 1: Filter time constant: 0.000266 (s)\n", - " Demodulator 1: Filter order: 4.000000 ()\n", - " Demodulator 1: Sample rate: 1716.613770 (Sa/s)\n", - "META\n", - " Expected sweep time: 4.9 (s)\n", - " Sweep timeout: 20 (s)\n", - " Sweep built and ready to execute: True\n" - ] - } - ], + "outputs": [], "source": [ "# Gee, that looks good! Note the last line, the sweep is NOT ready to execute.\n", "zi.Sweep.build_sweep()\n", @@ -360,9 +224,11 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -375,7 +241,9 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": true + "collapsed": true, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -384,7 +252,94 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "plt.figure()\n", + "plt.plot(X, lw=2, color=(0.5, 0, 0.3))\n", + "plt.plot(Y, lw=2, color=(0.5, 0.3, 0))\n", + "plt.plot(R, lw=2, color=(0.3, 0, 0.5))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "# zi.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [], + "source": [ + "print(zi.daq.get('/dev2235/sigouts/0/amplitudes/3'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "zi.list_nodes(\"sigouts/*/amplitudes\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "zi._list_nodes(\"sigouts/*\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import qcodes.utils.validators as vals\n", + "from qcodes.instrument.parameter import ManualParameter" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "bob = ManualParameter(name='testparam', vals=vals.Numbers(1, 2.5))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, "metadata": { "collapsed": false }, @@ -392,50 +347,50 @@ { "data": { "text/plain": [ - "[]" + "420000.0000011528" ] }, - "execution_count": 33, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ - "plt.figure()\n", - "plt.plot(X, lw=2, color=(0.5, 0, 0.3))\n", - "plt.plot(Y, lw=2, color=(0.5, 0.3, 0))\n", - "plt.plot(R, lw=2, color=(0.3, 0, 0.5))" + "zi.oscillator1_freq.get()" ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 10, "metadata": { "collapsed": false }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "420000.0000011528" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "zi.close()" + "zi.oscillator1_freq.get_latest()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": true + "collapsed": false }, "outputs": [], - "source": [] + "source": [ + "bob.get_latest()" + ] } ], "metadata": { diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index 6ed50585308..c9f517cf786 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -221,21 +221,21 @@ def _parsesweepdata(self, sweepresult): return tuple(returndata) -class Sweep(MultiParameter): - """ - Class similar to the Sweeper class - """ +# class Scope(MultiParameter): +# """ +# Class similar to the Sweeper class +# """ - __init__(): - # The __init__ requires that we supply names and shapes, - # but there is no way to know what they could be known at this time. - # They are updated via build_sweep. - super().__init__(name, names=('',), shapes=((1,),), **kwargs) - self._instrument = instrument +# __init__(self): +# # The __init__ requires that we supply names and shapes, +# # but there is no way to know what they could be known at this time. +# # They are updated via build_sweep. +# super().__init__(name, names=('',), shapes=((1,),), **kwargs) +# self._instrument = instrument - def build_scope(self): +# def build_scope(self): - def get(self): +# def get(self): class ZIUHFLI(Instrument): """ @@ -270,6 +270,10 @@ def __init__(self, name, device_ID, api_level=5, **kwargs): self.sweeper = self.daq.sweep() self.sweeper.set('sweep/device', self.device) + # # a dictionary containing parameters whose validators depend on other parameters + # self._dependant_parameters = {"sigouts/offset": "sigouts/imp50", + # } + # this variable enforces building the sweep before using it self._sweep_cb = False @@ -286,24 +290,17 @@ def sweep_correctly_built(self, value): ######################################## # INSTRUMENT PARAMETERS + ######################################## # Oscillators - self.add_parameter('oscillator1_freq', - label='Frequency of oscillator 1', - unit='Hz', - set_cmd=partial(self._setter, 'oscs', - 0, 1, 'freq'), - get_cmd=partial(self._getter, 'oscs', - 0, 1, 'freq'), - vals=vals.Numbers(0, 600e6)) - - self.add_parameter('oscillator2_freq', - label='Frequency of oscillator 2', - unit='Hz', - set_cmd=partial(self._setter, 'oscs', - 1, 1, 'freq'), - get_cmd=partial(self._getter, 'oscs', - 1, 1, 'freq'), - vals=vals.Numbers(0, 600e6)) + for oscs in range(1,3): + self.add_parameter('oscillator{}_freq'.format(oscs), + label='Frequency of oscillator {}'.format(oscs), + unit='Hz', + set_cmd=partial(self._setter, 'oscs', + oscs-1, 1, 'freq'), + get_cmd=partial(self._getter, 'oscs', + oscs-1, 1, 'freq'), + vals=vals.Numbers(0, 600e6)) ######################################## # DEMODULATOR PARAMETERS @@ -311,9 +308,9 @@ def sweep_correctly_built(self, value): for demod in range(1, 9): self.add_parameter('demod{}_order'.format(demod), label='Filter order', - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 0, 'order'), - set_cmd=partial(self._setter, 'demod', + set_cmd=partial(self._setter, 'demods', demod-1, 0, 'order'), vals=vals.Ints(1, 8) ) @@ -321,27 +318,27 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_harmonic'.format(demod), label=('Reference frequency multiplication' + ' factor'), - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 1, 'harmonic'), - set_cmd=partial(self._setter, 'demod', + set_cmd=partial(self._setter, 'demods', demod-1, 1, 'harmonic'), vals=vals.Ints(1, 999) ) self.add_parameter('demod{}_timeconstant'.format(demod), label='Filter time constant', - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 1, 'timeconstant'), - set_cmd=partial(self._setter, + set_cmd=partial(self._setter, 'demods', demod-1, 1, 'timeconstant'), unit='s' ) self.add_parameter('demod{}_samplerate'.format(demod), label='Sample rate', - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 1, 'rate'), - set_cmd=partial(self._setter, 'demod', + set_cmd=partial(self._setter, 'demods', demod-1, 1, 'rate'), unit='Sa/s', docstring=""" @@ -354,9 +351,9 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_phaseshift'.format(demod), label='Phase shift', unit='degrees', - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 1, 'phaseshift'), - set_cmd=partial(self._setter, 'demod', + set_cmd=partial(self._setter, 'demods', demod-1, 1, 'phaseshift') ) @@ -376,9 +373,9 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_signalin'.format(demod), label='Signal input', - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 0,'adcselect'), - set_cmd=partial(self._setter, 'demod', + set_cmd=partial(self._setter, 'demods', demod-1, 0, 'adcselect'), val_mapping=dmsigins, vals=vals.Enum(*list(dmsigins.keys())) @@ -386,9 +383,9 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_sinc'.format(demod), label='Sinc filter', - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 0, 'sinc'), - set_cmd=partial(self._setter, 'demod', + set_cmd=partial(self._setter, 'demods', demod-1, 0, 'sinc'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') @@ -396,9 +393,9 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_streaming'.format(demod), label='Data streaming', - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 0, 'enable'), - set_cmd=partial(self._setter, 'demod', + set_cmd=partial(self._setter, 'demods', demod-1, 0, 'enable'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') @@ -423,14 +420,144 @@ def sweep_correctly_built(self, value): self.add_parameter('demod{}_trigger'.format(demod), label='Trigger', - get_cmd=partial(self._getter, 'demod', + get_cmd=partial(self._getter, 'demods', demod-1, 0, 'trigger'), - set_cmd=partial(self._setter, 'demod', + set_cmd=partial(self._setter, 'demods', demod-1, 0, 'trigger'), val_mapping=dmtrigs, vals=vals.Enum(*list(dmtrigs.keys())) ) + ######################################## + # SIGNAL INPUTS + + for sigin in range(1, 3): + + self.add_parameter('signal_input{}_range'.format(sigin), + label='Input range', + set_cmd=partial(self._setter, 'sigins', + sigin-1, 1, 'range'), + get_cmd=partial(self._getter, 'sigins', + sigin-1, 1, 'range'), + unit='V') + + self.add_parameter('signal_input{}_scaling'.format(sigin), + label='Input scaling', + set_cmd=partial(self._setter, 'sigins', + sigin-1, 1, 'scaling'), + get_cmd=partial(self._getter, 'sigins', + sigin-1, 1, 'scaling'), + ) + + self.add_parameter('signal_input{}_AC'.format(sigin), + label='AC coupling', + set_cmd=partial(self._setter,'sigins', + sigin-1, 0, 'ac'), + get_cmd=partial(self._getter, 'sigins', + sigin-1, 0, 'ac'), + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF') + ) + + self.add_parameter('signal_input{}_impedance'.format(sigin), + label='Input impedance', + set_cmd=partial(self._setter, 'sigins', + sigin-1, 0, 'imp50'), + get_cmd=partial(self._getter, 'sigins', + sigin-1, 0, 'imp50'), + val_mapping={50: 1, 1000: 0}, + vals=vals.Enum(50, 1000) + ) + + sigindiffs = {'Off': 0, 'Inverted': 1, 'Input 1 - Input 2': 2, + 'Input 2 - Input 1': 3} + self.add_parameter('signal_input{}_diff'.format(sigin), + label='Input signal subtraction', + set_cmd=partial(self._setter, 'sigins', + sigin-1, 0, 'diff'), + get_cmd=partial(self._getter, 'sigins', + sigin-1, 0, 'diff'), + val_mapping=sigindiffs, + vals=vals.Enum(*list(sigindiffs.keys()))) + + ######################################## + # SIGNAL OUTPUTS + outputamps = {1: 'amplitudes/3', 2: 'amplitudes/7'} + outputampenable = {1: 'enables/3', 2: 'enables/7'} + + for sigout in range(1,3): + + self.add_parameter('signal_output{}_on'.format(sigout), + label='Turn signal output on and off.', + set_cmd=partial(self._setter, sigout, + sigout-1, 0, 'offset'), + get_cmd=partial(self._getter, sigout, + sigout-1, 0, 'offset'), + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF') ) + + self.add_parameter('signal_output{}_50Ohm'.format(sigout), + label='Switch to turn on 50 Ohm impedance', + set_cmd=partial(self._setter, sigout, + sigout-1, 0, 'offset'), + get_cmd=partial(self._getter, sigout, + sigout-1, 0, 'offset'), + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF') ) + + self.add_parameter('signal_output{}_amplitude'.format(sigout), + label='Signal output amplitude', + set_cmd=partial(self._setter, sigout, + sigout-1, 1, outputamps[sigout]), + get_cmd=partial(self._getter, sigout, + sigout-1, 1, outputamps[sigout]), + vals=vals.Numbers(-1.5, 1.5), + unit='V') + + self.add_parameter('signal_output{}_ampdef'.format(sigout), + parameter_class=ManualParameter, + initial_value='Vpk', + label="Signal output amplitude's definition", + unit='V', + vals=vals.Enum('Vpk','Vrms')) + + self.add_parameter('signal_output{}_range'.format(sigout), + label='Signal output range', + set_cmd=partial(self._setter, sigout, + sigout-1, 1, 'range'), + get_cmd=partial(self._getter, sigout, + sigout-1, 1, 'range'), + vals=vals.Enum(0.075, 0.15, 0.75, 1.5)) + + self.add_parameter('signal_output{}_offset'.format(sigout), + label='Signal output offset', + set_cmd=partial(self._setter, sigout, + sigout-1, 1, 'offset'), + get_cmd=partial(self._getter, sigout, + sigout-1, 1, 'offset'), + vals=vals.Numbers(-1.5, 1.5), + unit='V') + + self.add_parameter('signal_output{}_autorange'.format(sigout), + label='Enable signal output range.', + set_cmd=partial(self._setter, sigout, + sigout-1, 0, 'autorange'), + get_cmd=partial(self._getter, sigout, + sigout-1, 0, 'autorange'), + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF') ) + + self.add_parameter('signal_output{}_enable'.format(sigout), + label="Enable signal output's amplitude.", + set_cmd=partial(self._setter, sigout, + sigout-1, 0, outputampenable[sigout]), + get_cmd=partial(self._getter, sigout, + sigout-1, 0, outputampenable[sigout]), + val_mapping={'ON': 1, 'OFF': 0}, + vals=vals.Enum('ON', 'OFF') ) + + + ######################################## # SWEEPER PARAMETERS @@ -676,69 +803,6 @@ def sweep_correctly_built(self, value): initial_value=600, parameter_class=ManualParameter) - ######################################## - # SIGNAL INPUTS - - for sigin in range(1, 3): - - self.add_parameter('signal_input{}_range'.format(sigin), - label='Input range', - set_cmd=partial(self._setter, 'sigin', - sigin-1, 1, 'range'), - get_cmd=partial(self._getter, 'sigin' - sigin-1, 1, 'range'), - unit='V') - - self.add_parameter('signal_input{}_scaling'.format(sigin), - label='Input scaling', - set_cmd=partial(self._setter, 'sigin', - sigin-1, 1, 'scaling'), - get_cmd=partial(self._getter, 'sigin', - sigin-1, 1, 'scaling'), - ) - - self.add_parameter('signal_input{}_AC'.format(sigin), - label='AC coupling', - set_cmd=partial(self._setter,'sigin', - sigin-1, 0, 'ac'), - get_cmd=partial(self._getter, 'sigin', - sigin-1, 0, 'ac'), - val_mapping={'ON': 1, 'OFF': 0}, - vals=vals.Enum('ON', 'OFF') - ) - - self.add_parameter('signal_input{}_impedance'.format(sigin), - label='Input impedance', - set_cmd=partial(self._setter, 'sigin', - sigin-1, 0, 'imp50'), - get_cmd=partial(self._getter, 'sigin', - sigin-1, 0, 'imp50'), - val_mapping={50: 1, 1000: 0}, - vals=vals.Enum(50, 1000) - ) - - sigindiffs = {'Off': 0, 'Inverted': 1, 'Input 1 - Input 2': 2, - 'Input 2 - Input 1': 3} - self.add_parameter('signal_input{}_diff'.format(sigin), - label='Input signal subtraction', - set_cmd=partial(self._setter, 'sigin', - sigin-1, 0, 'diff'), - get_cmd=partial(self._getter, sigin, - sigin-1, 0, 'diff'), - val_mapping=sigindiffs, - vals=vals.Enum(*list(sigindiffs.keys()))) - ######################################## - # SIGNAL OUTPUTS - for sigout in range(1,3) - - self.add_parameter('signal_output{}_Amplitude'.format(sigout), - label='Signal output amplitude', - set_cmd=partial(self._sigout_setter, - 0,sigout-1,'diff') - ) - - - @@ -813,106 +877,25 @@ def _getter(self, module, number, mode, setting): """ querystr = '/{}/{}/{}/{}'.format(self.device, module, number, setting) if mode == 0: - value self.daq.getInt(querystr) + value = self.daq.getInt(querystr) if mode == 1: value = self.daq.getDouble(querystr) return value + def _list_nodes(self, node): + """ + Returns a list with all nodes in the sub-tree below the specified node. - # def _demod_setter(self, mode, demod, setting, value): - # """ - # General set_cmd for demodulator parameters - - # This function counts demodulators in a zero-indexed way. - - # Args: - # mode (int): 0 means 'call setInt', 1 means 'call setDouble' - # demod (int): The demodulator in question (0-8) - # setting (str): The attribute to set, e.g. 'order' - # value (Union[int, float]): The value to set the attribute to - # """ - # setstr = '/{}/demods/{}/{}'.format(self.device, demod, setting) - - # if mode == 0: - # self.daq.setInt(setstr, value) - # if mode == 1: - # self.daq.setDouble(setstr, value) - - - # def _demod_getter(self, demod, setting): - # """ - # General get_cmd for demodulator parameters - - # The built-in self.daq.get commands returns a dictionary, but we - # want a single value - - # This function counts demodulators in a zero-indexed way. - - # returns: - # Union[int, float]: In all cases checked so far, a single value - # is returned. - # """ - # querystr = '/{}/demods/{}/{}'.format(self.device, demod, setting) - # returndict = self.daq.get(querystr) - # demod = str(demod) - # rawvalue = returndict[self.device]['demods'][demod][setting]['value'] - - # if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: - # value = rawvalue[0] - # elif isinstance(rawvalue, list) and len(rawvalue) == 1: - # value = rawvalue[0] - # else: - # value = rawvalue - - # return value - - # def _sigin_setter(self, mode, sigin, setting, value): - # """ - # General set_cmd for signal input parameters - - # This function counts signal inputs in a zero-indexed way. - - # Args: - # mode (int): 0 means 'call setInt', 1 means 'call setDouble' - # demod (int): The signal input in question (0 or 1) - # setting (str): The attribute to set, e.g. 'scaling' - # value (Union[int, float]): The value to set the attribute to - # """ - # setstr = '/{}/sigins/{}/{}'.format(self.device, sigin, setting) - - # if mode == 0: - # self.daq.setInt(setstr, value) - # if mode == 1: - # self.daq.setDouble(setstr, value) - - # def _sigin_getter(self, sigin, setting): - # """ - # General get_cmd for signal input parameters - - # The built-in self.daq.get commands returns a dictionary, but we - # want a single value - - # This function counts signal inputs in a zero-indexed way. - - # returns: - # Union[int, float]: In all cases checked so far, a single value - # is returned. - # """ - # querystr = '/{}/sigins/{}/{}'.format(self.device, sigin, setting) - # returndict = self.daq.get(querystr) - # sigin = str(sigin) - # rawvalue = returndict[self.device]['sigins'][sigin][setting]['value'] - - # if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: - # value = rawvalue[0] - # elif isinstance(rawvalue, list) and len(rawvalue) == 1: - # value = rawvalue[0] - # else: - # value = rawvalue + Args: + node (str): Module of which you want to know the parameters. + return: + list of sub-nodes + """ + node_list = self.daq.getList('/{}/{}/'.format(self.device, node)) + return node_list - # return value @staticmethod def NEPBW_to_timeconstant(NEPBW, order): @@ -972,13 +955,13 @@ def _get_sweep_time(self): demods = set([sig.split('/')[3] for sig in self._sweeper_signals]) rates = [] for demod in demods: - rates.append(self._demod_getter(demod, 'rate')) + rates.append(self._getter('demods', demod, 1, 'rate')) rate = min(rates) if mode == 'current': tcs = [] for demod in demods: - tcs.append(self._demod_getter(demod, 'timeconstant')) + tcs.append(self._getter('demods', demod, 1, 'timeconstant')) tau_c = max(tcs) From ceb59e33812f377ca398cb8f32a42b1363d227ad Mon Sep 17 00:00:00 2001 From: jana-d Date: Wed, 8 Feb 2017 15:10:52 +0100 Subject: [PATCH 08/16] testing" " --- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index c9f517cf786..d1686b418de 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -45,6 +45,8 @@ def __init__(self, name, instrument, **kwargs): super().__init__(name, names=('',), shapes=((1,),), **kwargs) self._instrument = instrument + print('We rock out at git!') + def build_sweep(self): """ Build a sweep with the current sweep settings. Must be called From f1752604f9b77b81aa9ec57e8846ceeffa5d6479 Mon Sep 17 00:00:00 2001 From: jana-d Date: Mon, 20 Feb 2017 13:31:11 +0100 Subject: [PATCH 09/16] feat: Add parameters Finish Sigout parameters and start adding scope parameters. --- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 518 +++++++++++++++++++++--- 1 file changed, 469 insertions(+), 49 deletions(-) diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index d1686b418de..17ed300aa45 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -45,8 +45,6 @@ def __init__(self, name, instrument, **kwargs): super().__init__(name, names=('',), shapes=((1,),), **kwargs) self._instrument = instrument - print('We rock out at git!') - def build_sweep(self): """ Build a sweep with the current sweep settings. Must be called @@ -223,21 +221,60 @@ def _parsesweepdata(self, sweepresult): return tuple(returndata) -# class Scope(MultiParameter): -# """ -# Class similar to the Sweeper class -# """ -# __init__(self): -# # The __init__ requires that we supply names and shapes, -# # but there is no way to know what they could be known at this time. -# # They are updated via build_sweep. -# super().__init__(name, names=('',), shapes=((1,),), **kwargs) -# self._instrument = instrument -# def build_scope(self): -# def get(self): +class Scope(MultiParameter): + """ + Class similar to the Sweeper class + TO DO: update docs... + Set scopeMode: time or frequency domaine + + """ + def __init__(self, instrument): + # The __init__ requires that we supply names and shapes, + # but there is no way to know what they could be known at this time. + # They are updated via build_scope. + super().__init__(name, names=('',), shapes=((1,),), **kwargs) + self._instrument = instrument + + def build_scope(self): + + scopedict = self._instrument._scopedict + + log.info('Built a scope') + + # define the gridnode + + npts = scopedict['lenght'] + + # self.setpoints = ((sw,),)*len(signals) + # self.shapes = ((npts,),)*len(signals) + + # Send settings to device + + # for (setting, value) in scopedict.items(): + # setting = 'sweep/' + setting + # self._instrument.sweeper.set(setting, value) + + + + # send the settings saved in scopedict to the device + + self._instrument.daq.sync() + self._instrument.scope_correctly_built = True + + def get(self): + """ + read scope data + """ + # daq = self._instrument.daq + # use load_labone_zibin() ? loads data saved in binary format from ziControl + # zi.daq.subscribe('/%s/scopes/0/wave' % device) + # zi.daq.poll(poll_length, poll_timeout, poll_flags, poll_return_flat_dict) + # zi.daq.unsubscribe('*') + + class ZIUHFLI(Instrument): """ @@ -268,17 +305,14 @@ def __init__(self, name, device_ID, api_level=5, **kwargs): super().__init__(name, **kwargs) (self.daq, self.device, self.props) = zhinst.utils.create_api_session(device_ID, api_level) - + # create (instantiate) an instance of each module we will use self.sweeper = self.daq.sweep() self.sweeper.set('sweep/device', self.device) - - # # a dictionary containing parameters whose validators depend on other parameters - # self._dependant_parameters = {"sigouts/offset": "sigouts/imp50", - # } - - # this variable enforces building the sweep before using it + self.scope = self.daq.scopeModule() + # this variable enforces building the sweep and scope before using it self._sweep_cb = False - + self._scope_cb = False + @property def sweep_correctly_built(self): return self._sweep_cd @@ -289,6 +323,16 @@ def sweep_correctly_built(self, value): raise ValueError('sweep_correctly_built') self._sweep_cb = value + @property + def scope_correctly_built(self): + return self._scope_cd + + @scope_correctly_built.setter + def sweep_correctly_built(self, value): + if not isinstance(value, bool): + raise ValueError('scope_correctly_built') + self._scope_cb = value + ######################################## # INSTRUMENT PARAMETERS @@ -491,29 +535,28 @@ def sweep_correctly_built(self, value): self.add_parameter('signal_output{}_on'.format(sigout), label='Turn signal output on and off.', - set_cmd=partial(self._setter, sigout, - sigout-1, 0, 'offset'), - get_cmd=partial(self._getter, sigout, - sigout-1, 0, 'offset'), + set_cmd=partial(self._sigout_setter, + sigout-1, 0, 'on'), + get_cmd=partial(self._sigout_getter, + sigout-1, 0, 'on'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') ) - self.add_parameter('signal_output{}_50Ohm'.format(sigout), + self.add_parameter('signal_output{}_imp50'.format(sigout), label='Switch to turn on 50 Ohm impedance', - set_cmd=partial(self._setter, sigout, - sigout-1, 0, 'offset'), - get_cmd=partial(self._getter, sigout, - sigout-1, 0, 'offset'), + set_cmd=partial(self._sigout_setter, + sigout-1, 0, 'imp50'), + get_cmd=partial(self._sigout_getter, + sigout-1, 0, 'imp50'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') ) self.add_parameter('signal_output{}_amplitude'.format(sigout), label='Signal output amplitude', - set_cmd=partial(self._setter, sigout, + set_cmd=partial(self._sigout_setter, sigout-1, 1, outputamps[sigout]), - get_cmd=partial(self._getter, sigout, + get_cmd=partial(self._sigout_getter, sigout-1, 1, outputamps[sigout]), - vals=vals.Numbers(-1.5, 1.5), unit='V') self.add_parameter('signal_output{}_ampdef'.format(sigout), @@ -521,39 +564,39 @@ def sweep_correctly_built(self, value): initial_value='Vpk', label="Signal output amplitude's definition", unit='V', - vals=vals.Enum('Vpk','Vrms')) + vals=vals.Enum('Vpk','Vrms', 'dBm')) self.add_parameter('signal_output{}_range'.format(sigout), label='Signal output range', - set_cmd=partial(self._setter, sigout, + set_cmd=partial(self._sigout_setter, sigout-1, 1, 'range'), - get_cmd=partial(self._getter, sigout, + get_cmd=partial(self._sigout_getter, sigout-1, 1, 'range'), vals=vals.Enum(0.075, 0.15, 0.75, 1.5)) self.add_parameter('signal_output{}_offset'.format(sigout), label='Signal output offset', - set_cmd=partial(self._setter, sigout, + set_cmd=partial(self._sigout_setter, sigout-1, 1, 'offset'), - get_cmd=partial(self._getter, sigout, + get_cmd=partial(self._sigout_getter, sigout-1, 1, 'offset'), vals=vals.Numbers(-1.5, 1.5), unit='V') self.add_parameter('signal_output{}_autorange'.format(sigout), label='Enable signal output range.', - set_cmd=partial(self._setter, sigout, + set_cmd=partial(self._sigout_setter, sigout-1, 0, 'autorange'), - get_cmd=partial(self._getter, sigout, + get_cmd=partial(self._sigout_getter, sigout-1, 0, 'autorange'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') ) self.add_parameter('signal_output{}_enable'.format(sigout), label="Enable signal output's amplitude.", - set_cmd=partial(self._setter, sigout, + set_cmd=partial(self._sigout_setter, sigout-1, 0, outputampenable[sigout]), - get_cmd=partial(self._getter, sigout, + get_cmd=partial(self._sigout_getter, sigout-1, 0, outputampenable[sigout]), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') ) @@ -805,9 +848,6 @@ def sweep_correctly_built(self, value): initial_value=600, parameter_class=ManualParameter) - - - ######################################## # THE SWEEP ITSELF self.add_parameter('Sweep', @@ -837,6 +877,208 @@ def sweep_correctly_built(self, value): # Set up the sweeper with the above settings self.Sweep.build_sweep() + ######################################## + # SCOPE PARAMETERS + # default parameters: + self._sweepdict = {'time': 1e6, + 'length': 4096 + } + + self.add_parameter('scope_mode', + label="Scope's mode: choose time or frequency domain.", + set_cmd=partial(self._scope_setter, 1, + 'mode'), + get_cmd=partial(self._scope_getter, 'mode'), + val_mapping={'Time Domain': 1, 'Freq Domain FFT': 3}, + vals=vals.Enum('Time Domain', 'Freq Domain FFT') + ) + + sampling_rates = {'1.80 Ghz': 0, + '900 MHz': 1, + '450 MHz': 2, + '225 MHz': 3, + '113 MHz': 4, + '56.2 MHz': 5, + '28.1 MHz': 6, + '14.0 MHz': 7, + '7.03 MHz': 8, + '3.50 MHz': 9, + '1.75 MHz': 10, + '880 kHz': 11, + '440 kHz': 12, + '220 kHz': 13, + '110 kHz': 14, + '54.9 kHz': 15, + '27.5 kHz': 16} + + self.add_parameter('scope_samplingrate', + label="Scope's sampling rate", + set_cmd=partial(self._scope_setter, 0, + 'time'), + get_cmd=partial(self._getter, 'scopes', 0, + 0, 'time'), + val_mapping=sampling_rates, + vals=vals.Enum(*list(sampling_rates.keys())) + ) + + self.add_parameter('scope_length', + label="Scope's length [pts]", + set_cmd=partial(self._scope_setter, 0, + 'length'), + get_cmd=partial(self._getter, 'scopes', 0, + 1, 'length'), + val_mapping=sampling_rates, + vals=vals.Numbers(4096, 128000000) + ) + #scope duration: ManualParameter? validation depends on value of length + # self.add_parameter('scope_duration', + # label="Scope's sduration [s]", + # set_cmd=partial(self._scope_setter, + # 'time'), + # get_cmd=partial(self._scope_getter, 'time'), + # val_mapping=sampling_rates, + # vals=vals.Numbers(2.27e-6,4.660e3) + # ) + + inputselect = {'Signal Input 1': 0, + 'Signal Input 2': 1, + 'Trig Input 1': 2, + 'Trig Input 2': 3, + 'Aux Output 1': 4, + 'Aux Output 2': 5, + 'Aux Output 3': 6, + 'Aux Output 4': 7, + 'Aux In 1 Ch 1': 8, + 'Aux In 1 Ch 2': 9, + 'Osc phi Demod 4': 10, + 'Osc phi Demod 8': 11, + 'AU Cartesian 1': 112, + 'AU Cartesian 2': 113, + 'AU Polar 1': 128, + 'AU Polar 2': 129, + } + for demod in range(1,9): + inputselect['Demod {} X'.format(demod)] = 15+demod + inputselect['Demod {} Y'.format(demod)] = 31+demod + inputselect['Demod {} R'.format(demod)] = 47+demod + inputselect['Demod {} Phase'.format(demod)] = 63+demod + + for channel in range(1,3): + self.add_parameter('scope_channel{}_input'.format(channel), + label="Scope's channel {} input source".format(channel), + set_cmd=partial(self._scope_setter, 0, + '{}/inputselect'.format(channel-1)), + get_cmd=partial(self._getter, 'scopes', 0, + 0, '{}/inputselect'.format(channel-1)), + val_mapping=inputselect, + vals=vals.Enum(*list(inputselect.keys())) + ) + #TO DO: Implement the average filter correctly. Use a parameter, function or method? + + # self.add_parameter('scope_avgfilter', + # label="Scope's Avg Filter", + # set_cmd=partial(self._scope_setter, 1, + # '/averager/'), + # get_cmd=partial(self._scope_getter, + # '/averager/'), + # # val_mapping={'None': 0, 'Exp Moving Avg':}, # to do: double check when it sets it to 1 + # vals=vals.Enum(*list(inputselect.keys())) + # ) + + self.add_parameter('scope_average_weight', + label="Scope Averages", + set_cmd=partial(self._scope_setter, 1, + '/averager/weight'), + get_cmd=partial(self._scope_getter, + '/averager/weight'), + vals=vals.Numbers(min_value=1) + ) + + self.add_function('scope_reset_avg', + call_cmd=partial(self.scope.set, 'scopeModule/averager/restart', 1), + ) + + self.add_parameter('scope_trig_enable', + label="Enable triggering for scope readout", + set_cmd=partial(self._setter, 'scopes', 0, + 0, 'trigenable'), + get_cmd=partial(self._getter, 'scopes', 0, + 0, 'trigenable'), + val_mapping={'ON': 1, 'OFF': 0}, + # vals=vals.Enum('ON', 'OFF') + ) + + self.add_parameter('scope_trig_signal', + label="Trigger signal source", + set_cmd=partial(self._setter, 'scopes', 0, + 0, 'trigchannel'), + get_cmd=partial(self._getter, 'scopes', 0, + 0, 'trigchannel'), + val_mapping=inputselect, + vals=vals.Enum(*list(inputselect.keys())) + ) + + slopes = {'None': 0, 'Rise': 1, 'Fall': 2, 'Both': 3} + + self.add_parameter('scope_trig_slope', + label="Scope's triggering slope", + set_cmd=partial(self._setter, 'scopes', 0, + 0, 'trigslope'), + get_cmd=partial(self._getter, 'scopes', 0, + 0, 'trigslope'), + val_mapping=slopes, + vals=vals.Enum(*list(slopes.keys())) + ) + + self.add_parameter('scope_trig_level', + label="Scope trigger level (deg)", + set_cmd=partial(self._setter, 'scopes', 0, + 1, 'triglevel'), + get_cmd=partial(self._getter, 'scopes', 0, + 1, 'triglevel'), + vals=vals.Numbers() + ) + #TO DO: Find out how to enable/disable trigger lever. GUI's button does not appear in logging. + + self.add_parameter('scope_trig_hystmode', + label="Enable triggering for scope readout.", + set_cmd=partial(self._setter, 'scopes', 0, + 0, 'trighysteresis/mode'), + get_cmd=partial(self._getter, 'scopes', 0, + 0, 'trighysteresis/mode'), + val_mapping={'deg': 0, '%': 1}, + vals=vals.Enum('ON', 'OFF') + ) + + self.add_parameter('scope_trig_hystrelative', + label="Trigger hysteresis, relative value in %", + set_cmd=partial(self._setter, 'scopes', 0, + 1, 'trighysteresis/relative'), + get_cmd=partial(self._getter, 'scopes', 0, + 1, 'trighysteresis/relative'), + # val_mapping= lambda x: 0.01*x, + vals=vals.Numbers(0) + ) + + self.add_parameter('scope_trig_hystabsolute', + label="Trigger hysteresis, absolute value in degrees", + set_cmd=partial(self._setter, 'scopes', 0, + 1, 'trighysteresis/absolute'), + get_cmd=partial(self._getter, 'scopes', 0, + 1, 'trighysteresis/absolute'), + vals=vals.Numbers(0, 20) + ) + + self.add_parameter('scope_trig_holdoffmode', + label="Scope trigger holdoff mode", + set_cmd=partial(self._setter, 'scopes', 0, + 0, 'trigholdoffmode'), + get_cmd=partial(self._getter, 'scopes', 0, + 0, 'trigholdoffmode'), + val_mapping={'s': 0, 'events': 1}, + vals=vals.Enum('s', 'events') + ) + def _setter(self, module, number, mode, setting, value): """ General function to set/send settings to the device. @@ -852,14 +1094,14 @@ def _setter(self, module, number, mode, setting, value): setting (str): The module's setting to set. value (int/double): The value to set. """ - setstr = '/{}/{}/{}/{}'.format(self.device, module, number, setting) + + setstr = '/{}/{}/{}/{}'.format(self.device, module, number, setting) if mode == 0: self.daq.setInt(setstr, value) if mode == 1: self.daq.setDouble(setstr, value) - def _getter(self, module, number, mode, setting): """ General get function for all parameters (except sweeper parameters). @@ -877,6 +1119,7 @@ def _getter(self, module, number, mode, setting): inquered value """ + querystr = '/{}/{}/{}/{}'.format(self.device, module, number, setting) if mode == 0: value = self.daq.getInt(querystr) @@ -885,6 +1128,139 @@ def _getter(self, module, number, mode, setting): return value + def _sigout_setter(self, number, mode, setting, value): + """ + Function to set signal output's settings. Specific setter function is needed as + parameters depend on each other and need to be checked and updated accordingly. + + Args: + number (int): + mode (bool): Indicating whether we are asking for an int or double + setting (str): The module's setting to set. + value (int/float): + """ + + def amp_valid(): + nonlocal value + ampdef_val = self.parameters['signal_output{}_ampdef'.format(number+1)].get() + autorange_val = self.parameters['signal_output{}_autorange'.format(number+1)].get() + + if autorange_val == 'ON': + imp50_val = self.parameters['signal_output{}_imp50'.format(number+1)].get() + imp50_dic = {'OFF': 1.5, 'ON': 0.75} + range_val = imp50_dic[imp50_val] + + else: + range_val = round(self.parameters['signal_output{}_range'.format(number+1)].get(),3) + + amp_val_dict={'Vpk': lambda value: value, + 'Vrms': lambda value: value*sqrt(2), + 'dBm': lambda value: 10**((value-10)/20) } + + if -range_val < amp_val_dict[ampdef_val](value) > range_val: + raise ValueError('Signal Output:' + + ' Amplitude too high for chosen range.') + value = amp_val_dict[ampdef_val](value) + + def offset_valid(): + nonlocal value + nonlocal number + range_val = round(self.parameters['signal_output{}_range'.format(number+1)].get(),3) + amp_val = round(self.parameters['signal_output{}_amplitude'.format(number+1)].get(),3) + autorange_val = self.parameters['signal_output{}_autorange'.format(number+1)].get() + if -range_val< value+amp_val > range_val: + raise ValueError('Signal Output: Offset too high for chosen range.') + + def range_valid(): + nonlocal value + nonlocal number + autorange_val = self.parameters['signal_output{}_autorange'.format(number+1)].get() + imp50_val = self.parameters['signal_output{}_imp50'.format(number+1)].get() + imp50_dic = {'OFF': [1.5, 0.15], 'ON': [0.75, 0.075]} + + if autorange_val == "ON": + raise ValueError('Signal Output :' \ + + ' Cannot set range as autorange is turned on.') + + if value not in imp50_dic[imp50_val]: + raise ValueError('Signal Output: Choose a valid range:' \ + + '[0.75, 0.075] if imp50 is on, [1.5, 0.15] otherwise.') + + def ampdef_valid(): + # check which amplitude definition you can use. dBm is only with imp50 "ON" possible + imp50_val = self.parameters['signal_output{}_imp50'.format(number+1)].get() + imp50_ampdef_dict = {'ON': ['Vpk','Vrms', 'dBm'], 'OFF': ['Vpk','Vrms']} + if value not in imp50_ampdef_dict[imp50_val]: + raise ValueError('Signal Output: Choose a valid amplitude definition' \ + + "['Vpk','Vrms', 'dBm'] if imp50 is on, ['Vpk','Vrms'] otherwise.") + + def imp50_valid(): + amp_val = round(self.parameters['signal_output{}_amplitude'.format(number+1)].get(),3) + amp_val = round(self.parameters['signal_output{}_offset'.format(number+1)].get(),3) + + dynamic_validation = {'range': range_valid, + 'ampdef': ampdef_valid, + 'amplitudes/3': amp_valid, + 'amplitudes/7': amp_valid, + 'offset': offset_valid} + + + def update_range_offset_amp(): + range_val = self.parameters['signal_output{}_range'.format(number+1)].get() + offset_val = self.parameters['signal_output{}_offset'.format(number+1)].get() + amp_val = self.parameters['signal_output{}_amplitude'.format(number+1)].get() + if -range_val < offset_val + amp_val > range_val: + #The GUI would allow higher values but it would clip the signal. + raise ValueError('Signal Output: Amplitude and/or offset out of range.') + + def update_offset(): + self.parameters['signal_output{}_offset'.format(number+1)].get() + + def update_amp(): + self.parameters['signal_output{}_amplitude'.format(number+1)].get() + + def update_range(): + self.parameters['signal_output{}_autorange'.format(number+1)].get() + + # parameters whic will potentially change other parameters + changing_param = {'imp50': [update_range_offset_amp, update_range], + 'autorange': [update_range], + 'range': [update_offset, update_amp], + 'amplitudes/3': [update_range, update_amp], # needed if we are setting in dBm: switchy knob + 'amplitudes/7': [update_range, update_amp], # needed if we are setting in dBm: switchy knob + 'offset': [update_range]} # range can change if autorange is selected + + setstr = '/{}/sigouts/{}/{}'.format(self.device, number, setting) + + if setting in dynamic_validation: + dynamic_validation[setting]() + + if mode == 0: + self.daq.setInt(setstr, value) + if mode == 1: + self.daq.setDouble(setstr, value) + + if setting in changing_param: + [f() for f in changing_param[setting]] + + def _sigout_getter(self, number, mode, setting): + """ + Function to query the settings of signal outputs. Specific setter function is needed as + parameters depend on each other and need to be checked and updated accordingly. + + Args: + number (int): + mode (bool): Indicating whether we are asking for an int or double + setting (str): The module's setting to set. + """ + + querystr = '/{}/sigouts/{}/{}'.format(self.device, number, setting) + if mode == 0: + value = self.daq.getInt(querystr) + if mode == 1: + value = self.daq.getDouble(querystr) + + return value def _list_nodes(self, node): """ @@ -898,7 +1274,6 @@ def _list_nodes(self, node): node_list = self.daq.getList('/{}/{}/'.format(self.device, node)) return node_list - @staticmethod def NEPBW_to_timeconstant(NEPBW, order): """ @@ -1145,6 +1520,51 @@ def print_sweeper_settings(self): ready = self.sweep_correctly_built print(' Sweep built and ready to execute: {}'.format(ready)) + def _scope_setter(self, setting, scopemodule, value): + """ + set_cmd for all scope parameters. The value and setting are saved in + a dictionary which is read by the Scope parameter's build_scope method + and only then sent to the instrument. + + scopemodule: boolean indicating whether it is a setting of the scopeModule or not + + We have two different parameter types: those under /scopes/0/ and those under scopeModule/ + """ + scopemoduledict = {0: '/%s/scopes/0/', 1: 'scopeModule/'} + + key = scopemoduledict[scopemodule] + setting + self._scopedict[key] = value + self.scope_correctly_built = False + + def _scope_getter(self, setting): + """ + get_cmd for scopeModule parameters + + """ + querystr = 'scopeModule/' + setting + returndict = self.scope.get(querystr) + + # The dict may have different 'depths' depending on the parameter. + # The depth is encoded in the setting string (number of '/') + keys = setting.split('/')[1:] + + while keys != []: + key = keys.pop(0) + returndict = returndict[key] + rawvalue = returndict + + if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: + value = rawvalue[0] + elif isinstance(rawvalue, list) and len(rawvalue) == 1: + value = rawvalue[0] + else: + value = rawvalue + + return value + + return value + + def close(self): """ Override of the base class' close function From 1066df6b01d6016da8ff7f34ccdfe201aea0fcfd Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Thu, 23 Feb 2017 12:27:57 +0100 Subject: [PATCH 10/16] Intermediate commit To be cleaned later --- .../Qcodes example with ZI UHF-LI.ipynb | 2349 ++++++++++++++++- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 366 ++- 2 files changed, 2537 insertions(+), 178 deletions(-) diff --git a/docs/examples/Qcodes example with ZI UHF-LI.ipynb b/docs/examples/Qcodes example with ZI UHF-LI.ipynb index de2a728f22a..800c071e7ce 100644 --- a/docs/examples/Qcodes example with ZI UHF-LI.ipynb +++ b/docs/examples/Qcodes example with ZI UHF-LI.ipynb @@ -11,6 +11,7 @@ }, "outputs": [], "source": [ + "%matplotlib notebook\n", "import time\n", "import logging\n", "import numpy as np\n", @@ -65,15 +66,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": { "collapsed": false, "deletable": true, "editable": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Oscillator 2 has frequency: 752100 Hz\n" + ] + } + ], "source": [ - "zi.oscillator1_freq.set(752.1e3)\n", + "zi.oscillator2_freq.set(752.1e3)\n", "print('Oscillator 2 has frequency: {:.0f} Hz'.format(zi.oscillator2_freq.get()))\n", "zi.signal_input1_range(1)\n", "zi.signal_input1_scaling(1)" @@ -91,13 +100,31 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": { "collapsed": false, "deletable": true, "editable": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Available demodulator settings:\n", + "\n", + " demod1_harmonic, Reference frequency multiplication factor ()\n", + " demod1_samplerate, Sample rate (Sa/s)\n", + " demod1_trigger, Trigger ()\n", + " demod1_streaming, Data streaming ()\n", + " demod1_signalin, Signal input ()\n", + " demod1_order, Filter order ()\n", + " demod1_phaseshift, Phase shift (degrees)\n", + " demod1_timeconstant, Filter time constant (s)\n", + " demod1_sinc, Sinc filter ()\n" + ] + } + ], "source": [ "print('Available demodulator settings:\\n')\n", "for param in [p for p in zi.parameters if 'demod1' in p]:\n", @@ -127,7 +154,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 64, "metadata": { "collapsed": false, "deletable": true, @@ -149,13 +176,35 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 65, "metadata": { "collapsed": false, "deletable": true, "editable": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " For each sweep point, the demodulator\n", + " filter bandwidth (time constant) may\n", + " be either set automatically, be the\n", + " current demodulator bandwidth or be\n", + " a fixed number; the sweeper_BW\n", + " parameter.\n", + " \r\n", + "\r\n", + "Parameter class:\r\n", + "\r\n", + "* `name` sweeper_BWmode\r\n", + "* `label` Sweeper bandwidth control mode\r\n", + "* `unit` \r\n", + "* `vals` \n" + ] + } + ], "source": [ "# I wonder what the sweeper BWmode does...\n", "print(zi.sweeper_BWmode.__doc__)" @@ -163,7 +212,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 66, "metadata": { "collapsed": true, "deletable": true, @@ -179,7 +228,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 67, "metadata": { "collapsed": false, "deletable": true, @@ -194,13 +243,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 68, "metadata": { "collapsed": false, "deletable": true, "editable": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ACQUISITION\n", + " Sweeper bandwidth control mode: fixed ()\n", + " Fixed bandwidth sweeper bandwidth (NEP): 50.0 ()\n", + " Sweeper filter order: 1 ()\n", + " Minimal no. of samples to average at each sweep point: 25 ()\n", + " Minimal averaging time: 0.1 (s)\n", + " Minimal settling time for the sweeper: 1e-06 (s)\n", + " Sweep filter settling time: 4.605170185988091 (dim. less.)\n", + "HORISONTAL\n", + " Start value of the sweep: 1000000.0\n", + " Stop value of the sweep: 10000000.0\n", + " Units of sweep x-axis: Hz\n", + " Length of the sweep (pts): 25\n", + " Parameter to sweep (sweep x-axis): Osc 1 Frequency\n", + " Sweep mode: Sequential\n", + " Sweep timeout: 600\n", + "VERTICAL\n", + " Signal 1: Demodulator 1: Xrms\n", + " Signal 2: Demodulator 1: Yrms\n", + " Signal 3: Demodulator 1: Rrms\n", + "DEMODULATORS\n", + " Demodulator 1: Filter time constant: 0.000810 (s)\n", + " Demodulator 1: Filter order: 3.000000 ()\n", + " Demodulator 1: Sample rate: 1716.613770 (Sa/s)\n", + "META\n", + " Expected sweep time: 0.9 (s)\n", + " Sweep timeout: 600 (s)\n", + " Sweep built and ready to execute: False\n" + ] + } + ], "source": [ "# I wonder what kind of sweep we have made now...\n", "zi.print_sweeper_settings()" @@ -208,13 +292,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 69, "metadata": { "collapsed": false, "deletable": true, "editable": true }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ACQUISITION\n", + " Sweeper bandwidth control mode: fixed ()\n", + " Fixed bandwidth sweeper bandwidth (NEP): 250.0 ()\n", + " Sweeper filter order: 4 ()\n", + " Minimal no. of samples to average at each sweep point: 25 ()\n", + " Minimal averaging time: 0.1 (s)\n", + " Minimal settling time for the sweeper: 1e-06 (s)\n", + " Sweep filter settling time: 9.998049677807453 (dim. less.)\n", + "HORISONTAL\n", + " Start value of the sweep: 1000000.0\n", + " Stop value of the sweep: 10000000.0\n", + " Units of sweep x-axis: Hz\n", + " Length of the sweep (pts): 100\n", + " Parameter to sweep (sweep x-axis): Osc 1 Frequency\n", + " Sweep mode: Sequential\n", + " Sweep timeout: 600\n", + "VERTICAL\n", + " Signal 1: Demodulator 1: Xrms\n", + " Signal 2: Demodulator 1: Yrms\n", + " Signal 3: Demodulator 1: Rrms\n", + "DEMODULATORS\n", + " Demodulator 1: Filter time constant: 0.000810 (s)\n", + " Demodulator 1: Filter order: 3.000000 ()\n", + " Demodulator 1: Sample rate: 1716.613770 (Sa/s)\n", + "META\n", + " Expected sweep time: 1.8 (s)\n", + " Sweep timeout: 600 (s)\n", + " Sweep built and ready to execute: True\n" + ] + } + ], "source": [ "# Gee, that looks good! Note the last line, the sweep is NOT ready to execute.\n", "zi.Sweep.build_sweep()\n", @@ -224,7 +343,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 70, "metadata": { "collapsed": false, "deletable": true, @@ -239,7 +358,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 71, "metadata": { "collapsed": true, "deletable": true, @@ -252,13 +371,813 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 72, "metadata": { "collapsed": false, "deletable": true, "editable": true }, - "outputs": [], + "outputs": [ + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('
');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " if (mpl.ratio != 1) {\n", + " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", + " }\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '
');\n", + " var titletext = $(\n", + " '
');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('
');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var backingStore = this.context.backingStorePixelRatio ||\n", + "\tthis.context.webkitBackingStorePixelRatio ||\n", + "\tthis.context.mozBackingStorePixelRatio ||\n", + "\tthis.context.msBackingStorePixelRatio ||\n", + "\tthis.context.oBackingStorePixelRatio ||\n", + "\tthis.context.backingStorePixelRatio || 1;\n", + "\n", + " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband = $('');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width * mpl.ratio);\n", + " canvas.attr('height', height * mpl.ratio);\n", + " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('
')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('
');\n", + " var button = $('');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(el){\n", + " var fig = this\n", + " el.on(\"remove\", function(){\n", + "\tfig.close_ws(fig, {});\n", + " });\n", + "}\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "ename": "AssertionError", + "evalue": "Wait a minute, something is wrong here", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mplt\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfigure\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mtime\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mwave\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdataparser\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mplt\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mplot\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtime\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mwave\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mwave\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mwave\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mdataparser\u001b[0;34m(rawdata)\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0mwave\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'wave'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'channelscaling'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;31m# 0: first of up to four waves. Our UHF only supports two.\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[0mN\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'totalsamples'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m \u001b[1;32massert\u001b[0m \u001b[0mN\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mwave\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'Wait a minute, something is wrong here'\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 11\u001b[0m \u001b[0mdt\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'dt'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mtime\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlinspace\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mdt\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0mN\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mN\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAssertionError\u001b[0m: Wait a minute, something is wrong here" + ] + } + ], + "source": [ + "plt.figure()\n", + "time, wave = dataparser(data)\n", + "plt.plot(time, wave)\n", + "print(wave[0], len(wave))" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ - "bob.get_latest()" + "zi.close()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/qcodes/instrument_drivers/ZI/ZIUHFLI.py b/qcodes/instrument_drivers/ZI/ZIUHFLI.py index 17ed300aa45..46b71fe81fb 100644 --- a/qcodes/instrument_drivers/ZI/ZIUHFLI.py +++ b/qcodes/instrument_drivers/ZI/ZIUHFLI.py @@ -63,6 +63,7 @@ def build_sweep(self): parameter. """ + signals = self._instrument._sweeper_signals sweepdict = self._instrument._sweepdict @@ -228,7 +229,7 @@ class Scope(MultiParameter): """ Class similar to the Sweeper class TO DO: update docs... - Set scopeMode: time or frequency domaine + Set scopeMode: time or frequency domaine """ def __init__(self, instrument): @@ -252,7 +253,7 @@ def build_scope(self): # self.shapes = ((npts,),)*len(signals) # Send settings to device - + # for (setting, value) in scopedict.items(): # setting = 'sweep/' + setting # self._instrument.sweeper.set(setting, value) @@ -312,7 +313,7 @@ def __init__(self, name, device_ID, api_level=5, **kwargs): # this variable enforces building the sweep and scope before using it self._sweep_cb = False self._scope_cb = False - + @property def sweep_correctly_built(self): return self._sweep_cd @@ -521,7 +522,7 @@ def sweep_correctly_built(self, value): label='Input signal subtraction', set_cmd=partial(self._setter, 'sigins', sigin-1, 0, 'diff'), - get_cmd=partial(self._getter, 'sigins', + get_cmd=partial(self._getter, 'sigins', sigin-1, 0, 'diff'), val_mapping=sigindiffs, vals=vals.Enum(*list(sigindiffs.keys()))) @@ -541,7 +542,7 @@ def sweep_correctly_built(self, value): sigout-1, 0, 'on'), val_mapping={'ON': 1, 'OFF': 0}, vals=vals.Enum('ON', 'OFF') ) - + self.add_parameter('signal_output{}_imp50'.format(sigout), label='Switch to turn on 50 Ohm impedance', set_cmd=partial(self._sigout_setter, @@ -576,7 +577,7 @@ def sweep_correctly_built(self, value): self.add_parameter('signal_output{}_offset'.format(sigout), label='Signal output offset', - set_cmd=partial(self._sigout_setter, + set_cmd=partial(self._sigout_setter, sigout-1, 1, 'offset'), get_cmd=partial(self._sigout_getter, sigout-1, 1, 'offset'), @@ -880,83 +881,83 @@ def sweep_correctly_built(self, value): ######################################## # SCOPE PARAMETERS # default parameters: - self._sweepdict = {'time': 1e6, - 'length': 4096 - } self.add_parameter('scope_mode', - label="Scope's mode: choose time or frequency domain.", - set_cmd=partial(self._scope_setter, 1, + label="Scope's mode: time or frequency domain.", + set_cmd=partial(self._scope_setter, 1, 0, 'mode'), get_cmd=partial(self._scope_getter, 'mode'), - val_mapping={'Time Domain': 1, 'Freq Domain FFT': 3}, + val_mapping={'Time Domain': 1, + 'Freq Domain FFT': 3}, vals=vals.Enum('Time Domain', 'Freq Domain FFT') ) - sampling_rates = {'1.80 Ghz': 0, - '900 MHz': 1, - '450 MHz': 2, - '225 MHz': 3, - '113 MHz': 4, - '56.2 MHz': 5, - '28.1 MHz': 6, - '14.0 MHz': 7, - '7.03 MHz': 8, - '3.50 MHz': 9, - '1.75 MHz': 10, - '880 kHz': 11, - '440 kHz': 12, - '220 kHz': 13, - '110 kHz': 14, - '54.9 kHz': 15, - '27.5 kHz': 16} + self._samplingrate_codes = {'1.80 Ghz': 0, + '900 MHz': 1, + '450 MHz': 2, + '225 MHz': 3, + '113 MHz': 4, + '56.2 MHz': 5, + '28.1 MHz': 6, + '14.0 MHz': 7, + '7.03 MHz': 8, + '3.50 MHz': 9, + '1.75 MHz': 10, + '880 kHz': 11, + '440 kHz': 12, + '220 kHz': 13, + '110 kHz': 14, + '54.9 kHz': 15, + '27.5 kHz': 16} self.add_parameter('scope_samplingrate', label="Scope's sampling rate", - set_cmd=partial(self._scope_setter, 0, + set_cmd=partial(self._scope_setter, 0, 0, 'time'), get_cmd=partial(self._getter, 'scopes', 0, 0, 'time'), - val_mapping=sampling_rates, - vals=vals.Enum(*list(sampling_rates.keys())) + val_mapping=self._samplingrate_codes, + vals=vals.Enum(*list(self._samplingrate_codes.keys())) ) self.add_parameter('scope_length', - label="Scope's length [pts]", - set_cmd=partial(self._scope_setter, 0, + label="Length of scope trace (pts)", + set_cmd=partial(self._scope_setter, 0, 1, 'length'), get_cmd=partial(self._getter, 'scopes', 0, 1, 'length'), - val_mapping=sampling_rates, - vals=vals.Numbers(4096, 128000000) + vals=vals.Numbers(4096, 128000000), + get_parser=int ) #scope duration: ManualParameter? validation depends on value of length - # self.add_parameter('scope_duration', - # label="Scope's sduration [s]", - # set_cmd=partial(self._scope_setter, - # 'time'), - # get_cmd=partial(self._scope_getter, 'time'), - # val_mapping=sampling_rates, - # vals=vals.Numbers(2.27e-6,4.660e3) - # ) - + self.add_parameter('scope_duration', + label="Scope trace duration", + set_cmd=partial(self._scope_setter, 0, 0, + 'duration'), + get_cmd=partial(self._scope_getter, + 'duration'), + vals=vals.Numbers(2.27e-6,4.660e3), + unit='s' + ) + inputselect = {'Signal Input 1': 0, - 'Signal Input 2': 1, - 'Trig Input 1': 2, - 'Trig Input 2': 3, - 'Aux Output 1': 4, - 'Aux Output 2': 5, - 'Aux Output 3': 6, - 'Aux Output 4': 7, - 'Aux In 1 Ch 1': 8, - 'Aux In 1 Ch 2': 9, - 'Osc phi Demod 4': 10, - 'Osc phi Demod 8': 11, - 'AU Cartesian 1': 112, - 'AU Cartesian 2': 113, - 'AU Polar 1': 128, - 'AU Polar 2': 129, - } + 'Signal Input 2': 1, + 'Trig Input 1': 2, + 'Trig Input 2': 3, + 'Aux Output 1': 4, + 'Aux Output 2': 5, + 'Aux Output 3': 6, + 'Aux Output 4': 7, + 'Aux In 1 Ch 1': 8, + 'Aux In 1 Ch 2': 9, + 'Osc phi Demod 4': 10, + 'Osc phi Demod 8': 11, + 'AU Cartesian 1': 112, + 'AU Cartesian 2': 113, + 'AU Polar 1': 128, + 'AU Polar 2': 129, + } + for demod in range(1,9): inputselect['Demod {} X'.format(demod)] = 15+demod inputselect['Demod {} Y'.format(demod)] = 31+demod @@ -966,7 +967,7 @@ def sweep_correctly_built(self, value): for channel in range(1,3): self.add_parameter('scope_channel{}_input'.format(channel), label="Scope's channel {} input source".format(channel), - set_cmd=partial(self._scope_setter, 0, + set_cmd=partial(self._scope_setter, 0, 0, '{}/inputselect'.format(channel-1)), get_cmd=partial(self._getter, 'scopes', 0, 0, '{}/inputselect'.format(channel-1)), @@ -987,15 +988,11 @@ def sweep_correctly_built(self, value): self.add_parameter('scope_average_weight', label="Scope Averages", - set_cmd=partial(self._scope_setter, 1, + set_cmd=partial(self._scope_setter, 1, 0, '/averager/weight'), - get_cmd=partial(self._scope_getter, + get_cmd=partial(self._scope_getter, '/averager/weight'), vals=vals.Numbers(min_value=1) - ) - - self.add_function('scope_reset_avg', - call_cmd=partial(self.scope.set, 'scopeModule/averager/restart', 1), ) self.add_parameter('scope_trig_enable', @@ -1005,7 +1002,7 @@ def sweep_correctly_built(self, value): get_cmd=partial(self._getter, 'scopes', 0, 0, 'trigenable'), val_mapping={'ON': 1, 'OFF': 0}, - # vals=vals.Enum('ON', 'OFF') + vals=vals.Enum('ON', 'OFF') ) self.add_parameter('scope_trig_signal', @@ -1015,7 +1012,7 @@ def sweep_correctly_built(self, value): get_cmd=partial(self._getter, 'scopes', 0, 0, 'trigchannel'), val_mapping=inputselect, - vals=vals.Enum(*list(inputselect.keys())) + vals=vals.Enum(*list(inputselect.keys())) ) slopes = {'None': 0, 'Rise': 1, 'Fall': 2, 'Both': 3} @@ -1027,18 +1024,20 @@ def sweep_correctly_built(self, value): get_cmd=partial(self._getter, 'scopes', 0, 0, 'trigslope'), val_mapping=slopes, - vals=vals.Enum(*list(slopes.keys())) + vals=vals.Enum(*list(slopes.keys())) ) + # TODO: figure out how value/percent works for the trigger level self.add_parameter('scope_trig_level', - label="Scope trigger level (deg)", + label="Scope trigger level", set_cmd=partial(self._setter, 'scopes', 0, 1, 'triglevel'), get_cmd=partial(self._getter, 'scopes', 0, 1, 'triglevel'), - vals=vals.Numbers() + vals=vals.Numbers() ) - #TO DO: Find out how to enable/disable trigger lever. GUI's button does not appear in logging. + #TO DO: Find out how to enable/disable trigger lever. GUI's button does + # not appear in logging. self.add_parameter('scope_trig_hystmode', label="Enable triggering for scope readout.", @@ -1047,7 +1046,7 @@ def sweep_correctly_built(self, value): get_cmd=partial(self._getter, 'scopes', 0, 0, 'trighysteresis/mode'), val_mapping={'deg': 0, '%': 1}, - vals=vals.Enum('ON', 'OFF') + vals=vals.Enum('ON', 'OFF') ) self.add_parameter('scope_trig_hystrelative', @@ -1057,18 +1056,20 @@ def sweep_correctly_built(self, value): get_cmd=partial(self._getter, 'scopes', 0, 1, 'trighysteresis/relative'), # val_mapping= lambda x: 0.01*x, - vals=vals.Numbers(0) + vals=vals.Numbers(0) ) self.add_parameter('scope_trig_hystabsolute', - label="Trigger hysteresis, absolute value in degrees", + label="Trigger hysteresis, absolute value", set_cmd=partial(self._setter, 'scopes', 0, 1, 'trighysteresis/absolute'), get_cmd=partial(self._getter, 'scopes', 0, 1, 'trighysteresis/absolute'), - vals=vals.Numbers(0, 20) + vals=vals.Numbers(0, 20) ) + # make this a slave parameter off scope_holdoff_seconds + # and scope_holdoff_events self.add_parameter('scope_trig_holdoffmode', label="Scope trigger holdoff mode", set_cmd=partial(self._setter, 'scopes', 0, @@ -1076,26 +1077,50 @@ def sweep_correctly_built(self, value): get_cmd=partial(self._getter, 'scopes', 0, 0, 'trigholdoffmode'), val_mapping={'s': 0, 'events': 1}, - vals=vals.Enum('s', 'events') + vals=vals.Enum('s', 'events') + ) + + self.add_parameter('scope_trig_holdoffseconds', + label='Scope trigger holdoff', + set_cmd=partial(self._scope_setter, 0, 1, + 'trigholdoff'), + get_cmd=partial(self._getter, 'scopes', 0, + 1, 'trigholdoff'), + unit='s', + vals=vals.Numbers(20e-6, 10) + ) + + self.add_parameter('scope_segments', + label='No. of segments returned by scope', + set_cmd=partial(self._setter, 'scopes', 0, 1, + 'segments/count'), + get_cmd=partial(self._getter, 'scopes', 0, 1, + 'segments/count'), + vals=vals.Ints(1, 32768) + ) + + self.add_function('scope_reset_avg', + call_cmd=partial(self.scope.set, + 'scopeModule/averager/restart', 1), ) def _setter(self, module, number, mode, setting, value): """ General function to set/send settings to the device. - The module (e.g demodulator, input, output,..) is counted in a zero - indexed fashion. + The module (e.g demodulator, input, output,..) number is counted in a + zero indexed fashion. Args: module (str): The module (eg. demodulator, input, output, ..) to set. number (int): Module's index - mode (bool): Indicating whether we are asking for an int or double + mode (bool): Indicating whether we are setting an int or double setting (str): The module's setting to set. value (int/double): The value to set. """ - setstr = '/{}/{}/{}/{}'.format(self.device, module, number, setting) + setstr = '/{}/{}/{}/{}'.format(self.device, module, number, setting) if mode == 0: self.daq.setInt(setstr, value) @@ -1104,16 +1129,18 @@ def _setter(self, module, number, mode, setting, value): def _getter(self, module, number, mode, setting): """ - General get function for all parameters (except sweeper parameters). + General get function for generic parameters. Note that some parameters + use more specialised setter/getters. + + The module (e.g demodulator, input, output,..) number is counted in a + zero indexed fashion. - The module (e.g demodulator, input, output,..) is counted in a zero - indexed fashion. - Args: module (str): The module (eg. demodulator, input, output, ..) we want to know the value of. number (int): Module's index - mode (bool): Indicating whether we are asking for an int or double + mode (int): Indicating whether we are asking for an int or double. + 0: Int, 1: double. setting (str): The module's setting to set. returns: inquered value @@ -1126,12 +1153,13 @@ def _getter(self, module, number, mode, setting): if mode == 1: value = self.daq.getDouble(querystr) + # Weird exception, samplingrate returns a string return value def _sigout_setter(self, number, mode, setting, value): """ - Function to set signal output's settings. Specific setter function is needed as - parameters depend on each other and need to be checked and updated accordingly. + Function to set signal output's settings. Specific setter function is needed as + parameters depend on each other and need to be checked and updated accordingly. Args: number (int): @@ -1149,7 +1177,7 @@ def amp_valid(): imp50_val = self.parameters['signal_output{}_imp50'.format(number+1)].get() imp50_dic = {'OFF': 1.5, 'ON': 0.75} range_val = imp50_dic[imp50_val] - + else: range_val = round(self.parameters['signal_output{}_range'.format(number+1)].get(),3) @@ -1158,15 +1186,15 @@ def amp_valid(): 'dBm': lambda value: 10**((value-10)/20) } if -range_val < amp_val_dict[ampdef_val](value) > range_val: - raise ValueError('Signal Output:' - + ' Amplitude too high for chosen range.') + raise ValueError('Signal Output:' + + ' Amplitude too high for chosen range.') value = amp_val_dict[ampdef_val](value) def offset_valid(): nonlocal value nonlocal number range_val = round(self.parameters['signal_output{}_range'.format(number+1)].get(),3) - amp_val = round(self.parameters['signal_output{}_amplitude'.format(number+1)].get(),3) + amp_val = round(self.parameters['signal_output{}_amplitude'.format(number+1)].get(),3) autorange_val = self.parameters['signal_output{}_autorange'.format(number+1)].get() if -range_val< value+amp_val > range_val: raise ValueError('Signal Output: Offset too high for chosen range.') @@ -1196,7 +1224,7 @@ def ampdef_valid(): def imp50_valid(): amp_val = round(self.parameters['signal_output{}_amplitude'.format(number+1)].get(),3) - amp_val = round(self.parameters['signal_output{}_offset'.format(number+1)].get(),3) + amp_val = round(self.parameters['signal_output{}_offset'.format(number+1)].get(),3) dynamic_validation = {'range': range_valid, 'ampdef': ampdef_valid, @@ -1210,7 +1238,7 @@ def update_range_offset_amp(): offset_val = self.parameters['signal_output{}_offset'.format(number+1)].get() amp_val = self.parameters['signal_output{}_amplitude'.format(number+1)].get() if -range_val < offset_val + amp_val > range_val: - #The GUI would allow higher values but it would clip the signal. + #The GUI would allow higher values but it would clip the signal. raise ValueError('Signal Output: Amplitude and/or offset out of range.') def update_offset(): @@ -1237,7 +1265,7 @@ def update_range(): if mode == 0: self.daq.setInt(setstr, value) - if mode == 1: + if mode == 1: self.daq.setDouble(setstr, value) if setting in changing_param: @@ -1245,7 +1273,7 @@ def update_range(): def _sigout_getter(self, number, mode, setting): """ - Function to query the settings of signal outputs. Specific setter function is needed as + Function to query the settings of signal outputs. Specific setter function is needed as parameters depend on each other and need to be checked and updated accordingly. Args: @@ -1520,55 +1548,137 @@ def print_sweeper_settings(self): ready = self.sweep_correctly_built print(' Sweep built and ready to execute: {}'.format(ready)) - def _scope_setter(self, setting, scopemodule, value): + def _scope_setter(self, scopemodule, mode, setting, value): """ set_cmd for all scope parameters. The value and setting are saved in a dictionary which is read by the Scope parameter's build_scope method and only then sent to the instrument. - scopemodule: boolean indicating whether it is a setting of the scopeModule or not - - We have two different parameter types: those under /scopes/0/ and those under scopeModule/ + Args: + scopemodule (int): Indicates whether this is a setting of the + scopeModule or not. 1: it is a scopeModule setting, + 0: it is not. + mode (int): Indicates whether we are setting an int or a float. + 0: int, 1: float. NOTE: Ignored if scopemodule==1. + setting (str): The setting, e.g. 'length'. + value (Union[int, float, str]): The value to set. """ - scopemoduledict = {0: '/%s/scopes/0/', 1: 'scopeModule/'} - - key = scopemoduledict[scopemodule] + setting - self._scopedict[key] = value + # Because setpoints need to be built self.scope_correctly_built = False + # Some parameters are linked to each other in specific ways + # Therefore, we need special actions for setting these parameters + + SRtranslation = {'kHz': 1e3, 'MHz': 1e6, 'GHz': 1e9, + 'khz': 1e3, 'Mhz': 1e6, 'Ghz': 1e9} + + def setlength(value): + # TODO: add validation. The GUI seems to correect this value + self.daq.setDouble('/{}/scopes/0/length'.format(self.device), + value) + SR_str = self.parameters['scope_samplingrate'].get() + (number, unit) = SR_str.split(' ') + SR = float(number)*SRtranslation[unit] + self.parameters['scope_duration']._save_val(value/SR) + self.daq.setInt('/{}/scopes/0/length'.format(self.device), value) + + def setduration(value): + # TODO: validation? + SR_str = self.parameters['scope_samplingrate'].get() + (number, unit) = SR_str.split(' ') + SR = float(number)*SRtranslation[unit] + N = int(np.round(value*SR)) + self.parameters['scope_length']._save_val(N) + self.parameters['scope_duration']._save_val(value) + self.daq.setInt('/{}/scopes/0/length'.format(self.device), N) + + def setholdoffseconds(value): + self.parameters['scope_trig_holdoffmode'].set('s') + self.daq.setDouble('/{}/scopes/0/trigholdoff'.format(self.device), + value) + + def setsamplingrate(value): + # When the sample rate is changed, the number of points of the trace + # remains unchanged and the duration changes accordingly + newSR_str = dict(zip(self._samplingrate_codes.values(), + self._samplingrate_codes.keys()))[value] + (number, unit) = newSR_str.split(' ') + newSR = float(number)*SRtranslation[unit] + oldSR_str = self.parameters['scope_samplingrate'].get() + (number, unit) = oldSR_str.split(' ') + oldSR = float(number)*SRtranslation[unit] + oldduration = self.parameters['scope_duration'].get() + newduration = oldduration*oldSR/newSR + self.parameters['scope_duration']._save_val(newduration) + self.daq.setInt('/{}/scopes/0/time'.format(self.device), value) + + specialcases = {'length': setlength, + 'duration': setduration, + 'scope_trig_holdoffseconds': setholdoffseconds, + 'time': setsamplingrate} + + if setting in specialcases: + specialcases[setting](value) + self.daq.sync() + return + else: + # We have two different parameter types: those under + # /scopes/0/ and those under scopeModule/ + if scopemodule: + self.daq.set('scopeModule/{}'.format(setting), value) + elif mode == 0: + self.daq.setInt('/{}/scopes/0/{}'.format(self.device, + setting), value) + elif mode == 1: + self.daq.setDouble('/{}/scopes/0/{}'.format(self.device, + setting), value) + return + def _scope_getter(self, setting): """ get_cmd for scopeModule parameters - - """ - querystr = 'scopeModule/' + setting - returndict = self.scope.get(querystr) - - # The dict may have different 'depths' depending on the parameter. - # The depth is encoded in the setting string (number of '/') - keys = setting.split('/')[1:] - - while keys != []: - key = keys.pop(0) - returndict = returndict[key] - rawvalue = returndict - if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: - value = rawvalue[0] - elif isinstance(rawvalue, list) and len(rawvalue) == 1: - value = rawvalue[0] + """ + # There are a few special cases + SRtranslation = {'kHz': 1e3, 'MHz': 1e6, 'GHz': 1e9, + 'khz': 1e3, 'Mhz': 1e6, 'Ghz': 1e9} + + def getduration(): + SR_str = self.parameters['scope_samplingrate'].get() + (number, unit) = SR_str.split(' ') + SR = float(number)*SRtranslation[unit] + N = self.parameters['scope_length'].get() + duration = N/SR + return duration + + specialcases = {'duration': getduration} + + if setting in specialcases: + value = specialcases[setting]() else: - value = rawvalue + querystr = 'scopeModule/' + setting + returndict = self.scope.get(querystr) + # The dict may have different 'depths' depending on the parameter. + # The depth is encoded in the setting string (number of '/') + keys = setting.split('/')[1:] + + while keys != []: + key = keys.pop(0) + returndict = returndict[key] + rawvalue = returndict + + if isinstance(rawvalue, np.ndarray) and len(rawvalue) == 1: + value = rawvalue[0] + elif isinstance(rawvalue, list) and len(rawvalue) == 1: + value = rawvalue[0] + else: + value = rawvalue return value - return value - - def close(self): """ Override of the base class' close function """ self.daq.disconnect() super().close() - From 8b8a3c452ad587171bcdb7d430bd59b4b2530f7e Mon Sep 17 00:00:00 2001 From: WilliamHPNielsen Date: Mon, 27 Feb 2017 18:32:03 +0100 Subject: [PATCH 11/16] feat: Add scope get function Add a get function to the scope multiparameter and document this in the notebook. --- .../Qcodes example with ZI UHF-LI.ipynb | 1450 +++-------------- qcodes/instrument_drivers/ZI/ZIUHFLI.py | 330 +++- 2 files changed, 533 insertions(+), 1247 deletions(-) diff --git a/docs/examples/Qcodes example with ZI UHF-LI.ipynb b/docs/examples/Qcodes example with ZI UHF-LI.ipynb index 800c071e7ce..56d09d9af08 100644 --- a/docs/examples/Qcodes example with ZI UHF-LI.ipynb +++ b/docs/examples/Qcodes example with ZI UHF-LI.ipynb @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 3, "metadata": { "collapsed": false, "deletable": true, @@ -100,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 66, "metadata": { "collapsed": false, "deletable": true, @@ -113,15 +113,15 @@ "text": [ "Available demodulator settings:\n", "\n", - " demod1_harmonic, Reference frequency multiplication factor ()\n", - " demod1_samplerate, Sample rate (Sa/s)\n", - " demod1_trigger, Trigger ()\n", - " demod1_streaming, Data streaming ()\n", - " demod1_signalin, Signal input ()\n", - " demod1_order, Filter order ()\n", " demod1_phaseshift, Phase shift (degrees)\n", " demod1_timeconstant, Filter time constant (s)\n", - " demod1_sinc, Sinc filter ()\n" + " demod1_order, Filter order ()\n", + " demod1_samplerate, Sample rate (Sa/s)\n", + " demod1_sinc, Sinc filter ()\n", + " demod1_harmonic, Reference frequency multiplication factor ()\n", + " demod1_signalin, Signal input ()\n", + " demod1_streaming, Data streaming ()\n", + " demod1_trigger, Trigger ()\n" ] } ], @@ -1199,9 +1199,11 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1220,16 +1222,18 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "4999999.999997584\n" + "10000000.000001563\n" ] } ], @@ -1241,9 +1245,11 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 7, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { @@ -1252,7 +1258,7 @@ "'28.1 MHz'" ] }, - "execution_count": 29, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -1263,19 +1269,21 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 5, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Current scope settings: SR: 28.1 MHz, Length: 281000, duration: 0.010000 (s)\n", - "The length is 562000, 281000\n", - "The duration is 0.0025, 0.0025 and the length is 140500\n", - "Finally, length: 140500 points, duration : 0.005 (s)\n" + "Current scope settings: SR: 56.2 MHz, Length: 70300, duration: 0.001251 (s)\n", + "The length is 562000, 562000\n", + "The duration is 0.005, 0.005 and the length is 281000\n", + "Finally, length: 281000 points, duration : 0.01 (s)\n" ] } ], @@ -1304,32 +1312,79 @@ "print('Finally, length: {} points, duration : {} (s)'.format(zi.scope_length.get_latest(), zi.scope_duration.get_latest()))" ] }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "source": [ + "### Setting up a scope measurement" + ] + }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 4, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, - "outputs": [ - { - "data": { - "text/plain": [ - "0.00125" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], + "source": [ + "# You must define at least everything. Here we go through the GUI settings one-by-one.\n", + "\n", + "# THE CONTROL TAB\n", + "# Horisontal\n", + "zi.scope_mode.set('Time Domain')\n", + "zi.scope_samplingrate.set('7.03 MHz')\n", + "zi.scope_duration.set(1e-3) # seconds\n", + "# Vertical\n", + "zi.scope_channel1_input.set('Signal Input 1')\n", + "zi.scope_channel2_input.set('Signal Input 2')\n", + "zi.scope_channels.set(3) # 1: Chan1 only, 2: Chan2 only, 3: Chan1 + Chan2\n", + "zi.scope.set('scopeModule/averager/weight', int(3)) # Ooh, ooh, experimental, might be like totes wrong\n", + "\n", + "# THE TRIG TAB\n", + "# Trigger\n", + "zi.scope_trig_enable.set('ON')\n", + "zi.scope_trig_signal.set('Signal Input 2')\n", + "zi.scope_trig_slope.set('Rise')\n", + "zi.scope_trig_level.set(20e-3) # Volts if the input is volts\n", + "zi.scope_trig_hystmode.set('absolute')\n", + "zi.scope_trig_hystabsolute.set(3e-3) # Volts if the input is volts\n", + "zi.scope_trig_gating_enable.set('OFF')\n", + "zi.scope_trig_gating_source.set('Trigger In 4 Low')\n", + "zi.scope_trig_holdoffmode.set('s') # QCoDeS currently does not support a holdoff in events. Ask William why. \n", + "zi.scope_trig_holdoffseconds.set(2e-5)\n", + "zi.scope_trig_reference.set(0) # Sets the reference for the delay in percent of the trace duration, i.e. 0 is at the trigger\n", + "zi.scope_trig_delay.set(0) # Sets the delay for the acquisition\n", + "# Segments (NB: Requires purchase of the DIG upgrade)\n", + "zi.scope_segments.set('ON')\n", + "zi.scope_segments_count.set(5)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false, + "deletable": true, + "editable": true + }, + "outputs": [], "source": [ + "# Then the scope must be prepared for the measurement\n", + "zi.Scope.prepare_scope()\n", "\n", - "zi.scope_duration()" + "# And data can be pulled out with the get command\n", + "data = zi.Scope.get()" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 6, "metadata": { "collapsed": false, "deletable": true, @@ -1337,53 +1392,81 @@ }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "540000.0\n" - ] + "data": { + "text/plain": [ + "(array([[-0.00917618, -0.01128768, -0.01334508, ..., 0.00045804,\n", + " -0.00190501, -0.00428636],\n", + " [-0.00591366, -0.00837713, -0.01054263, ..., 0.00361725,\n", + " 0.0012476 , -0.00082889],\n", + " [-0.00481794, -0.00726346, -0.00938114, ..., 0.00477027,\n", + " 0.00245649, 0.00027759],\n", + " [-0.00638182, -0.00870368, -0.01094747, ..., 0.00284436,\n", + " 0.0007177 , -0.00149851],\n", + " [-0.0058118 , -0.00809663, -0.01019629, ..., 0.00378163,\n", + " 0.00163368, -0.00070343]], dtype=float32),\n", + " array([[ 0.01701382, 0.02403538, 0.02907249, ..., 0.0069413 ,\n", + " 0.0069251 , 0.00631101],\n", + " [ 0.01578119, 0.03344741, 0.04541878, ..., -0.00172873,\n", + " -0.00822302, -0.01172128],\n", + " [ 0.01580764, 0.03327226, 0.04495153, ..., -0.0018122 ,\n", + " -0.00830042, -0.01217201],\n", + " [ 0.01676583, 0.0299919 , 0.03889248, ..., 0.00160042,\n", + " -0.00247763, -0.00368527],\n", + " [ 0.01643909, 0.03378678, 0.04534306, ..., -0.00205142,\n", + " -0.00840378, -0.01160002]], dtype=float32))" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "\n", - "zi.scope_duration(300e-6)\n", - "print(zi.scope_length())" + "# And here is a small plotter\n" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { "data": { "text/plain": [ - "'s'" + "int" ] }, - "execution_count": 7, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "zi.scope_trig_holdoffmode()" + "type(zi.Scope._instrument.parameters['scope_channels'].get())" ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 10, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "40\n" + "ename": "NameError", + "evalue": "name 'poll_like_the_example' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m-----------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mexdata\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mpoll_like_the_example\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mexdata\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'/dev2235/scopes/0/wave'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[0;31mNameError\u001b[0m: name 'poll_like_the_example' is not defined" ] } ], @@ -1394,7 +1477,7 @@ }, { "cell_type": "code", - "execution_count": 171, + "execution_count": 8, "metadata": { "collapsed": true, "deletable": true, @@ -1402,78 +1485,104 @@ }, "outputs": [], "source": [ - "def poll_scope_data(length, trig_lvl, segments=1):\n", + "def poll_scope_segments(segments, length, FFT=False):\n", "\n", " device = zi.device\n", "\n", - "\n", " # set the trace length\n", " zi.scope_duration(length)\n", "\n", - " # 'enable' : enable the scope.\n", - " zi.daq.setInt('/%s/scopes/0/enable' % device, 1)\n", + "\n", " # Subscribe to the scope's data.\n", "\n", " zi.daq.setInt('/dev2235/scopes/0/segments/enable', 1)\n", - " zi.scope_segments(segments)\n", + " zi.scope_segments_count(segments)\n", "\n", " zi.daq.setInt('/%s/scopes/0/trigenable' % device, 1)\n", - " zi.daq.setDouble('/%s/scopes/0/triglevel' % device, trig_lvl)\n", + " #zi.daq.setDouble('/%s/scopes/0/triglevel' % device, 200e-3)\n", "\n", - " deadtime = zi.scope_trig_holdoffseconds()\n", + " zi.scope = zi.daq.scopeModule()\n", + " if FFT:\n", + " zi.scope.set('scopeModule/mode', 3)\n", + " else:\n", + " zi.scope.set('scopeModule/mode', 1)\n", "\n", - " # poll_length = (length+deadtime)*segments # (s)\n", - " poll_timeout = (length+deadtime)*2100 # (ms), 1100 instead of 1000 to allow for small delays\n", + " zi.scope.set('scopeModule/averager/weight', 1)\n", + " zi.scope.subscribe('/{}/scopes/0/wave'.format(device))\n", + " \n", + " deadtime = zi.scope_trig_holdoffseconds() # what happens if holdoffmode is events?\n", + "\n", + " measurementtime = segments*(length+deadtime)\n", + " timeout = measurementtime + 1 # one second for latencies etc.\n", "\n", - " zi.daq.subscribe('/%s/scopes/0/wave' % device)\n", " zi.daq.sync()\n", + " # 'enable' : enable the scope. Same as Run.\n", + " zi.daq.setInt('/%s/scopes/0/enable' % device, 1)\n", + " zi.scope.execute()\n", + "\n", + " starttime = time.time()\n", + " timedout = False\n", + "\n", + " while zi.scope.progress() < 1.0: # Wait until the sweep is complete\n", + " time.sleep(0.1)\n", + " progress = zi.scope.progress()\n", + " records = zi.scope.get(\"scopeModule/records\")[\"records\"]\n", + " print(\"Scope module progress: {:.2%}, records: {}, finished: {}.\".format(progress[0], records, zi.scope.finished()))\n", "\n", - " # data = zi.daq.poll(poll_length, poll_timeout, 0, True)\n", - " data = []\n", - " for polls in range(segments):\n", - " try:\n", - " data.append(zi.daq.pollEvent(int(poll_timeout))[0]) # polls a single buffer. What constitutes a buffer?\n", - " except IndexError:\n", - " print('Scope not responding')\n", - " # 'enable' : disable the scope.\n", + " if (time.time() - starttime) > timeout:\n", + " zi.scope.finish()\n", + " timedout = True\n", + " print('Force quitting')\n", + " break\n", + "\n", + " if not timedout:\n", + " data = zi.scope.read()\n", + " else:\n", + " data = None\n", + "\n", + " # disable scope. Same as Stop.\n", " zi.daq.setInt('/%s/scopes/0/enable' % device, 0)\n", - " # unsubscribe scope's data.\n", - " zi.daq.unsubscribe('*')\n", "\n", - " zi.daq.sync()\n", - " return data" + " zi.scope.clear()\n", + "\n", + " return data\n", + "\n" ] }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 9, "metadata": { - "collapsed": true, + "collapsed": false, "deletable": true, "editable": true }, "outputs": [], "source": [ - "def dataparser(rawdata):\n", + "def dataparser(rawdata, scopelen, segments, FFT=False):\n", " \"\"\"\n", - " Parses the output of the poll_scope_data\n", + " Parses the output of the poll_scope_segments\n", " \n", - " Returns a scaled np.array\n", + " Returns a tuple of (ch1wave, ch2wave) where each wave is an N x M numpy array\n", + " where N is the scope length and M is the number of segments\n", " \"\"\"\n", - " data = rawdata['/{}/scopes/0/wave'.format(zi.device)][0]\n", - " wave = data['wave']*data['channelscaling'][0] # 0: first of up to four waves. Our UHF only supports two.\n", - " N = data['totalsamples']\n", - " assert N == len(wave), 'Wait a minute, something is wrong here'\n", - " dt = data['dt']\n", - " time = np.linspace(0, dt*N, N)\n", + " #data = rawdata['/{}/scopes/0'.format(zi.device)][0][0]['wave']\n", + " data = rawdata['{}'.format(zi.device)]['scopes']['0']['wave'][0][0]\n", + "\n", + " print('Got some data of size {}'.format(np.shape(data['wave'][0])))\n", + "\n", + " if not FFT:\n", + " datach1 = data['wave'][0].reshape(segments, scopelen)\n", + " else:\n", + " datach1 = data['wave'][0].reshape(segments, int(scopelen/2))\n", + " #datach1 = data['wave'][0]\n", "\n", - " return time, wave\n", - " \n" + " return datach1" ] }, { "cell_type": "code", - "execution_count": 176, + "execution_count": 10, "metadata": { "collapsed": false, "deletable": true, @@ -1484,1148 +1593,107 @@ "name": "stdout", "output_type": "stream", "text": [ - "Polled data from ZI scope. Got 5 segment(s)\n", - "Segment number 0. Error flag: 0\n", - "Segment number 0. Error flag: 1\n", - "Segment number 2. Error flag: 0\n", - "Segment number 3. Error flag: 0\n", - "Segment number 4. Error flag: 0\n" + "Scope module progress: 100.00%, records: [1], finished: False.\n", + "Got some data of size (51200,)\n" + ] + }, + { + "ename": "ValueError", + "evalue": "cannot reshape array of size 51200 into shape (25,3515)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m-----------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mdata\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mpoll_scope_segments\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msegments\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m1e-3\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mFFT\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0mwavech1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdataparser\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mzi\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mscope_length\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msegments\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mFFT\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mTrue\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 7\u001b[0m \u001b[0mff\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0maxs\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mplt\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msubplots\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0mtoplot\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m-\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m-\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mdataparser\u001b[0;34m(rawdata, scopelen, segments, FFT)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0mdatach1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'wave'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mreshape\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msegments\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mscopelen\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0mdatach1\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m'wave'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mreshape\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0msegments\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mscopelen\u001b[0m\u001b[1;33m/\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 17\u001b[0m \u001b[1;31m#datach1 = data['wave'][0]\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[0;31mValueError\u001b[0m: cannot reshape array of size 51200 into shape (25,3515)" ] } ], "source": [ - "data = poll_scope_data(5e-3, 0.2, segments=5)\n", - "print('Polled data from ZI scope. Got {} segment(s)'.format(len(data)))\n", - "for wave in data:\n", - " print('Segment number {}. Error flag: {}'.format(wave['segmentnumber'], wave['flags']))" + "# Measure and plot\n", + "segments = 25\n", + "\n", + "data = poll_scope_segments(segments, 1e-3, FFT=True)\n", + "\n", + "wavech1 = dataparser(data, zi.scope_length(), segments, FFT=True)\n", + "ff, axs = plt.subplots(3, 1)\n", + "toplot = [0, -2, -1]\n", + "for ind, ax in enumerate(axs):\n", + " ax.plot(wavech1[toplot[ind], :], '-')\n", + " #ax.set_ylim([-2e-3, 1e-3])\n" ] }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 81, "metadata": { - "collapsed": false + "collapsed": true, + "deletable": true, + "editable": true }, - "outputs": [ - { - "data": { - "text/plain": [ - "[[{'blockmarker': 1,\n", - " 'blocknumber': 0,\n", - " 'channelbwlimit': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelenable': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelinput': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelmath': array([0, 0, 0, 0], dtype=int8),\n", - " 'channeloffset': array([ 0., 0., 0., 0.]),\n", - " 'channelscaling': array([ 1.22070314e-05, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00], dtype=float32),\n", - " 'datatransfermode': 0,\n", - " 'dt': 1.1377777777777777e-06,\n", - " 'flags': 0,\n", - " 'sampleformat': 4,\n", - " 'segmentnumber': 1,\n", - " 'sequencenumber': 78763,\n", - " 'timestamp': 160035165608055,\n", - " 'totalsamples': 4400,\n", - " 'triggerenable': 1,\n", - " 'triggerinput': 1,\n", - " 'triggertimestamp': 160035156687777,\n", - " 'wave': array([[11648],\n", - " [11744],\n", - " [11840],\n", - " ..., \n", - " [ 576],\n", - " [ 576],\n", - " [ 560]], dtype=int16)}],\n", - " [{'blockmarker': 1,\n", - " 'blocknumber': 0,\n", - " 'channelbwlimit': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelenable': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelinput': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelmath': array([0, 0, 0, 0], dtype=int8),\n", - " 'channeloffset': array([ 0., 0., 0., 0.]),\n", - " 'channelscaling': array([ 1.22070314e-05, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00], dtype=float32),\n", - " 'datatransfermode': 0,\n", - " 'dt': 1.1377777777777777e-06,\n", - " 'flags': 0,\n", - " 'sampleformat': 4,\n", - " 'segmentnumber': 2,\n", - " 'sequencenumber': 78764,\n", - " 'timestamp': 160035201610119,\n", - " 'totalsamples': 4400,\n", - " 'triggerenable': 1,\n", - " 'triggerinput': 1,\n", - " 'triggertimestamp': 160035192688819,\n", - " 'wave': array([[11728],\n", - " [11824],\n", - " [11920],\n", - " ..., \n", - " [ 560],\n", - " [ 576],\n", - " [ 560]], dtype=int16)}],\n", - " [{'blockmarker': 1,\n", - " 'blocknumber': 0,\n", - " 'channelbwlimit': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelenable': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelinput': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelmath': array([0, 0, 0, 0], dtype=int8),\n", - " 'channeloffset': array([ 0., 0., 0., 0.]),\n", - " 'channelscaling': array([ 1.22070314e-05, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00], dtype=float32),\n", - " 'datatransfermode': 0,\n", - " 'dt': 1.1377777777777777e-06,\n", - " 'flags': 0,\n", - " 'sampleformat': 4,\n", - " 'segmentnumber': 3,\n", - " 'sequencenumber': 78765,\n", - " 'timestamp': 160035237610135,\n", - " 'totalsamples': 4400,\n", - " 'triggerenable': 1,\n", - " 'triggerinput': 1,\n", - " 'triggertimestamp': 160035228688963,\n", - " 'wave': array([[11696],\n", - " [11792],\n", - " [11904],\n", - " ..., \n", - " [ 560],\n", - " [ 560],\n", - " [ 560]], dtype=int16)}],\n", - " [{'blockmarker': 1,\n", - " 'blocknumber': 0,\n", - " 'channelbwlimit': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelenable': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelinput': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelmath': array([0, 0, 0, 0], dtype=int8),\n", - " 'channeloffset': array([ 0., 0., 0., 0.]),\n", - " 'channelscaling': array([ 1.22070314e-05, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00], dtype=float32),\n", - " 'datatransfermode': 0,\n", - " 'dt': 1.1377777777777777e-06,\n", - " 'flags': 0,\n", - " 'sampleformat': 4,\n", - " 'segmentnumber': 4,\n", - " 'sequencenumber': 78766,\n", - " 'timestamp': 160035273610151,\n", - " 'totalsamples': 4400,\n", - " 'triggerenable': 1,\n", - " 'triggerinput': 1,\n", - " 'triggertimestamp': 160035264689452,\n", - " 'wave': array([[11680],\n", - " [11776],\n", - " [11872],\n", - " ..., \n", - " [ 560],\n", - " [ 560],\n", - " [ 560]], dtype=int16)}],\n", - " [{'blockmarker': 1,\n", - " 'blocknumber': 0,\n", - " 'channelbwlimit': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelenable': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelinput': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelmath': array([0, 0, 0, 0], dtype=int8),\n", - " 'channeloffset': array([ 0., 0., 0., 0.]),\n", - " 'channelscaling': array([ 1.22070314e-05, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00], dtype=float32),\n", - " 'datatransfermode': 0,\n", - " 'dt': 1.1377777777777777e-06,\n", - " 'flags': 0,\n", - " 'sampleformat': 4,\n", - " 'segmentnumber': 0,\n", - " 'sequencenumber': 78767,\n", - " 'timestamp': 160035309612447,\n", - " 'totalsamples': 4400,\n", - " 'triggerenable': 1,\n", - " 'triggerinput': 1,\n", - " 'triggertimestamp': 160035300691036,\n", - " 'wave': array([[11760],\n", - " [11872],\n", - " [11952],\n", - " ..., \n", - " [ 560],\n", - " [ 560],\n", - " [ 576]], dtype=int16)}]]" - ] - }, - "execution_count": 128, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "data" + "Ntot = 175750" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 13, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "This is scope setter. Received 5\n", - "This is scope setter. Trying to setInt with time, 5\n" - ] - } - ], - "source": [] + "outputs": [], + "source": [ + "zi.daq.setInt('/%s/scopes/0/enable' % zi.device, 0)" + ] }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 94, "metadata": { - "collapsed": false + "collapsed": true, + "deletable": true, + "editable": true }, - "outputs": [ - { - "ename": "IndexError", - "evalue": "list index out of range", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[1;33m[\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;31mIndexError\u001b[0m: list index out of range" - ] - } - ], + "outputs": [], "source": [ - "[][0]" + "zi.daq.sync()" ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 106, "metadata": { "collapsed": false, - "scrolled": true + "deletable": true, + "editable": true }, "outputs": [ { "data": { "text/plain": [ - "{'/dev2235/scopes/0/wave': [{'blockmarker': 1,\n", - " 'blocknumber': 0,\n", - " 'channelbwlimit': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelenable': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelinput': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelmath': array([0, 0, 0, 0], dtype=int8),\n", - " 'channeloffset': array([ 0., 0., 0., 0.]),\n", - " 'channelscaling': array([ 1.22070314e-05, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00], dtype=float32),\n", - " 'datatransfermode': 0,\n", - " 'dt': 8.888888888888889e-09,\n", - " 'flags': 0,\n", - " 'sampleformat': 4,\n", - " 'segmentnumber': 2,\n", - " 'sequencenumber': 8988,\n", - " 'timestamp': 55424282852019,\n", - " 'totalsamples': 562500,\n", - " 'triggerenable': 1,\n", - " 'triggerinput': 1,\n", - " 'triggertimestamp': 55424273942100,\n", - " 'wave': array([[11648],\n", - " [11712],\n", - " [11680],\n", - " ..., \n", - " [ 560],\n", - " [ 592],\n", - " [ 560]], dtype=int16)},\n", - " {'blockmarker': 1,\n", - " 'blocknumber': 0,\n", - " 'channelbwlimit': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelenable': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelinput': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelmath': array([0, 0, 0, 0], dtype=int8),\n", - " 'channeloffset': array([ 0., 0., 0., 0.]),\n", - " 'channelscaling': array([ 1.22070314e-05, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00], dtype=float32),\n", - " 'datatransfermode': 0,\n", - " 'dt': 8.888888888888889e-09,\n", - " 'flags': 0,\n", - " 'sampleformat': 4,\n", - " 'segmentnumber': 3,\n", - " 'sequencenumber': 8989,\n", - " 'timestamp': 55424300852067,\n", - " 'totalsamples': 562500,\n", - " 'triggerenable': 1,\n", - " 'triggerinput': 1,\n", - " 'triggertimestamp': 55424291942149,\n", - " 'wave': array([[11648],\n", - " [11696],\n", - " [11664],\n", - " ..., \n", - " [ 624],\n", - " [ 592],\n", - " [ 592]], dtype=int16)},\n", - " {'blockmarker': 1,\n", - " 'blocknumber': 0,\n", - " 'channelbwlimit': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelenable': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelinput': array([1, 0, 0, 0], dtype=int8),\n", - " 'channelmath': array([0, 0, 0, 0], dtype=int8),\n", - " 'channeloffset': array([ 0., 0., 0., 0.]),\n", - " 'channelscaling': array([ 1.22070314e-05, 0.00000000e+00, 0.00000000e+00,\n", - " 0.00000000e+00], dtype=float32),\n", - " 'datatransfermode': 0,\n", - " 'dt': 8.888888888888889e-09,\n", - " 'flags': 0,\n", - " 'sampleformat': 4,\n", - " 'segmentnumber': 4,\n", - " 'sequencenumber': 8990,\n", - " 'timestamp': 55424318854195,\n", - " 'totalsamples': 562500,\n", - " 'triggerenable': 1,\n", - " 'triggerinput': 1,\n", - " 'triggertimestamp': 55424309944268,\n", - " 'wave': array([[11744],\n", - " [11712],\n", - " [11728],\n", - " ..., \n", - " [ 560],\n", - " [ 576],\n", - " [ 560]], dtype=int16)}]}" + "2" ] }, - "execution_count": 66, + "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "data" + "zi.daq.getInt('/dev2235/scopes/0/channel')" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 8, "metadata": { "collapsed": false, "deletable": true, - "editable": true, - "scrolled": false - }, - "outputs": [ - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support.' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " this.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
')\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('
');\n", + " var button = $('');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(el){\n", + " var fig = this\n", + " el.on(\"remove\", function(){\n", + "\tfig.close_ws(fig, {});\n", + " });\n", + "}\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "window.mpl = {};\n", + "\n", + "\n", + "mpl.get_websocket_type = function() {\n", + " if (typeof(WebSocket) !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof(MozWebSocket) !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert('Your browser does not have WebSocket support.' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.');\n", + " };\n", + "}\n", + "\n", + "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = (this.ws.binaryType != undefined);\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById(\"mpl-warnings\");\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent = (\n", + " \"This browser does not support binary websocket messages. \" +\n", + " \"Performance may be slow.\");\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = $('
');\n", + " this._root_extra_style(this.root)\n", + " this.root.attr('style', 'display: inline-block');\n", + "\n", + " $(parent_element).append(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", + " fig.send_message(\"send_image_mode\", {});\n", + " if (mpl.ratio != 1) {\n", + " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", + " }\n", + " fig.send_message(\"refresh\", {});\n", + " }\n", + "\n", + " this.imageObj.onload = function() {\n", + " if (fig.image_mode == 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function() {\n", + " this.ws.close();\n", + " }\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "}\n", + "\n", + "mpl.figure.prototype._init_header = function() {\n", + " var titlebar = $(\n", + " '
');\n", + " var titletext = $(\n", + " '
');\n", + " titlebar.append(titletext)\n", + " this.root.append(titlebar);\n", + " this.header = titletext[0];\n", + "}\n", + "\n", + "\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._init_canvas = function() {\n", + " var fig = this;\n", + "\n", + " var canvas_div = $('
');\n", + "\n", + " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", + "\n", + " function canvas_keyboard_event(event) {\n", + " return fig.key_event(event, event['data']);\n", + " }\n", + "\n", + " canvas_div.keydown('key_press', canvas_keyboard_event);\n", + " canvas_div.keyup('key_release', canvas_keyboard_event);\n", + " this.canvas_div = canvas_div\n", + " this._canvas_extra_style(canvas_div)\n", + " this.root.append(canvas_div);\n", + "\n", + " var canvas = $('');\n", + " canvas.addClass('mpl-canvas');\n", + " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", + "\n", + " this.canvas = canvas[0];\n", + " this.context = canvas[0].getContext(\"2d\");\n", + "\n", + " var backingStore = this.context.backingStorePixelRatio ||\n", + "\tthis.context.webkitBackingStorePixelRatio ||\n", + "\tthis.context.mozBackingStorePixelRatio ||\n", + "\tthis.context.msBackingStorePixelRatio ||\n", + "\tthis.context.oBackingStorePixelRatio ||\n", + "\tthis.context.backingStorePixelRatio || 1;\n", + "\n", + " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband = $('');\n", + " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", + "\n", + " var pass_mouse_events = true;\n", + "\n", + " canvas_div.resizable({\n", + " start: function(event, ui) {\n", + " pass_mouse_events = false;\n", + " },\n", + " resize: function(event, ui) {\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " stop: function(event, ui) {\n", + " pass_mouse_events = true;\n", + " fig.request_resize(ui.size.width, ui.size.height);\n", + " },\n", + " });\n", + "\n", + " function mouse_event_fn(event) {\n", + " if (pass_mouse_events)\n", + " return fig.mouse_event(event, event['data']);\n", + " }\n", + "\n", + " rubberband.mousedown('button_press', mouse_event_fn);\n", + " rubberband.mouseup('button_release', mouse_event_fn);\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband.mousemove('motion_notify', mouse_event_fn);\n", + "\n", + " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", + " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", + "\n", + " canvas_div.on(\"wheel\", function (event) {\n", + " event = event.originalEvent;\n", + " event['data'] = 'scroll'\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " mouse_event_fn(event);\n", + " });\n", + "\n", + " canvas_div.append(canvas);\n", + " canvas_div.append(rubberband);\n", + "\n", + " this.rubberband = rubberband;\n", + " this.rubberband_canvas = rubberband[0];\n", + " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", + " this.rubberband_context.strokeStyle = \"#000000\";\n", + "\n", + " this._resize_canvas = function(width, height) {\n", + " // Keep the size of the canvas, canvas container, and rubber band\n", + " // canvas in synch.\n", + " canvas_div.css('width', width)\n", + " canvas_div.css('height', height)\n", + "\n", + " canvas.attr('width', width * mpl.ratio);\n", + " canvas.attr('height', height * mpl.ratio);\n", + " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", + "\n", + " rubberband.attr('width', width);\n", + " rubberband.attr('height', height);\n", + " }\n", + "\n", + " // Set the figure to an initial 600x600px, this will subsequently be updated\n", + " // upon first draw.\n", + " this._resize_canvas(600, 600);\n", + "\n", + " // Disable right mouse context menu.\n", + " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", + " return false;\n", + " });\n", + "\n", + " function set_focus () {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "}\n", + "\n", + "mpl.figure.prototype._init_toolbar = function() {\n", + " var fig = this;\n", + "\n", + " var nav_element = $('
')\n", + " nav_element.attr('style', 'width: 100%');\n", + " this.root.append(nav_element);\n", + "\n", + " // Define a callback function for later on.\n", + " function toolbar_event(event) {\n", + " return fig.toolbar_button_onclick(event['data']);\n", + " }\n", + " function toolbar_mouse_event(event) {\n", + " return fig.toolbar_button_onmouseover(event['data']);\n", + " }\n", + "\n", + " for(var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " // put a spacer in here.\n", + " continue;\n", + " }\n", + " var button = $('