From 704541188eb7e4a2871d2c87d1b17678dfed6954 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 12 Jun 2017 14:07:04 +0200 Subject: [PATCH 01/56] fix: refactor Instrument to avoid removing methods from channels --- qcodes/instrument/base.py | 417 ++++++++++++++++++++--------------- qcodes/instrument/channel.py | 33 +-- 2 files changed, 238 insertions(+), 212 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index 24f1efcaa523..f65bb2cddcf9 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -12,7 +12,241 @@ from .function import Function -class Instrument(Metadatable, DelegateAttributes): +class InstrumentBase(Metadatable, DelegateAttributes): + """ + Base class for all QCodes instruments and instrument channels + + Args: + name (str): an identifier for this instrument, particularly for + attaching it to a Station. + + metadata (Optional[Dict]): additional static metadata to add to this + instrument's JSON snapshot. + + + Attributes: + name (str): an identifier for this instrument, particularly for + attaching it to a Station. + + parameters (Dict[Parameter]): All the parameters supported by this + instrument. Usually populated via ``add_parameter`` + + functions (Dict[Function]): All the functions supported by this + instrument. Usually populated via ``add_function`` + """ + def __init__(self, name, **kwargs): + self.name = str(name) + self.parameters = {} + self.functions = {} + self.submodules = {} + super().__init__(**kwargs) + + def add_parameter(self, name, parameter_class=StandardParameter, + **kwargs): + """ + Bind one Parameter to this instrument. + + Instrument subclasses can call this repeatedly in their ``__init__`` + for every real parameter of the instrument. + + In this sense, parameters are the state variables of the instrument, + anything the user can set and/or get + + Args: + name (str): How the parameter will be stored within + ``instrument.parameters`` and also how you address it using the + shortcut methods: ``instrument.set(param_name, value)`` etc. + + parameter_class (Optional[type]): You can construct the parameter + out of any class. Default ``StandardParameter``. + + **kwargs: constructor arguments for ``parameter_class``. + + Raises: + KeyError: if this instrument already has a parameter with this + name. + """ + if name in self.parameters: + raise KeyError('Duplicate parameter name {}'.format(name)) + param = parameter_class(name=name, instrument=self, **kwargs) + self.parameters[name] = param + + def add_function(self, name, **kwargs): + """ + Bind one Function to this instrument. + + Instrument subclasses can call this repeatedly in their ``__init__`` + for every real function of the instrument. + + This functionality is meant for simple cases, principally things that + map to simple commands like '\*RST' (reset) or those with just a few + arguments. It requires a fixed argument count, and positional args + only. If your case is more complicated, you're probably better off + simply making a new method in your ``Instrument`` subclass definition. + + Args: + name (str): how the Function will be stored within + ``instrument.Functions`` and also how you address it using the + shortcut methods: ``instrument.call(func_name, *args)`` etc. + + **kwargs: constructor kwargs for ``Function`` + + Raises: + KeyError: if this instrument already has a function with this + name. + """ + if name in self.functions: + raise KeyError('Duplicate function name {}'.format(name)) + func = Function(name=name, instrument=self, **kwargs) + self.functions[name] = func + + def snapshot_base(self, update=False): + """ + State of the instrument as a JSON-compatible dict. + + Args: + update (bool): If True, update the state by querying the + instrument. If False, just use the latest values in memory. + + Returns: + dict: base snapshot + """ + snap = {'parameters': dict((name, param.snapshot(update=update)) + for name, param in self.parameters.items()), + 'functions': dict((name, func.snapshot(update=update)) + for name, func in self.functions.items()), + '__class__': full_class(self), + } + for attr in set(self._meta_attrs): + if hasattr(self, attr): + snap[attr] = getattr(self, attr) + return snap + + def print_readable_snapshot(self, update=False, max_chars=80): + """ + Prints a readable version of the snapshot. + The readable snapshot includes the name, value and unit of each + parameter. + A convenience function to quickly get an overview of the status of an instrument. + + Args: + update (bool) : If True, update the state by querying the + instrument. If False, just use the latest values in memory. + This argument gets passed to the snapshot function. + max_chars (int) : the maximum number of characters per line. The + readable snapshot will be cropped if this value is exceeded. + Defaults to 80 to be consistent with default terminal width. + """ + floating_types = (float, np.integer, np.floating) + snapshot = self.snapshot(update=update) + + par_lengths = [len(p) for p in snapshot['parameters']] + + # Min of 50 is to prevent a super long parameter name to break this + # function + par_field_len = min(max(par_lengths)+1, 50) + + print(self.name + ':') + print('{0:<{1}}'.format('\tparameter ', par_field_len) + 'value') + print('-'*80) + for par in sorted(snapshot['parameters']): + name = snapshot['parameters'][par]['name'] + msg = '{0:<{1}}:'.format(name, par_field_len) + val = snapshot['parameters'][par]['value'] + unit = snapshot['parameters'][par].get('unit', None) + if unit is None: + # this may be a multi parameter + unit = snapshot['parameters'][par].get('units', None) + if isinstance(val, floating_types): + msg += '\t{:.5g} '.format(val) + else: + msg += '\t{} '.format(val) + if unit is not '': # corresponds to no unit + msg += '({})'.format(unit) + # Truncate the message if it is longer than max length + if len(msg) > max_chars and not max_chars == -1: + msg = msg[0:max_chars-3] + '...' + print(msg) + + # + # shortcuts to parameters & setters & getters # + # + # instrument['someparam'] === instrument.parameters['someparam'] # + # instrument.someparam === instrument.parameters['someparam'] # + # instrument.get('someparam') === instrument['someparam'].get() # + # etc... # + # + delegate_attr_dicts = ['parameters', 'functions'] + + def __getitem__(self, key): + """Delegate instrument['name'] to parameter or function 'name'.""" + try: + return self.parameters[key] + except KeyError: + return self.functions[key] + + def set(self, param_name, value): + """ + Shortcut for setting a parameter from its name and new value. + + Args: + param_name (str): The name of a parameter of this instrument. + value (any): The new value to set. + """ + self.parameters[param_name].set(value) + + def get(self, param_name): + """ + Shortcut for getting a parameter from its name. + + Args: + param_name (str): The name of a parameter of this instrument. + + Returns: + any: The current value of the parameter. + """ + return self.parameters[param_name].get() + + def call(self, func_name, *args): + """ + Shortcut for calling a function from its name. + + Args: + func_name (str): The name of a function of this instrument. + *args: any arguments to the function. + + Returns: + any: The return value of the function. + """ + return self.functions[func_name].call(*args) + + def __getstate__(self): + """Prevent pickling instruments, and give a nice error message.""" + raise RuntimeError( + 'Pickling %s. qcodes Instruments should not be pickled. Likely this means you ' + 'were trying to use a local instrument (defined with ' + 'server_name=None) in a background Loop. Local instruments can ' + 'only be used in Loops with background=False.' % self.name) + + def validate_status(self, verbose=False): + """ Validate the values of all gettable parameters + + The validation is done for all parameters that have both a get and + set method. + + Arguments: + verbose (bool): If True, then information about the parameters that are being check is printed. + + """ + for k, p in self.parameters.items(): + if p.has_get and p.has_set: + value = p.get() + if verbose: + print('validate_status: param %s: %s' % (k, value)) + p.validate(value) + + +class Instrument(InstrumentBase): """ Base class for all QCodes instruments. @@ -49,7 +283,7 @@ def __init__(self, name, **kwargs): if kwargs.pop('server_name', False): warnings.warn("server_name argument not supported any more", stacklevel=0) - super().__init__(**kwargs) + super().__init__(name, **kwargs) self.parameters = {} self.functions = {} self.submodules = {} @@ -252,64 +486,6 @@ def find_instrument(cls, name, instrument_class=None): return ins - def add_parameter(self, name, parameter_class=StandardParameter, - **kwargs): - """ - Bind one Parameter to this instrument. - - Instrument subclasses can call this repeatedly in their ``__init__`` - for every real parameter of the instrument. - - In this sense, parameters are the state variables of the instrument, - anything the user can set and/or get - - Args: - name (str): How the parameter will be stored within - ``instrument.parameters`` and also how you address it using the - shortcut methods: ``instrument.set(param_name, value)`` etc. - - parameter_class (Optional[type]): You can construct the parameter - out of any class. Default ``StandardParameter``. - - **kwargs: constructor arguments for ``parameter_class``. - - Raises: - KeyError: if this instrument already has a parameter with this - name. - """ - if name in self.parameters: - raise KeyError('Duplicate parameter name {}'.format(name)) - param = parameter_class(name=name, instrument=self, **kwargs) - self.parameters[name] = param - - def add_function(self, name, **kwargs): - """ - Bind one Function to this instrument. - - Instrument subclasses can call this repeatedly in their ``__init__`` - for every real function of the instrument. - - This functionality is meant for simple cases, principally things that - map to simple commands like '\*RST' (reset) or those with just a few - arguments. It requires a fixed argument count, and positional args - only. If your case is more complicated, you're probably better off - simply making a new method in your ``Instrument`` subclass definition. - - Args: - name (str): how the Function will be stored within - ``instrument.Functions`` and also how you address it using the - shortcut methods: ``instrument.call(func_name, *args)`` etc. - - **kwargs: constructor kwargs for ``Function`` - - Raises: - KeyError: if this instrument already has a function with this - name. - """ - if name in self.functions: - raise KeyError('Duplicate function name {}'.format(name)) - func = Function(name=name, instrument=self, **kwargs) - self.functions[name] = func def add_submodule(self, name, submodule): """ @@ -365,52 +541,6 @@ def snapshot_base(self, update=False): snap[attr] = getattr(self, attr) return snap - def print_readable_snapshot(self, update=False, max_chars=80): - """ - Prints a readable version of the snapshot. - The readable snapshot includes the name, value and unit of each - parameter. - A convenience function to quickly get an overview of the status of an instrument. - - Args: - update (bool) : If True, update the state by querying the - instrument. If False, just use the latest values in memory. - This argument gets passed to the snapshot function. - max_chars (int) : the maximum number of characters per line. The - readable snapshot will be cropped if this value is exceeded. - Defaults to 80 to be consistent with default terminal width. - """ - floating_types = (float, np.integer, np.floating) - snapshot = self.snapshot(update=update) - - par_lengths = [len(p) for p in snapshot['parameters']] - - # Min of 50 is to prevent a super long parameter name to break this - # function - par_field_len = min(max(par_lengths)+1, 50) - - print(self.name + ':') - print('{0:<{1}}'.format('\tparameter ', par_field_len) + 'value') - print('-'*80) - for par in sorted(snapshot['parameters']): - name = snapshot['parameters'][par]['name'] - msg = '{0:<{1}}:'.format(name, par_field_len) - val = snapshot['parameters'][par]['value'] - unit = snapshot['parameters'][par].get('unit', None) - if unit is None: - # this may be a multi parameter - unit = snapshot['parameters'][par].get('units', None) - if isinstance(val, floating_types): - msg += '\t{:.5g} '.format(val) - else: - msg += '\t{} '.format(val) - if unit is not '': # corresponds to no unit - msg += '({})'.format(unit) - # Truncate the message if it is longer than max length - if len(msg) > max_chars and not max_chars == -1: - msg = msg[0:max_chars-3] + '...' - print(msg) - # `write_raw` and `ask_raw` are the interface to hardware # # `write` and `ask` are standard wrappers to help with error reporting # # @@ -490,80 +620,5 @@ def ask_raw(self, cmd): 'Instrument {} has not defined an ask method'.format( type(self).__name__)) - # - # shortcuts to parameters & setters & getters # - # - # instrument['someparam'] === instrument.parameters['someparam'] # - # instrument.someparam === instrument.parameters['someparam'] # - # instrument.get('someparam') === instrument['someparam'].get() # - # etc... # - # - delegate_attr_dicts = ['parameters', 'functions', 'submodules'] - def __getitem__(self, key): - """Delegate instrument['name'] to parameter or function 'name'.""" - try: - return self.parameters[key] - except KeyError: - return self.functions[key] - - def set(self, param_name, value): - """ - Shortcut for setting a parameter from its name and new value. - - Args: - param_name (str): The name of a parameter of this instrument. - value (any): The new value to set. - """ - self.parameters[param_name].set(value) - - def get(self, param_name): - """ - Shortcut for getting a parameter from its name. - - Args: - param_name (str): The name of a parameter of this instrument. - - Returns: - any: The current value of the parameter. - """ - return self.parameters[param_name].get() - - def call(self, func_name, *args): - """ - Shortcut for calling a function from its name. - - Args: - func_name (str): The name of a function of this instrument. - *args: any arguments to the function. - - Returns: - any: The return value of the function. - """ - return self.functions[func_name].call(*args) - - def __getstate__(self): - """Prevent pickling instruments, and give a nice error message.""" - raise RuntimeError( - 'Pickling %s. qcodes Instruments should not be pickled. Likely this means you ' - 'were trying to use a local instrument (defined with ' - 'server_name=None) in a background Loop. Local instruments can ' - 'only be used in Loops with background=False.' % self.name) - - def validate_status(self, verbose=False): - """ Validate the values of all gettable parameters - - The validation is done for all parameters that have both a get and - set method. - - Arguments: - verbose (bool): If True, then information about the parameters that are being check is printed. - - """ - for k, p in self.parameters.items(): - if p.has_get and p.has_set: - value = p.get() - if verbose: - print('validate_status: param %s: %s' % (k, value)) - p.validate(value) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index f5c441101247..dab5eceb3980 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -1,13 +1,13 @@ """ Base class for the channel of an instrument """ from typing import List, Tuple, Union -from .base import Instrument +from .base import InstrumentBase, Instrument from .parameter import MultiParameter, ArrayParameter from ..utils.metadata import Metadatable from ..utils.helpers import full_class -class InstrumentChannel(Instrument): +class InstrumentChannel(InstrumentBase): """ Base class for a channel in an instrument @@ -46,35 +46,6 @@ def __repr__(self): type(self._parent).__name__, self._parent.name) - # We aren't a member of the global list of instruments, don't try and remove ourself - def __del__(self): - """ Does nothing for an instrument channel """ - pass - - def close(self): - """ Doesn't make sense to just close a channel by default, raise NotImplemented """ - raise NotImplementedError("Can't close a channel. Close my parent instead.") - - @classmethod - def record_instance(cls, instance): - """ Instances should not be recorded for channels. This should happen for the parent instrument. """ - pass - - @classmethod - def instances(cls): - """ Instances should not be recorded for channels. This should happen for the parent instrument. """ - pass - - @classmethod - def remove_instances(cls, instance): - """ It doesn't make sense to remove a channel from an instrument, raise NotImplemented""" - raise NotImplementedError("Can't remove a channel.") - - # This method doesn't make sense for a channel, raise NotImplemented - @classmethod - def find_instruments(cls, name, instrument_class=None): - raise NotImplementedError("Can't find instruments in a channel") - # Pass any commands to read or write from the instrument up to the parent def write(self, cmd): return self._parent.write(cmd) From 4b93576adf435e29f18eeadd2d622aaa221f74e3 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 12 Jun 2017 14:51:44 +0200 Subject: [PATCH 02/56] Include channels in print_readable_snaphoot --- qcodes/instrument/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index f65bb2cddcf9..a168f7f91e74 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -168,6 +168,14 @@ def print_readable_snapshot(self, update=False, max_chars=80): msg = msg[0:max_chars-3] + '...' print(msg) + for submodule in self.submodules.values(): + if hasattr(submodule, '_channels'): + if submodule._snapshotable: + for channel in submodule._channels: + channel.print_readable_snapshot() + else: + submodule.print_readable_snapshot(update, max_chars) + # # shortcuts to parameters & setters & getters # # From 31cc34c4b61f6a0b0f604914d6fc3c7ebd2284bb Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 12 Jun 2017 16:40:00 +0200 Subject: [PATCH 03/56] Fix: make locked channels a named tuple That way you can access the names on the channellist via names as soon as they are locked --- qcodes/instrument/channel.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index dab5eceb3980..805dae608d73 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -1,8 +1,10 @@ """ Base class for the channel of an instrument """ from typing import List, Tuple, Union +from collections import namedtuple from .base import InstrumentBase, Instrument from .parameter import MultiParameter, ArrayParameter +from qcodes.utils.helpers import DelegateAttributes from ..utils.metadata import Metadatable from ..utils.helpers import full_class @@ -35,6 +37,7 @@ def __init__(self, parent, name, **kwargs): self.functions = {} self.name = "{}_{}".format(parent.name, str(name)) + self.short_name = str(name) self._meta_attrs = ['name'] self._parent = parent @@ -276,7 +279,9 @@ def lock(self): """ if self._locked: return - self._channels = tuple(self._channels) + + channeltuple = namedtuple('channels', [channel.short_name for channel in self._channels]) + self._channels = channeltuple(*self._channels) self._locked = True def snapshot_base(self, update=False): @@ -310,7 +315,7 @@ def __getattr__(self, name): Params: name(str): The name of the parameter or function that we want to operate on. """ - # Check if this is a valid parameter + # Check if this is a valid parametervx if name in self._channels[0].parameters: setpoints = None setpoint_names = None @@ -362,4 +367,18 @@ def multi_func(*args, **kwargs): chan.functions[name](*args, **kwargs) return multi_func + try: + return getattr(self._channels, name) + except: + pass + raise AttributeError('\'{}\' object has no attribute \'{}\''.format(self.__class__.__name__, name)) + + def __dir__(self): + names = super().__dir__() + if self._channels: + names += list(self._channels[0].parameters.keys()) + names += list(self._channels[0].functions.keys()) + if self._locked: + names += [channel.short_name for channel in self._channels] + return sorted(set(names)) From 8734ab4d5741d3031ff1e448bb23c0ea9f0b1ccf Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 13 Jun 2017 11:16:12 +0200 Subject: [PATCH 04/56] Fix: channels add support for indexing from 1 many instruments have hardware channels that are indexed and start at 1 --- qcodes/instrument/channel.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 805dae608d73..e34f2961097b 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -143,7 +143,8 @@ class ChannelList(Metadatable): def __init__(self, parent, name, chan_type: type, chan_list=None, snapshotable=True, - multichan_paramclass: type=MultiChannelInstrumentParameter): + multichan_paramclass: type = MultiChannelInstrumentParameter, + oneindexed=False): super().__init__() self._parent = parent @@ -160,6 +161,7 @@ def __init__(self, parent, name, chan_type: type, chan_list=None, self._chan_type = chan_type self._snapshotable = snapshotable self._paramclass = multichan_paramclass + self._oneindexed = oneindexed # If a list of channels is not provided, define a list to store channels. # This will eventually become a locked tuple. @@ -180,9 +182,18 @@ def __getitem__(self, i): i (int/slice): Either a single channel index or a slice of channels to get """ if isinstance(i, slice): + if self._oneindexed: + if i.start < 1 or i.stop < 1: + raise IndexError("1 indexed channel lists only support positive indexes") + i = slice(i.start-1, i.stop-1, i.step) return ChannelList(self._parent, self._name, self._chan_type, self._channels[i], - multichan_paramclass=self._paramclass) + multichan_paramclass=self._paramclass, + oneindexed=self._oneindexed) + elif self._oneindexed: + if i<1: + raise IndexError("1 indexed channel lists only support positive indexes") + i += -1 return self._channels[i] def __iter__(self): @@ -213,7 +224,8 @@ def __add__(self, other): if self._parent != other._parent: raise ValueError("Can only add channels from the same parent together.") - return ChannelList(self._parent, self._name, self._chan_type, self._channels + other._channels) + return ChannelList(self._parent, self._name, self._chan_type, self._channels + other._channels, + oneindexed=self._oneindexed) def append(self, obj): """ @@ -253,7 +265,7 @@ def index(self, obj): Args: obj(chan_type): The object to find in the channel list. """ - return self._channels.index(obj) + return self._channels.index(obj)+ self._oneindexed def insert(self, index, obj): """ @@ -270,6 +282,11 @@ def insert(self, index, obj): raise TypeError("All items in a channel list must be of the same type." " Adding {} to a list of {}.".format(type(obj).__name__, self._chan_type.__name__)) + if self._oneindexed: + if index < 1: + raise IndexError("1 based channel lists only support positive indexes") + index += 1 + return self._channels.insert(index, obj) def lock(self): From 75b3fa6c7681b04801579655542e38b292fc0494 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 14 Jun 2017 15:12:45 +0200 Subject: [PATCH 05/56] Fix linting errors in channel --- qcodes/instrument/channel.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index e34f2961097b..a92c095a0030 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -4,7 +4,6 @@ from .base import InstrumentBase, Instrument from .parameter import MultiParameter, ArrayParameter -from qcodes.utils.helpers import DelegateAttributes from ..utils.metadata import Metadatable from ..utils.helpers import full_class @@ -141,7 +140,7 @@ class ChannelList(Metadatable): """ - def __init__(self, parent, name, chan_type: type, chan_list=None, + def __init__(self, parent: Instrument, name, chan_type: type, chan_list=None, snapshotable=True, multichan_paramclass: type = MultiChannelInstrumentParameter, oneindexed=False): @@ -191,7 +190,7 @@ def __getitem__(self, i): multichan_paramclass=self._paramclass, oneindexed=self._oneindexed) elif self._oneindexed: - if i<1: + if i < 1: raise IndexError("1 indexed channel lists only support positive indexes") i += -1 return self._channels[i] @@ -265,7 +264,7 @@ def index(self, obj): Args: obj(chan_type): The object to find in the channel list. """ - return self._channels.index(obj)+ self._oneindexed + return self._channels.index(obj) + self._oneindexed def insert(self, index, obj): """ @@ -359,7 +358,7 @@ def __getattr__(self, name): if self._channels[0].parameters[name].setpoint_units: setpoint_units = tuple(chan.parameters[name].setpoint_units for chan in self._channels) else: - shapes = tuple(() for chan in self._channels) + shapes = tuple(() for _ in self._channels) param = self._paramclass(self._channels, param_name=name, @@ -386,7 +385,7 @@ def multi_func(*args, **kwargs): try: return getattr(self._channels, name) - except: + except AttributeError: pass raise AttributeError('\'{}\' object has no attribute \'{}\''.format(self.__class__.__name__, name)) From 4b1b5d0767270d06f8b93200e42ecab3b727ce30 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 16 Jun 2017 11:23:38 +0200 Subject: [PATCH 06/56] fix typo --- qcodes/instrument/channel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index a92c095a0030..670f789a4529 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -331,7 +331,7 @@ def __getattr__(self, name): Params: name(str): The name of the parameter or function that we want to operate on. """ - # Check if this is a valid parametervx + # Check if this is a valid parameter if name in self._channels[0].parameters: setpoints = None setpoint_names = None From 0610ee5d76dbf86aef87ddd40aa4878080954a3d Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 19 Jun 2017 14:49:26 +0200 Subject: [PATCH 07/56] Move print readable snapshot to instrument class where it belongs --- qcodes/instrument/base.py | 113 ++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 59 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index a168f7f91e74..cac6072cbd59 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -38,7 +38,6 @@ def __init__(self, name, **kwargs): self.name = str(name) self.parameters = {} self.functions = {} - self.submodules = {} super().__init__(**kwargs) def add_parameter(self, name, parameter_class=StandardParameter, @@ -122,60 +121,6 @@ def snapshot_base(self, update=False): snap[attr] = getattr(self, attr) return snap - def print_readable_snapshot(self, update=False, max_chars=80): - """ - Prints a readable version of the snapshot. - The readable snapshot includes the name, value and unit of each - parameter. - A convenience function to quickly get an overview of the status of an instrument. - - Args: - update (bool) : If True, update the state by querying the - instrument. If False, just use the latest values in memory. - This argument gets passed to the snapshot function. - max_chars (int) : the maximum number of characters per line. The - readable snapshot will be cropped if this value is exceeded. - Defaults to 80 to be consistent with default terminal width. - """ - floating_types = (float, np.integer, np.floating) - snapshot = self.snapshot(update=update) - - par_lengths = [len(p) for p in snapshot['parameters']] - - # Min of 50 is to prevent a super long parameter name to break this - # function - par_field_len = min(max(par_lengths)+1, 50) - - print(self.name + ':') - print('{0:<{1}}'.format('\tparameter ', par_field_len) + 'value') - print('-'*80) - for par in sorted(snapshot['parameters']): - name = snapshot['parameters'][par]['name'] - msg = '{0:<{1}}:'.format(name, par_field_len) - val = snapshot['parameters'][par]['value'] - unit = snapshot['parameters'][par].get('unit', None) - if unit is None: - # this may be a multi parameter - unit = snapshot['parameters'][par].get('units', None) - if isinstance(val, floating_types): - msg += '\t{:.5g} '.format(val) - else: - msg += '\t{} '.format(val) - if unit is not '': # corresponds to no unit - msg += '({})'.format(unit) - # Truncate the message if it is longer than max length - if len(msg) > max_chars and not max_chars == -1: - msg = msg[0:max_chars-3] + '...' - print(msg) - - for submodule in self.submodules.values(): - if hasattr(submodule, '_channels'): - if submodule._snapshotable: - for channel in submodule._channels: - channel.print_readable_snapshot() - else: - submodule.print_readable_snapshot(update, max_chars) - # # shortcuts to parameters & setters & getters # # @@ -292,12 +237,8 @@ def __init__(self, name, **kwargs): warnings.warn("server_name argument not supported any more", stacklevel=0) super().__init__(name, **kwargs) - self.parameters = {} - self.functions = {} self.submodules = {} - self.name = str(name) - self.add_parameter('IDN', get_cmd=self.get_idn, vals=Anything()) @@ -525,6 +466,60 @@ def add_submodule(self, name, submodule): raise TypeError('Submodules must be metadatable.') self.submodules[name] = submodule + def print_readable_snapshot(self, update=False, max_chars=80): + """ + Prints a readable version of the snapshot. + The readable snapshot includes the name, value and unit of each + parameter. + A convenience function to quickly get an overview of the status of an instrument. + + Args: + update (bool) : If True, update the state by querying the + instrument. If False, just use the latest values in memory. + This argument gets passed to the snapshot function. + max_chars (int) : the maximum number of characters per line. The + readable snapshot will be cropped if this value is exceeded. + Defaults to 80 to be consistent with default terminal width. + """ + floating_types = (float, np.integer, np.floating) + snapshot = self.snapshot(update=update) + + par_lengths = [len(p) for p in snapshot['parameters']] + + # Min of 50 is to prevent a super long parameter name to break this + # function + par_field_len = min(max(par_lengths)+1, 50) + + print(self.name + ':') + print('{0:<{1}}'.format('\tparameter ', par_field_len) + 'value') + print('-'*80) + for par in sorted(snapshot['parameters']): + name = snapshot['parameters'][par]['name'] + msg = '{0:<{1}}:'.format(name, par_field_len) + val = snapshot['parameters'][par]['value'] + unit = snapshot['parameters'][par].get('unit', None) + if unit is None: + # this may be a multi parameter + unit = snapshot['parameters'][par].get('units', None) + if isinstance(val, floating_types): + msg += '\t{:.5g} '.format(val) + else: + msg += '\t{} '.format(val) + if unit is not '': # corresponds to no unit + msg += '({})'.format(unit) + # Truncate the message if it is longer than max length + if len(msg) > max_chars and not max_chars == -1: + msg = msg[0:max_chars-3] + '...' + print(msg) + + for submodule in self.submodules.values(): + if hasattr(submodule, '_channels'): + if submodule._snapshotable: + for channel in submodule._channels: + channel.print_readable_snapshot() + else: + submodule.print_readable_snapshot(update, max_chars) + def snapshot_base(self, update=False): """ State of the instrument as a JSON-compatible dict. From de6d1ed464fa5678a7a7a7e2f8c0a2a5c6c91deb Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 19 Jun 2017 17:03:03 +0200 Subject: [PATCH 08/56] Fix: more channel annotation --- qcodes/instrument/channel.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 670f789a4529..5232a9de819e 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -27,7 +27,7 @@ class InstrumentChannel(InstrumentBase): Usually populated via ``add_function`` """ - def __init__(self, parent, name, **kwargs): + def __init__(self, parent: Instrument, name: str, **kwargs): # Initialize base classes of Instrument. We will overwrite what we want to do # in the Instrument initializer super().__init__(name=name, **kwargs) @@ -73,7 +73,10 @@ class MultiChannelInstrumentParameter(MultiParameter): param_name(str): Name of the multichannel parameter """ - def __init__(self, channels: Union[List, Tuple], param_name, *args, **kwargs): + def __init__(self, + channels: Union[List, Tuple], + param_name: str, + *args, **kwargs): super().__init__(*args, **kwargs) self._channels = channels self._param_name = param_name @@ -140,10 +143,13 @@ class ChannelList(Metadatable): """ - def __init__(self, parent: Instrument, name, chan_type: type, chan_list=None, - snapshotable=True, + def __init__(self, parent: Instrument, + name: str, + chan_type: type, + chan_list: Union[List, Tuple, None]=None, + snapshotable: bool=True, multichan_paramclass: type = MultiChannelInstrumentParameter, - oneindexed=False): + oneindexed: bool=False): super().__init__() self._parent = parent @@ -226,7 +232,7 @@ def __add__(self, other): return ChannelList(self._parent, self._name, self._chan_type, self._channels + other._channels, oneindexed=self._oneindexed) - def append(self, obj): + def append(self, obj: InstrumentChannel): """ When initially constructing the channel list, a new channel to add to the end of the list @@ -257,7 +263,7 @@ def extend(self, objects): raise TypeError("All items in a channel list must be of the same type.") return self._channels.extend(objects) - def index(self, obj): + def index(self, obj: InstrumentChannel): """ Return the index of the given object @@ -266,7 +272,7 @@ def index(self, obj): """ return self._channels.index(obj) + self._oneindexed - def insert(self, index, obj): + def insert(self, index: int, obj): """ Insert an object into the channel list at a specific index. @@ -300,7 +306,7 @@ def lock(self): self._channels = channeltuple(*self._channels) self._locked = True - def snapshot_base(self, update=False): + def snapshot_base(self, update: bool=False): """ State of the instrument as a JSON-compatible dict. @@ -323,7 +329,7 @@ def snapshot_base(self, update=False): } return snap - def __getattr__(self, name): + def __getattr__(self, name: str): """ Return a multi-channel function or parameter that we can use to get or set all items in a channel list simultaneously. From dd471b32ed6c8cd9507e9ce0be7374caa014b4c8 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 19 Jun 2017 17:07:34 +0200 Subject: [PATCH 09/56] Improve error message --- qcodes/instrument/channel.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 5232a9de819e..c176762bb594 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -189,7 +189,7 @@ def __getitem__(self, i): if isinstance(i, slice): if self._oneindexed: if i.start < 1 or i.stop < 1: - raise IndexError("1 indexed channel lists only support positive indexes") + raise IndexError("1 indexed channel lists only support positive indices") i = slice(i.start-1, i.stop-1, i.step) return ChannelList(self._parent, self._name, self._chan_type, self._channels[i], @@ -197,7 +197,7 @@ def __getitem__(self, i): oneindexed=self._oneindexed) elif self._oneindexed: if i < 1: - raise IndexError("1 indexed channel lists only support positive indexes") + raise IndexError("1 indexed channel lists only support positive indices") i += -1 return self._channels[i] @@ -270,7 +270,7 @@ def index(self, obj: InstrumentChannel): Args: obj(chan_type): The object to find in the channel list. """ - return self._channels.index(obj) + self._oneindexed + return self._channels.index(obj) + int(self._oneindexed) def insert(self, index: int, obj): """ @@ -289,7 +289,7 @@ def insert(self, index: int, obj): self._chan_type.__name__)) if self._oneindexed: if index < 1: - raise IndexError("1 based channel lists only support positive indexes") + raise IndexError("1 based channel lists only support positive indices") index += 1 return self._channels.insert(index, obj) From c3c7d5bb600a6c09741fab1de6be9dc59b03e506 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 19 Jun 2017 17:11:15 +0200 Subject: [PATCH 10/56] pprint limit line lenght of header to max char --- qcodes/instrument/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index cac6072cbd59..8b36b3739af3 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -492,7 +492,7 @@ def print_readable_snapshot(self, update=False, max_chars=80): print(self.name + ':') print('{0:<{1}}'.format('\tparameter ', par_field_len) + 'value') - print('-'*80) + print('-'*max_chars) for par in sorted(snapshot['parameters']): name = snapshot['parameters'][par]['name'] msg = '{0:<{1}}:'.format(name, par_field_len) From 032144bfb95ef3d82e39a55d5099f979fecc95de Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 19 Jun 2017 17:25:37 +0200 Subject: [PATCH 11/56] pep8 line lenght --- qcodes/instrument/channel.py | 174 +++++++++++++++++++++-------------- 1 file changed, 107 insertions(+), 67 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index c176762bb594..157f4e8d2d2a 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -13,23 +13,24 @@ class InstrumentChannel(InstrumentBase): Base class for a channel in an instrument Args: - parent (Instrument): the instrument to which this channel should be attached + parent (Instrument): the instrument to which this channel should be + attached name (str): the name of this channel Attributes: name (str): the name of this channel - parameters (Dict[Parameter]): All the parameters supported by this channel. - Usually populated via ``add_parameter`` + parameters (Dict[Parameter]): All the parameters supported by this + channel. Usually populated via ``add_parameter`` - functions (Dict[Function]): All the functions supported by this channel. - Usually populated via ``add_function`` + functions (Dict[Function]): All the functions supported by this + channel. Usually populated via ``add_function`` """ def __init__(self, parent: Instrument, name: str, **kwargs): - # Initialize base classes of Instrument. We will overwrite what we want to do - # in the Instrument initializer + # Initialize base classes of Instrument. We will overwrite what we + # want to do in the Instrument initializer super().__init__(name=name, **kwargs) self.parameters = {} @@ -66,10 +67,12 @@ class MultiChannelInstrumentParameter(MultiParameter): """ Parameter to get or set multiple channels simultaneously. - Will normally be created by a ChannelList and not directly by anything else. + Will normally be created by a ChannelList and not directly by anything + else. Args: - channels(list[chan_type]): A list of channels which we can operate on simultaneously. + channels(list[chan_type]): A list of channels which we can operate on + simultaneously. param_name(str): Name of the multichannel parameter """ @@ -83,9 +86,11 @@ def __init__(self, def get(self): """ - Return a tuple containing the data from each of the channels in the list + Return a tuple containing the data from each of the channels in the + list """ - return tuple(chan.parameters[self._param_name].get() for chan in self._channels) + return tuple(chan.parameters[self._param_name].get() for chan + in self._channels) def set(self, value): """ @@ -100,9 +105,10 @@ def set(self, value): @property def full_names(self): - """Overwrite full_names because the instument name is already included in the name. - This happens because the instument name is included in the channel name merged into the - parameter name above. + """Overwrite full_names because the instument name is already included + in the name. This happens because the instument name is included in + the channel name merged into the + parameter name above. """ return self.names @@ -131,9 +137,9 @@ class ChannelList(Metadatable): stored inside a channel list are accessible in multiple ways and should not be repeated in an instrument snapshot. - multichan_paramclass (MultiChannelInstrumentParameter): The class of the object - to be returned by the ChanneList's __getattr__ method. Should be a - subclass of MultiChannelInstrumentParameter. + multichan_paramclass (MultiChannelInstrumentParameter): The class of + the object to be returned by the ChanneList's __getattr__ method. + Should be a subclass of MultiChannelInstrumentParameter. Raises: ValueError: If chan_type is not a subclass of InstrumentChannel @@ -159,7 +165,8 @@ def __init__(self, parent: Instrument, raise ValueError("Channel Lists can only hold instances of type" " InstrumentChannel") if (not isinstance(multichan_paramclass, type) or - not issubclass(multichan_paramclass, MultiChannelInstrumentParameter)): + not issubclass(multichan_paramclass, + MultiChannelInstrumentParameter)): raise ValueError("multichan_paramclass must be a (subclass of) " "MultiChannelInstrumentParameter") @@ -168,8 +175,8 @@ def __init__(self, parent: Instrument, self._paramclass = multichan_paramclass self._oneindexed = oneindexed - # If a list of channels is not provided, define a list to store channels. - # This will eventually become a locked tuple. + # If a list of channels is not provided, define a list to store + # channels. This will eventually become a locked tuple. if chan_list is None: self._locked = False self._channels = [] @@ -177,19 +184,23 @@ def __init__(self, parent: Instrument, self._locked = True self._channels = tuple(chan_list) if not all(isinstance(chan, chan_type) for chan in self._channels): - raise TypeError("All items in this channel list must be of type {}.".format(chan_type.__name__)) + raise TypeError("All items in this channel list must be of " + "type {}.".format(chan_type.__name__)) def __getitem__(self, i): """ - Return either a single channel, or a new ChannelList containing only the specified channels + Return either a single channel, or a new ChannelList containing only + the specified channels Args: - i (int/slice): Either a single channel index or a slice of channels to get + i (int/slice): Either a single channel index or a slice of channels + to get """ if isinstance(i, slice): if self._oneindexed: if i.start < 1 or i.stop < 1: - raise IndexError("1 indexed channel lists only support positive indices") + raise IndexError("1 indexed channel lists only support " + "positive indices") i = slice(i.start-1, i.stop-1, i.step) return ChannelList(self._parent, self._name, self._chan_type, self._channels[i], @@ -197,7 +208,8 @@ def __getitem__(self, i): oneindexed=self._oneindexed) elif self._oneindexed: if i < 1: - raise IndexError("1 indexed channel lists only support positive indices") + raise IndexError("1 indexed channel lists only support " + "positive indices") i += -1 return self._channels[i] @@ -208,33 +220,43 @@ def __len__(self): return len(self._channels) def __repr__(self): - return "ChannelList({!r}, {}, {!r})".format(self._parent, self._chan_type.__name__, self._channels) + return "ChannelList({!r}, {}, {!r})".format(self._parent, + self._chan_type.__name__, + self._channels) def __add__(self, other): """ - Return a new channel list containing the channels from both ChannelList self and r. + Return a new channel list containing the channels from both + ChannelList self and r. Both channel lists must hold the same type and have the same parent. Args: other(ChannelList): Right argument to add. """ - if not isinstance(self, ChannelList) or not isinstance(other, ChannelList): - raise TypeError("Can't add objects of type {} and {} together".format( - type(self).__name__, type(other).__name__)) + if not isinstance(self, ChannelList) or not isinstance(other, + ChannelList): + raise TypeError("Can't add objects of type" + " {} and {} together".format(type(self).__name__, + type(other).__name__)) if self._chan_type != other._chan_type: - raise TypeError("Both l and r arguments to add must contain channels of the same type." - " Adding channels of type {} and {}.".format(self._chan_type.__name__, - other._chan_type.__name__)) + raise TypeError("Both l and r arguments to add must contain " + "channels of the same type." + " Adding channels of type " + "{} and {}.".format(self._chan_type.__name__, + other._chan_type.__name__)) if self._parent != other._parent: - raise ValueError("Can only add channels from the same parent together.") + raise ValueError("Can only add channels from the same parent " + "together.") - return ChannelList(self._parent, self._name, self._chan_type, self._channels + other._channels, + return ChannelList(self._parent, self._name, self._chan_type, + self._channels + other._channels, oneindexed=self._oneindexed) def append(self, obj: InstrumentChannel): """ - When initially constructing the channel list, a new channel to add to the end of the list + When initially constructing the channel list, a new channel to add to + the end of the list Args: obj(chan_type): New channel to add to the list. @@ -242,9 +264,10 @@ def append(self, obj: InstrumentChannel): if self._locked: raise AttributeError("Cannot append to a locked channel list") if not isinstance(obj, self._chan_type): - raise TypeError("All items in a channel list must be of the same type." - " Adding {} to a list of {}.".format(type(obj).__name__, - self._chan_type.__name__)) + raise TypeError("All items in a channel list must be of the same " + "type. Adding {} to a list of {}" + ".".format(type(obj).__name__, + self._chan_type.__name__)) return self._channels.append(obj) def extend(self, objects): @@ -252,15 +275,17 @@ def extend(self, objects): Insert an iterable of objects into the list of channels. Args: - objects(Iterable[chan_type]): A list of objects to add into the ChannelList. + objects(Iterable[chan_type]): A list of objects to add into the + ChannelList. """ - # objects may be a generator but we need to iterate over it twice below so - # copy it into a tuple just in case. + # objects may be a generator but we need to iterate over it twice + # below so copy it into a tuple just in case. objects = tuple(objects) if self._locked: raise AttributeError("Cannot extend a locked channel list") if not all(isinstance(obj, self._chan_type) for obj in objects): - raise TypeError("All items in a channel list must be of the same type.") + raise TypeError("All items in a channel list must be of the same " + "type.") return self._channels.extend(objects) def index(self, obj: InstrumentChannel): @@ -284,25 +309,28 @@ def insert(self, index: int, obj): if self._locked: raise AttributeError("Cannot insert into a locked channel list") if not isinstance(obj, self._chan_type): - raise TypeError("All items in a channel list must be of the same type." - " Adding {} to a list of {}.".format(type(obj).__name__, - self._chan_type.__name__)) + raise TypeError("All items in a channel list must be of the same " + "type. Adding {} to a list of {}" + ".".format(type(obj).__name__, + self._chan_type.__name__)) if self._oneindexed: if index < 1: - raise IndexError("1 based channel lists only support positive indices") + raise IndexError("1 based channel lists only support positive" + " indices") index += 1 return self._channels.insert(index, obj) def lock(self): """ - Lock the channel list. Once this is done, the channel list is converted to a tuple - and any future changes to the list are prevented. + Lock the channel list. Once this is done, the channel list is + converted to a tuple and any future changes to the list are prevented. """ if self._locked: return - channeltuple = namedtuple('channels', [channel.short_name for channel in self._channels]) + channeltuple = namedtuple('channels', [channel.short_name for channel + in self._channels]) self._channels = channeltuple(*self._channels) self._locked = True @@ -331,11 +359,12 @@ def snapshot_base(self, update: bool=False): def __getattr__(self, name: str): """ - Return a multi-channel function or parameter that we can use to get or set all items - in a channel list simultaneously. + Return a multi-channel function or parameter that we can use to get or + set all items in a channel list simultaneously. Params: - name(str): The name of the parameter or function that we want to operate on. + name(str): The name of the parameter or function that we want to + operate on. """ # Check if this is a valid parameter if name in self._channels[0].parameters: @@ -344,25 +373,35 @@ def __getattr__(self, name: str): setpoint_labels = None setpoint_units = None # We need to construct a MultiParameter object to get each of the - # values our of each parameter in our list, we don't currently try to - # construct a multiparameter from a list of multi parameters + # values our of each parameter in our list, we don't currently try + # to construct a multiparameter from a list of multi parameters if isinstance(self._channels[0].parameters[name], MultiParameter): - raise NotImplementedError("Slicing is currently not supported for MultiParameters") - names = tuple("{}_{}".format(chan.name, name) for chan in self._channels) - labels = tuple(chan.parameters[name].label for chan in self._channels) - units = tuple(chan.parameters[name].unit for chan in self._channels) + raise NotImplementedError("Slicing is currently not " + "supported for MultiParameters") + names = tuple("{}_{}".format(chan.name, name) + for chan in self._channels) + labels = tuple(chan.parameters[name].label + for chan in self._channels) + units = tuple(chan.parameters[name].unit + for chan in self._channels) if isinstance(self._channels[0].parameters[name], ArrayParameter): - shapes = tuple(chan.parameters[name].shape for chan in self._channels) + shapes = tuple(chan.parameters[name].shape for + chan in self._channels) if self._channels[0].parameters[name].setpoints: - setpoints = tuple(chan.parameters[name].setpoints for chan in self._channels) + setpoints = tuple(chan.parameters[name].setpoints for + chan in self._channels) if self._channels[0].parameters[name].setpoint_names: - setpoint_names = tuple(chan.parameters[name].setpoint_names for chan in self._channels) + setpoint_names = tuple(chan.parameters[name].setpoint_names + for chan in self._channels) if self._channels[0].parameters[name].setpoint_labels: - setpoint_labels = tuple(chan.parameters[name].setpoint_labels for chan in self._channels) + setpoint_labels = tuple( + chan.parameters[name].setpoint_labels + for chan in self._channels) if self._channels[0].parameters[name].setpoint_units: - setpoint_units = tuple(chan.parameters[name].setpoint_units for chan in self._channels) + setpoint_units = tuple(chan.parameters[name].setpoint_units + for chan in self._channels) else: shapes = tuple(() for _ in self._channels) @@ -382,8 +421,8 @@ def __getattr__(self, name: str): # Check if this is a valid function if name in self._channels[0].functions: - # We want to return a reference to a function that would call the function - # for each of the channels in turn. + # We want to return a reference to a function that would call the + # function for each of the channels in turn. def multi_func(*args, **kwargs): for chan in self._channels: chan.functions[name](*args, **kwargs) @@ -394,7 +433,8 @@ def multi_func(*args, **kwargs): except AttributeError: pass - raise AttributeError('\'{}\' object has no attribute \'{}\''.format(self.__class__.__name__, name)) + raise AttributeError('\'{}\' object has no attribute \'{}\'' + ''.format(self.__class__.__name__, name)) def __dir__(self): names = super().__dir__() From f0b6f44accccdf5497ae8084a55d59ef63c63f44 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 21 Jun 2017 15:29:34 +0200 Subject: [PATCH 12/56] Fix: channel remove support for oneindexed channels --- qcodes/instrument/channel.py | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 157f4e8d2d2a..2f9b428e69da 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -105,10 +105,9 @@ def set(self, value): @property def full_names(self): - """Overwrite full_names because the instument name is already included - in the name. This happens because the instument name is included in - the channel name merged into the - parameter name above. + """Overwrite full_names because the instrument name is already included + in the name. This happens because the instrument name is included in + the channel name merged into the parameter name above. """ return self.names @@ -154,8 +153,7 @@ def __init__(self, parent: Instrument, chan_type: type, chan_list: Union[List, Tuple, None]=None, snapshotable: bool=True, - multichan_paramclass: type = MultiChannelInstrumentParameter, - oneindexed: bool=False): + multichan_paramclass: type = MultiChannelInstrumentParameter): super().__init__() self._parent = parent @@ -173,7 +171,6 @@ def __init__(self, parent: Instrument, self._chan_type = chan_type self._snapshotable = snapshotable self._paramclass = multichan_paramclass - self._oneindexed = oneindexed # If a list of channels is not provided, define a list to store # channels. This will eventually become a locked tuple. @@ -197,20 +194,9 @@ def __getitem__(self, i): to get """ if isinstance(i, slice): - if self._oneindexed: - if i.start < 1 or i.stop < 1: - raise IndexError("1 indexed channel lists only support " - "positive indices") - i = slice(i.start-1, i.stop-1, i.step) return ChannelList(self._parent, self._name, self._chan_type, self._channels[i], - multichan_paramclass=self._paramclass, - oneindexed=self._oneindexed) - elif self._oneindexed: - if i < 1: - raise IndexError("1 indexed channel lists only support " - "positive indices") - i += -1 + multichan_paramclass=self._paramclass) return self._channels[i] def __iter__(self): @@ -250,8 +236,7 @@ def __add__(self, other): "together.") return ChannelList(self._parent, self._name, self._chan_type, - self._channels + other._channels, - oneindexed=self._oneindexed) + self._channels + other._channels) def append(self, obj: InstrumentChannel): """ @@ -295,7 +280,7 @@ def index(self, obj: InstrumentChannel): Args: obj(chan_type): The object to find in the channel list. """ - return self._channels.index(obj) + int(self._oneindexed) + return self._channels.index(obj) def insert(self, index: int, obj): """ @@ -313,11 +298,6 @@ def insert(self, index: int, obj): "type. Adding {} to a list of {}" ".".format(type(obj).__name__, self._chan_type.__name__)) - if self._oneindexed: - if index < 1: - raise IndexError("1 based channel lists only support positive" - " indices") - index += 1 return self._channels.insert(index, obj) From 2bdf3169b16e7791f61de1b090a8c00c58cda579 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 21 Jun 2017 15:58:08 +0200 Subject: [PATCH 13/56] improve type annotation --- qcodes/instrument/channel.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 2f9b428e69da..4144f9e6db9b 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -1,5 +1,5 @@ """ Base class for the channel of an instrument """ -from typing import List, Tuple, Union +from typing import List, Tuple, Union, Optional from collections import namedtuple from .base import InstrumentBase, Instrument @@ -68,7 +68,7 @@ class MultiChannelInstrumentParameter(MultiParameter): Parameter to get or set multiple channels simultaneously. Will normally be created by a ChannelList and not directly by anything - else. + else. Args: channels(list[chan_type]): A list of channels which we can operate on @@ -84,7 +84,7 @@ def __init__(self, self._channels = channels self._param_name = param_name - def get(self): + def get(self) -> tuple: """ Return a tuple containing the data from each of the channels in the list @@ -151,7 +151,7 @@ class ChannelList(Metadatable): def __init__(self, parent: Instrument, name: str, chan_type: type, - chan_list: Union[List, Tuple, None]=None, + chan_list: Optional[List, Tuple]=None, snapshotable: bool=True, multichan_paramclass: type = MultiChannelInstrumentParameter): super().__init__() @@ -184,7 +184,7 @@ def __init__(self, parent: Instrument, raise TypeError("All items in this channel list must be of " "type {}.".format(chan_type.__name__)) - def __getitem__(self, i): + def __getitem__(self, i: Union[int, slice]): """ Return either a single channel, or a new ChannelList containing only the specified channels @@ -210,7 +210,7 @@ def __repr__(self): self._chan_type.__name__, self._channels) - def __add__(self, other): + def __add__(self, other: 'ChannelList'): """ Return a new channel list containing the channels from both ChannelList self and r. @@ -282,7 +282,7 @@ def index(self, obj: InstrumentChannel): """ return self._channels.index(obj) - def insert(self, index: int, obj): + def insert(self, index: int, obj: InstrumentChannel): """ Insert an object into the channel list at a specific index. @@ -416,7 +416,7 @@ def multi_func(*args, **kwargs): raise AttributeError('\'{}\' object has no attribute \'{}\'' ''.format(self.__class__.__name__, name)) - def __dir__(self): + def __dir__(self) -> list: names = super().__dir__() if self._channels: names += list(self._channels[0].parameters.keys()) From a88436f0a62432d46a730b7ff8b308a53c3852b7 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 21 Jun 2017 16:13:40 +0200 Subject: [PATCH 14/56] Optional -> Union --- qcodes/instrument/channel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 4144f9e6db9b..2140bb436657 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -1,5 +1,5 @@ """ Base class for the channel of an instrument """ -from typing import List, Tuple, Union, Optional +from typing import List, Tuple, Union from collections import namedtuple from .base import InstrumentBase, Instrument @@ -151,7 +151,7 @@ class ChannelList(Metadatable): def __init__(self, parent: Instrument, name: str, chan_type: type, - chan_list: Optional[List, Tuple]=None, + chan_list: Union[List, Tuple, None]=None, snapshotable: bool=True, multichan_paramclass: type = MultiChannelInstrumentParameter): super().__init__() From 644756189860c357638371c1c1496b5e570a958d Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 21 Jun 2017 16:52:34 +0200 Subject: [PATCH 15/56] add channels to api docs --- docs/api/public.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/api/public.rst b/docs/api/public.rst index 4ce87432fc9c..cacfa5f25290 100644 --- a/docs/api/public.rst +++ b/docs/api/public.rst @@ -101,6 +101,8 @@ Instrument IPInstrument VisaInstrument + InstrumentChannel + ChannelList Plot ~~~~ From b38ad6ed49c6fb581fc6cf362be6b1132c6d756a Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 22 Jun 2017 10:42:52 +0200 Subject: [PATCH 16/56] fix: make submodules nested --- qcodes/instrument/base.py | 197 ++++++++++++++++------------------- qcodes/instrument/channel.py | 3 - 2 files changed, 87 insertions(+), 113 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index 8b36b3739af3..5776cbdb1a49 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -38,6 +38,7 @@ def __init__(self, name, **kwargs): self.name = str(name) self.parameters = {} self.functions = {} + self.submodules = {} super().__init__(**kwargs) def add_parameter(self, name, parameter_class=StandardParameter, @@ -99,6 +100,36 @@ def add_function(self, name, **kwargs): func = Function(name=name, instrument=self, **kwargs) self.functions[name] = func + def add_submodule(self, name, submodule): + """ + Bind one submodule to this instrument. + + Instrument subclasses can call this repeatedly in their ``__init__`` + method for every submodule of the instrument. + + Submodules can effectively be considered as instruments within the main + instrument, and should at minimum be snapshottable. For example, they can + be used to either store logical groupings of parameters, which may or may + not be repeated, or channel lists. + + Args: + name (str): how the submodule will be stored within ``instrument.submodules`` + and also how it can be addressed. + + submodule (Metadatable): The submodule to be stored. + + Raises: + KeyError: if this instrument already contains a submodule with this + name. + TypeError: if the submodule that we are trying to add is not an instance + of an Metadatable object. + """ + if name in self.submodules: + raise KeyError('Duplicate submodule name {}'.format(name)) + if not isinstance(submodule, Metadatable): + raise TypeError('Submodules must be metadatable.') + self.submodules[name] = submodule + def snapshot_base(self, update=False): """ State of the instrument as a JSON-compatible dict. @@ -114,6 +145,8 @@ def snapshot_base(self, update=False): for name, param in self.parameters.items()), 'functions': dict((name, func.snapshot(update=update)) for name, func in self.functions.items()), + 'submodules': dict((name, subm.snapshot(update=update)) + for name, subm in self.submodules.items()), '__class__': full_class(self), } for attr in set(self._meta_attrs): @@ -121,6 +154,60 @@ def snapshot_base(self, update=False): snap[attr] = getattr(self, attr) return snap + def print_readable_snapshot(self, update=False, max_chars=80): + """ + Prints a readable version of the snapshot. + The readable snapshot includes the name, value and unit of each + parameter. + A convenience function to quickly get an overview of the status of an instrument. + + Args: + update (bool) : If True, update the state by querying the + instrument. If False, just use the latest values in memory. + This argument gets passed to the snapshot function. + max_chars (int) : the maximum number of characters per line. The + readable snapshot will be cropped if this value is exceeded. + Defaults to 80 to be consistent with default terminal width. + """ + floating_types = (float, np.integer, np.floating) + snapshot = self.snapshot(update=update) + + par_lengths = [len(p) for p in snapshot['parameters']] + + # Min of 50 is to prevent a super long parameter name to break this + # function + par_field_len = min(max(par_lengths)+1, 50) + + print(self.name + ':') + print('{0:<{1}}'.format('\tparameter ', par_field_len) + 'value') + print('-'*max_chars) + for par in sorted(snapshot['parameters']): + name = snapshot['parameters'][par]['name'] + msg = '{0:<{1}}:'.format(name, par_field_len) + val = snapshot['parameters'][par]['value'] + unit = snapshot['parameters'][par].get('unit', None) + if unit is None: + # this may be a multi parameter + unit = snapshot['parameters'][par].get('units', None) + if isinstance(val, floating_types): + msg += '\t{:.5g} '.format(val) + else: + msg += '\t{} '.format(val) + if unit is not '': # corresponds to no unit + msg += '({})'.format(unit) + # Truncate the message if it is longer than max length + if len(msg) > max_chars and not max_chars == -1: + msg = msg[0:max_chars-3] + '...' + print(msg) + + for submodule in self.submodules.values(): + if hasattr(submodule, '_channels'): + if submodule._snapshotable: + for channel in submodule._channels: + channel.print_readable_snapshot() + else: + submodule.print_readable_snapshot(update, max_chars) + # # shortcuts to parameters & setters & getters # # @@ -237,7 +324,6 @@ def __init__(self, name, **kwargs): warnings.warn("server_name argument not supported any more", stacklevel=0) super().__init__(name, **kwargs) - self.submodules = {} self.add_parameter('IDN', get_cmd=self.get_idn, vals=Anything()) @@ -435,115 +521,6 @@ def find_instrument(cls, name, instrument_class=None): return ins - - def add_submodule(self, name, submodule): - """ - Bind one submodule to this instrument. - - Instrument subclasses can call this repeatedly in their ``__init__`` - method for every submodule of the instrument. - - Submodules can effectively be considered as instruments within the main - instrument, and should at minimum be snapshottable. For example, they can - be used to either store logical groupings of parameters, which may or may - not be repeated, or channel lists. - - Args: - name (str): how the submodule will be stored within ``instrument.submodules`` - and also how it can be addressed. - - submodule (Metadatable): The submodule to be stored. - - Raises: - KeyError: if this instrument already contains a submodule with this - name. - TypeError: if the submodule that we are trying to add is not an instance - of an Metadatable object. - """ - if name in self.submodules: - raise KeyError('Duplicate submodule name {}'.format(name)) - if not isinstance(submodule, Metadatable): - raise TypeError('Submodules must be metadatable.') - self.submodules[name] = submodule - - def print_readable_snapshot(self, update=False, max_chars=80): - """ - Prints a readable version of the snapshot. - The readable snapshot includes the name, value and unit of each - parameter. - A convenience function to quickly get an overview of the status of an instrument. - - Args: - update (bool) : If True, update the state by querying the - instrument. If False, just use the latest values in memory. - This argument gets passed to the snapshot function. - max_chars (int) : the maximum number of characters per line. The - readable snapshot will be cropped if this value is exceeded. - Defaults to 80 to be consistent with default terminal width. - """ - floating_types = (float, np.integer, np.floating) - snapshot = self.snapshot(update=update) - - par_lengths = [len(p) for p in snapshot['parameters']] - - # Min of 50 is to prevent a super long parameter name to break this - # function - par_field_len = min(max(par_lengths)+1, 50) - - print(self.name + ':') - print('{0:<{1}}'.format('\tparameter ', par_field_len) + 'value') - print('-'*max_chars) - for par in sorted(snapshot['parameters']): - name = snapshot['parameters'][par]['name'] - msg = '{0:<{1}}:'.format(name, par_field_len) - val = snapshot['parameters'][par]['value'] - unit = snapshot['parameters'][par].get('unit', None) - if unit is None: - # this may be a multi parameter - unit = snapshot['parameters'][par].get('units', None) - if isinstance(val, floating_types): - msg += '\t{:.5g} '.format(val) - else: - msg += '\t{} '.format(val) - if unit is not '': # corresponds to no unit - msg += '({})'.format(unit) - # Truncate the message if it is longer than max length - if len(msg) > max_chars and not max_chars == -1: - msg = msg[0:max_chars-3] + '...' - print(msg) - - for submodule in self.submodules.values(): - if hasattr(submodule, '_channels'): - if submodule._snapshotable: - for channel in submodule._channels: - channel.print_readable_snapshot() - else: - submodule.print_readable_snapshot(update, max_chars) - - def snapshot_base(self, update=False): - """ - State of the instrument as a JSON-compatible dict. - - Args: - update (bool): If True, update the state by querying the - instrument. If False, just use the latest values in memory. - - Returns: - dict: base snapshot - """ - snap = {'parameters': dict((name, param.snapshot(update=update)) - for name, param in self.parameters.items()), - 'functions': dict((name, func.snapshot(update=update)) - for name, func in self.functions.items()), - 'submodules': dict((name, subm.snapshot(update=update)) - for name, subm in self.submodules.items()), - '__class__': full_class(self), - } - for attr in set(self._meta_attrs): - if hasattr(self, attr): - snap[attr] = getattr(self, attr) - return snap - # `write_raw` and `ask_raw` are the interface to hardware # # `write` and `ask` are standard wrappers to help with error reporting # # diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 2140bb436657..039c7568a152 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -33,9 +33,6 @@ def __init__(self, parent: Instrument, name: str, **kwargs): # want to do in the Instrument initializer super().__init__(name=name, **kwargs) - self.parameters = {} - self.functions = {} - self.name = "{}_{}".format(parent.name, str(name)) self.short_name = str(name) self._meta_attrs = ['name'] From cc56d6a8bed321cd207fa3453767233d56a9ad78 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 22 Jun 2017 10:47:27 +0200 Subject: [PATCH 17/56] add submodule attributes to base --- qcodes/instrument/base.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/qcodes/instrument/base.py b/qcodes/instrument/base.py index 5776cbdb1a49..1e15297fafa7 100644 --- a/qcodes/instrument/base.py +++ b/qcodes/instrument/base.py @@ -209,14 +209,14 @@ def print_readable_snapshot(self, update=False, max_chars=80): submodule.print_readable_snapshot(update, max_chars) # - # shortcuts to parameters & setters & getters # + # shortcuts to parameters & setters & getters # # # instrument['someparam'] === instrument.parameters['someparam'] # # instrument.someparam === instrument.parameters['someparam'] # # instrument.get('someparam') === instrument['someparam'].get() # # etc... # # - delegate_attr_dicts = ['parameters', 'functions'] + delegate_attr_dicts = ['parameters', 'functions', 'submodules'] def __getitem__(self, key): """Delegate instrument['name'] to parameter or function 'name'.""" @@ -599,6 +599,3 @@ def ask_raw(self, cmd): raise NotImplementedError( 'Instrument {} has not defined an ask method'.format( type(self).__name__)) - - delegate_attr_dicts = ['parameters', 'functions', 'submodules'] - From 8f81c967d42d1624e445f32361374b994d4a522f Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 17 May 2017 17:12:46 +0200 Subject: [PATCH 18/56] Docs strings --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 1131115c5914..5ef6c7cfdcf9 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -12,6 +12,21 @@ class FrequencySweep(MultiParameter): Instrument returns an list of transmission data in the form of a list of complex numbers taken from a frequency sweep. + Args: + name: parameter name + instrument: instrument the parameter belongs to + start: starting frequency of sweep + stop: ending frequency of sweep + npts: numper of points in frequency sweep + + Methods: + set_sweep(start, stop, npts): sets the shapes and + setpoint arrays of the parameter to correspond with the sweep + get(): executes a sweep and returns magnitude and phase arrays + + get_ramping: Queries the value of self.ramp_state and + self.ramp_time. Returns a string. + TODO: - ability to choose for abs or db in magnitude return """ @@ -24,7 +39,7 @@ def __init__(self, name, instrument, start, stop, npts): self.setpoint_names = (('frequency',), ('frequency',)) def set_sweep(self, start, stop, npts): - # needed to update config of the software parameter on sweep chage + # needed to update config of the software parameter on sweep change # freq setpoints tuple as needs to be hashable for look up f = tuple(np.linspace(int(start), int(stop), num=npts)) self.setpoints = ((f,), (f,)) From 79a9f6ee3ea4beca8692c835d903732072cd7e3c Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 17 May 2017 17:13:09 +0200 Subject: [PATCH 19/56] Fix: add option to set span and center --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 5ef6c7cfdcf9..65f9a3b611a7 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -116,6 +116,16 @@ def __init__(self, name, address, **kwargs): set_cmd=self._set_stop, get_parser=int) + self.add_parameter(name='center', + get_cmd = 'SENS:FREQ:CENT?', + set_cmd = self._set_center, + get_parser = int) + + self.add_parameter(name='span', + get_cmd = 'SENS:FREQ:SPAN?', + set_cmd=self._set_span, + get_parser = int) + self.add_parameter(name='npts', get_cmd='SENS:SWE:POIN?', set_cmd=self._set_npts, @@ -156,6 +166,14 @@ def _set_npts(self, val): # update setpoints for FrequencySweep param self.trace.set_sweep(self.start(), self.stop(), val) + def _set_span(self, val): + self.write('SENS:FREQ:SPAN {:.4f}'.format(val)) + self.trace.set_sweep(self.start(), self.stop(), self.npts()) + + def _set_center(self, val): + self.write('SENS:FREQ:CENT {:.4f}'.format(val)) + self.trace.set_sweep(self.start(), self.stop(), self.npts()) + def initialise(self): self.write('*RST') self.write('SENS1:SWE:TYPE LIN') From 7f0145a4f53c19df5aa0a95e9f43e32f28bd931c Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 17 May 2017 17:22:44 +0200 Subject: [PATCH 20/56] Int -> float --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 65f9a3b611a7..2b7831ad9676 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -109,22 +109,22 @@ def __init__(self, name, address, **kwargs): self.add_parameter(name='start', get_cmd='SENS:FREQ:START?', set_cmd=self._set_start, - get_parser=int) + get_parser=float) self.add_parameter(name='stop', get_cmd='SENS:FREQ:STOP?', set_cmd=self._set_stop, - get_parser=int) + get_parser=float) self.add_parameter(name='center', get_cmd = 'SENS:FREQ:CENT?', set_cmd = self._set_center, - get_parser = int) + get_parser=float) self.add_parameter(name='span', get_cmd = 'SENS:FREQ:SPAN?', set_cmd=self._set_span, - get_parser = int) + get_parser=float) self.add_parameter(name='npts', get_cmd='SENS:SWE:POIN?', From 73bcab1e69869a93a2edd733b1857f64d0f18318 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 19 May 2017 10:54:43 +0200 Subject: [PATCH 21/56] Update znb notebook --- ...odes example with Rohde Schwarz ZN20.ipynb | 1040 ++++------------- 1 file changed, 206 insertions(+), 834 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb index e0e2acad4721..080c7cd5378d 100644 --- a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb @@ -11,379 +11,27 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Anaconda3\\lib\\site-packages\\IPython\\kernel\\__init__.py:13: ShimWarning: The `IPython.kernel` package has been deprecated. You should import from ipykernel or jupyter_client instead.\n", - " \"You should import from ipykernel or jupyter_client instead.\", ShimWarning)\n" - ] - }, - { - "data": { - "application/javascript": [ - "/*\r\n", - " * Qcodes Jupyter/IPython widgets\r\n", - " */\r\n", - "require([\r\n", - " 'nbextensions/widgets/widgets/js/widget',\r\n", - " 'nbextensions/widgets/widgets/js/manager'\r\n", - "], function (widget, manager) {\r\n", - "\r\n", - " var UpdateView = widget.DOMWidgetView.extend({\r\n", - " render: function() {\r\n", - " window.MYWIDGET = this;\r\n", - " this._interval = 0;\r\n", - " this.update();\r\n", - " },\r\n", - " update: function() {\r\n", - " this.display(this.model.get('_message'));\r\n", - " this.setInterval();\r\n", - " },\r\n", - " display: function(message) {\r\n", - " /*\r\n", - " * display method: override this for custom display logic\r\n", - " */\r\n", - " this.el.innerHTML = message;\r\n", - " },\r\n", - " remove: function() {\r\n", - " clearInterval(this._updater);\r\n", - " },\r\n", - " setInterval: function(newInterval) {\r\n", - " var me = this;\r\n", - " if(newInterval===undefined) newInterval = me.model.get('interval');\r\n", - " if(newInterval===me._interval) return;\r\n", - "\r\n", - " me._interval = newInterval;\r\n", - "\r\n", - " if(me._updater) clearInterval(me._updater);\r\n", - "\r\n", - " if(me._interval) {\r\n", - " me._updater = setInterval(function() {\r\n", - " me.send({myupdate: true});\r\n", - " if(!me.model.comm_live) {\r\n", - " console.log('missing comm, canceling widget updates', me);\r\n", - " clearInterval(me._updater);\r\n", - " }\r\n", - " }, me._interval * 1000);\r\n", - " }\r\n", - " }\r\n", - " });\r\n", - " manager.WidgetManager.register_widget_view('UpdateView', UpdateView);\r\n", - "\r\n", - " var HiddenUpdateView = UpdateView.extend({\r\n", - " display: function(message) {\r\n", - " this.$el.hide();\r\n", - " }\r\n", - " });\r\n", - " manager.WidgetManager.register_widget_view('HiddenUpdateView', HiddenUpdateView);\r\n", - "\r\n", - " var SubprocessView = UpdateView.extend({\r\n", - " render: function() {\r\n", - " var me = this;\r\n", - " me._interval = 0;\r\n", - " me._minimize = '';\r\n", - " me._restore = '';\r\n", - "\r\n", - " // max lines of output to show\r\n", - " me.maxOutputLength = 500;\r\n", - "\r\n", - " // in case there is already an outputView present,\r\n", - " // like from before restarting the kernel\r\n", - " $('.qcodes-output-view').not(me.$el).remove();\r\n", - "\r\n", - " me.$el\r\n", - " .addClass('qcodes-output-view')\r\n", - " .attr('qcodes-state', 'docked')\r\n", - " .html(\r\n", - " '
' +\r\n", - " '
' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '' +\r\n", - " '
' +\r\n", - " '
'\r\n",
-       "                );\r\n",
-       "\r\n",
-       "            me.clearButton = me.$el.find('.qcodes-clear-output');\r\n",
-       "            me.minButton = me.$el.find('.qcodes-minimize');\r\n",
-       "            me.outputArea = me.$el.find('pre');\r\n",
-       "            me.subprocessList = me.$el.find('.qcodes-process-list');\r\n",
-       "            me.abortButton = me.$el.find('.qcodes-abort-loop');\r\n",
-       "            me.processLinesButton = me.$el.find('.qcodes-processlines')\r\n",
-       "\r\n",
-       "            me.outputLines = [];\r\n",
-       "\r\n",
-       "            me.clearButton.click(function() {\r\n",
-       "                me.outputArea.html('');\r\n",
-       "                me.clearButton.addClass('disabled');\r\n",
-       "            });\r\n",
-       "\r\n",
-       "            me.abortButton.click(function() {\r\n",
-       "                me.send({abort: true});\r\n",
-       "            });\r\n",
-       "\r\n",
-       "            me.processLinesButton.click(function() {\r\n",
-       "                // toggle multiline process list display\r\n",
-       "                me.subprocessesMultiline = !me.subprocessesMultiline;\r\n",
-       "                me.showSubprocesses();\r\n",
-       "            });\r\n",
-       "\r\n",
-       "            me.$el.find('.js-state').click(function() {\r\n",
-       "                var state = this.className.substr(this.className.indexOf('qcodes'))\r\n",
-       "                        .split('-')[1].split(' ')[0];\r\n",
-       "                me.model.set('_state', state);\r\n",
-       "            });\r\n",
-       "\r\n",
-       "            $(window)\r\n",
-       "                .off('resize.qcodes')\r\n",
-       "                .on('resize.qcodes', function() {me.clipBounds();});\r\n",
-       "\r\n",
-       "            me.update();\r\n",
-       "        },\r\n",
-       "\r\n",
-       "        updateState: function() {\r\n",
-       "            var me = this,\r\n",
-       "                oldState = me.$el.attr('qcodes-state'),\r\n",
-       "                state = me.model.get('_state');\r\n",
-       "\r\n",
-       "            if(state === oldState) return;\r\n",
-       "\r\n",
-       "            setTimeout(function() {\r\n",
-       "                // not sure why I can't pop it out of the widgetarea in render, but it seems that\r\n",
-       "                // some other bit of code resets the parent after render if I do it there.\r\n",
-       "                // To be safe, just do it on every state click.\r\n",
-       "                me.$el.appendTo('body');\r\n",
-       "\r\n",
-       "                if(oldState === 'floated') {\r\n",
-       "                    console.log('here');\r\n",
-       "                    me.$el.draggable('destroy').css({left:'', top: ''});\r\n",
-       "                }\r\n",
-       "\r\n",
-       "                me.$el.attr('qcodes-state', state);\r\n",
-       "\r\n",
-       "                if(state === 'floated') {\r\n",
-       "                    me.$el\r\n",
-       "                        .draggable({stop: function() { me.clipBounds(); }})\r\n",
-       "                        .css({\r\n",
-       "                            left: window.innerWidth - me.$el.width() - 15,\r\n",
-       "                            top: window.innerHeight - me.$el.height() - 10\r\n",
-       "                        });\r\n",
-       "                }\r\n",
-       "\r\n",
-       "                // any previous highlighting is now moot\r\n",
-       "                me.$el.removeClass('qcodes-highlight');\r\n",
-       "            }, 0);\r\n",
-       "\r\n",
-       "        },\r\n",
-       "\r\n",
-       "        clipBounds: function() {\r\n",
-       "            var me = this;\r\n",
-       "            if(me.$el.attr('qcodes-state') === 'floated') {\r\n",
-       "                var bounds = me.$el[0].getBoundingClientRect(),\r\n",
-       "                    minVis = 40,\r\n",
-       "                    maxLeft = window.innerWidth - minVis,\r\n",
-       "                    minLeft = minVis - bounds.width,\r\n",
-       "                    maxTop = window.innerHeight - minVis;\r\n",
-       "\r\n",
-       "                if(bounds.left > maxLeft) me.$el.css('left', maxLeft);\r\n",
-       "                else if(bounds.left < minLeft) me.$el.css('left', minLeft);\r\n",
-       "\r\n",
-       "                if(bounds.top > maxTop) me.$el.css('top', maxTop);\r\n",
-       "                else if(bounds.top < 0) me.$el.css('top', 0);\r\n",
-       "            }\r\n",
-       "        },\r\n",
-       "\r\n",
-       "        display: function(message) {\r\n",
-       "            var me = this;\r\n",
-       "            if(message) {\r\n",
-       "                var initialScroll = me.outputArea.scrollTop();\r\n",
-       "                me.outputArea.scrollTop(me.outputArea.prop('scrollHeight'));\r\n",
-       "                var scrollBottom = me.outputArea.scrollTop();\r\n",
-       "\r\n",
-       "                if(me.$el.attr('qcodes-state') === 'minimized') {\r\n",
-       "                    // if we add text and the box is minimized, highlight the\r\n",
-       "                    // title bar to alert the user that there are new messages.\r\n",
-       "                    // remove then add the class, so we get the animation again\r\n",
-       "                    // if it's already highlighted\r\n",
-       "                    me.$el.removeClass('qcodes-highlight');\r\n",
-       "                    setTimeout(function(){\r\n",
-       "                        me.$el.addClass('qcodes-highlight');\r\n",
-       "                    }, 0);\r\n",
-       "                }\r\n",
-       "\r\n",
-       "                var newLines = message.split('\\n'),\r\n",
-       "                    out = me.outputLines,\r\n",
-       "                    outLen = out.length;\r\n",
-       "                if(outLen) out[outLen - 1] += newLines[0];\r\n",
-       "                else out.push(newLines[0]);\r\n",
-       "\r\n",
-       "                for(var i = 1; i < newLines.length; i++) {\r\n",
-       "                    out.push(newLines[i]);\r\n",
-       "                }\r\n",
-       "\r\n",
-       "                if(out.length > me.maxOutputLength) {\r\n",
-       "                    out.splice(0, out.length - me.maxOutputLength + 1,\r\n",
-       "                        '<<< Output clipped >>>');\r\n",
-       "                }\r\n",
-       "\r\n",
-       "                me.outputArea.text(out.join('\\n'));\r\n",
-       "                me.clearButton.removeClass('disabled');\r\n",
-       "\r\n",
-       "                // if we were scrolled to the bottom initially, make sure\r\n",
-       "                // we stay that way.\r\n",
-       "                me.outputArea.scrollTop(initialScroll === scrollBottom ?\r\n",
-       "                    me.outputArea.prop('scrollHeight') : initialScroll);\r\n",
-       "            }\r\n",
-       "\r\n",
-       "            me.showSubprocesses();\r\n",
-       "            me.updateState();\r\n",
-       "        },\r\n",
-       "\r\n",
-       "        showSubprocesses: function() {\r\n",
-       "            var me = this,\r\n",
-       "                replacer = me.subprocessesMultiline ? '
' : ', ',\r\n", - " processes = (me.model.get('_processes') || '')\r\n", - " .replace(/\\n/g, '>' + replacer + '<');\r\n", - "\r\n", - " if(processes) processes = '<' + processes + '>';\r\n", - " else processes = 'No subprocesses';\r\n", - "\r\n", - " me.abortButton.toggleClass('disabled', processes.indexOf('Measurement')===-1);\r\n", - "\r\n", - " me.subprocessList.html(processes);\r\n", - " }\r\n", - " });\r\n", - " manager.WidgetManager.register_widget_view('SubprocessView', SubprocessView);\r\n", - "});\r\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "%matplotlib nbagg\n", "import matplotlib.pyplot as plt\n", "\n", "from imp import reload\n", - "\n", - "import qcodes as qc\n", - "\n", - "qc.set_mp_method('spawn') # force Windows behavior on mac\n", - "\n", - "# this makes a widget in the corner of the window to show and control\n", - "# subprocesses and any output they would print to the terminal\n", - "qc.show_subprocess_widget()" + "import qcodes as qc\n" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 27, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -394,25 +42,28 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.10) in 0.04s\n" + "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.10) in 0.11s\n" ] } ], "source": [ - "v1 =vna.ZNB20('VNA', 'TCPIP0::172.20.2.234::inst0::INSTR', server_name=None)" + "# v1 =vna.ZNB20('VNA', 'TCPIP0::172.20.2.234::inst0::INSTR', server_name=None)\n", + "v1 =vna.ZNB20('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR', server_name=None)" ] }, { "cell_type": "code", - "execution_count": 29, - "metadata": {}, + "execution_count": 4, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "station = qc.Station(v1)" @@ -420,417 +71,35 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "([0.9895826882081791,\n", - " 0.9869094348257063,\n", - " 0.9857022207845295,\n", - " 0.9849716811324213,\n", - " 0.9851777067679051,\n", - " 0.9842607626269199,\n", - " 0.9829348381629962,\n", - " 0.9816473088019041,\n", - " 0.9786861283846768,\n", - " 0.977592036189242,\n", - " 0.97576729729163,\n", - " 0.9740934018474197,\n", - " 0.9729518103133459,\n", - " 0.9721370921861145,\n", - " 0.9713039723260913,\n", - " 0.9709207543511132,\n", - " 0.9708230438550919,\n", - " 0.9703666979337292,\n", - " 0.9703414381126192,\n", - " 0.9700908859391326,\n", - " 0.9699112711672869,\n", - " 0.9694166657814337,\n", - " 0.968903093853172,\n", - " 0.9682554312580419,\n", - " 0.9678450187387361,\n", - " 0.9671028614781024,\n", - " 0.9664735489036151,\n", - " 0.965510156267443,\n", - " 0.9649242678857383,\n", - " 0.9649892999018361,\n", - " 0.9644816933427587,\n", - " 0.9641921319025079,\n", - " 0.963669775695679,\n", - " 0.9631865143768873,\n", - " 0.963223910382565,\n", - " 0.963030553862949,\n", - " 0.9624009542078145,\n", - " 0.9622513785770892,\n", - " 0.9620348386109799,\n", - " 0.961453672750619,\n", - " 0.9604823245357286,\n", - " 0.9601940834710165,\n", - " 0.9594606244612337,\n", - " 0.9593216823689693,\n", - " 0.9589108029090968,\n", - " 0.9578873673683236,\n", - " 0.9573589291476022,\n", - " 0.9569588227170734,\n", - " 0.9564502087571111,\n", - " 0.955669859967192,\n", - " 0.9549499221478315,\n", - " 0.9540578765671092,\n", - " 0.9530129876524023,\n", - " 0.9517121031522534,\n", - " 0.9513873939731231,\n", - " 0.9504770541287781,\n", - " 0.9514052798363227,\n", - " 0.9519324122396933,\n", - " 0.9502438823700848,\n", - " 0.943213062343683,\n", - " 0.9371787503752951,\n", - " 0.9361742294777097,\n", - " 0.9394688962970474,\n", - " 0.9445367130494726,\n", - " 0.9472092623822433,\n", - " 0.9479792705463528,\n", - " 0.9480799944513731,\n", - " 0.9473828821678538,\n", - " 0.9455337460737451,\n", - " 0.9457087929324874,\n", - " 0.9462239698094738,\n", - " 0.9461226512723114,\n", - " 0.9455034078803901,\n", - " 0.9443907659225531,\n", - " 0.94365363844252,\n", - " 0.9430770510774368,\n", - " 0.9420973162820626,\n", - " 0.9406199004439628,\n", - " 0.9393153552545652,\n", - " 0.9383821996868444,\n", - " 0.9380895206348119,\n", - " 0.9368862835566656,\n", - " 0.9344415030119433,\n", - " 0.9311402924887935,\n", - " 0.9262748633155682,\n", - " 0.9212472780821491,\n", - " 0.9193006436522684,\n", - " 0.9201060562261194,\n", - " 0.9239782946171617,\n", - " 0.9266855495081732,\n", - " 0.9256206644137215,\n", - " 0.9230752335742844,\n", - " 0.9203336283158239,\n", - " 0.9171825974615793,\n", - " 0.9155916002239227,\n", - " 0.9154638000567723,\n", - " 0.9156386345594286,\n", - " 0.915411675480082,\n", - " 0.914080287388952,\n", - " 0.9107595174729219,\n", - " 0.9038962008036946,\n", - " 0.8927686431493355,\n", - " 0.8758581077844744,\n", - " 0.8534214948797171,\n", - " 0.8260045725743231,\n", - " 0.7947028544404089,\n", - " 0.7599259059113547,\n", - " 0.7225393521464759,\n", - " 0.6823648596301773,\n", - " 0.6393656639277121,\n", - " 0.5931113543704739,\n", - " 0.5441506885467429,\n", - " 0.4935484833551584,\n", - " 0.44340326569642374,\n", - " 0.3952782959903659,\n", - " 0.35100264189785674,\n", - " 0.31151834258445116,\n", - " 0.27700917333421426,\n", - " 0.24721802127630774,\n", - " 0.22213474407679737,\n", - " 0.20066032690466426,\n", - " 0.18234508982200742,\n", - " 0.16670859050017847,\n", - " 0.1530462944576956,\n", - " 0.1413275026823718,\n", - " 0.13058719359036725,\n", - " 0.12087065818962711,\n", - " 0.11172748051132558,\n", - " 0.10290008431824464,\n", - " 0.09465149248317925,\n", - " 0.08666126323379829,\n", - " 0.07946789030378242,\n", - " 0.0725860300638041,\n", - " 0.06631945966880881,\n", - " 0.06068450429465256,\n", - " 0.05523756855252659,\n", - " 0.05048916409687119,\n", - " 0.046112634791750706,\n", - " 0.04252924175208469,\n", - " 0.039214999209167094,\n", - " 0.03632221589554925,\n", - " 0.033960487109423926,\n", - " 0.03160439859699994,\n", - " 0.029940075621481918,\n", - " 0.02796992271519951,\n", - " 0.026643014513012757,\n", - " 0.025244254189062704,\n", - " 0.023820806664581567,\n", - " 0.022447139371216618,\n", - " 0.021050545597438262,\n", - " 0.019874088905746982,\n", - " 0.018645562732940713,\n", - " 0.01727799703196309,\n", - " 0.016002710865995674,\n", - " 0.01503032327152424,\n", - " 0.013987715380044643,\n", - " 0.01310755688557064,\n", - " 0.012115593481965576,\n", - " 0.011412071313340755,\n", - " 0.010654009997301088,\n", - " 0.009989801855445736,\n", - " 0.009477542474530308,\n", - " 0.00895126947691363,\n", - " 0.008475730814023537,\n", - " 0.008023368159711393,\n", - " 0.007653496379647708,\n", - " 0.007230465955946998,\n", - " 0.006854537670863554,\n", - " 0.006594801721280584,\n", - " 0.006223036608081843,\n", - " 0.006032662337093347,\n", - " 0.005710267944673411,\n", - " 0.0054625256725857035,\n", - " 0.005182030628555568,\n", - " 0.004905518766564076,\n", - " 0.0046588769261585745,\n", - " 0.0043293071704507005,\n", - " 0.004052557827013329,\n", - " 0.0038341147896325907,\n", - " 0.0036112055308751634,\n", - " 0.0033927249044489443,\n", - " 0.0031880555712037926,\n", - " 0.0029939316028082855,\n", - " 0.0029020418309596368,\n", - " 0.002771223631302068,\n", - " 0.002645519992786253,\n", - " 0.0025457635648588333,\n", - " 0.002479129374286534,\n", - " 0.002376064201398493,\n", - " 0.0022786035577616643,\n", - " 0.0022308097785247146,\n", - " 0.002109930637083628,\n", - " 0.0020136527194443863,\n", - " 0.001906772783176855,\n", - " 0.0018292625864839147,\n", - " 0.001689509801043019,\n", - " 0.0015958608326242783,\n", - " 0.0015611211728045495,\n", - " 0.0014947999199123089,\n", - " 0.0014254963667496704,\n", - " 0.001330926232424635],\n", - " [-0.4637218035133345,\n", - " -0.6447678115627552,\n", - " -0.8231987038344319,\n", - " -1.005804636348533,\n", - " -1.1882935648852178,\n", - " -1.3691429237764958,\n", - " -1.5501365959864954,\n", - " -1.7318710619900028,\n", - " -1.9112238200978167,\n", - " -2.090251428805007,\n", - " -2.270426256251092,\n", - " -2.4503739923681915,\n", - " -2.6297055657459634,\n", - " -2.8096638874119417,\n", - " -2.989713608670389,\n", - " 3.114134012350575,\n", - " 2.933951309345143,\n", - " 2.7535528475439186,\n", - " 2.573724473324577,\n", - " 2.3921071348300385,\n", - " 2.2111013152935564,\n", - " 2.029675538192421,\n", - " 1.8490448702823405,\n", - " 1.6675329087276884,\n", - " 1.4862310265845533,\n", - " 1.30481106145617,\n", - " 1.1238499741204908,\n", - " 0.9419879730263643,\n", - " 0.7599432241785905,\n", - " 0.578023071372694,\n", - " 0.39572932365602964,\n", - " 0.2134962483220875,\n", - " 0.03221498371449915,\n", - " -0.14953955285881196,\n", - " -0.3327929653419456,\n", - " -0.51564614920525,\n", - " -0.6982052258600608,\n", - " -0.8813233462194436,\n", - " -1.0649083152488747,\n", - " -1.250962462159386,\n", - " -1.43452833522854,\n", - " -1.6179507116440404,\n", - " -1.8019172913015926,\n", - " -1.9852294528292043,\n", - " -2.165698517660056,\n", - " -2.3487423107423595,\n", - " -2.535057992685882,\n", - " -2.7216737022961035,\n", - " -2.911143633789084,\n", - " -3.099483457041719,\n", - " 2.997380309490084,\n", - " 2.8129135867365944,\n", - " 2.6274016434130973,\n", - " 2.4407574694932386,\n", - " 2.2540668201412193,\n", - " 2.0676436180305604,\n", - " 1.8805714687074337,\n", - " 1.6924983298403085,\n", - " 1.506379287093013,\n", - " 1.316522817990967,\n", - " 1.1285859712859345,\n", - " 0.9430229614399827,\n", - " 0.7544675543855067,\n", - " 0.561407170424298,\n", - " 0.37187845683006154,\n", - " 0.18204462982356642,\n", - " -0.009185311839872331,\n", - " -0.20121323075174838,\n", - " -0.395215188944158,\n", - " -0.5892489776971399,\n", - " -0.7840904833928506,\n", - " -0.9788691849876674,\n", - " -1.1742991845436888,\n", - " -1.3694082648677244,\n", - " -1.566090331445052,\n", - " -1.7634382871097927,\n", - " -1.9619593125415025,\n", - " -2.161584877084847,\n", - " -2.362519614981662,\n", - " -2.5640987969249416,\n", - " -2.7649350836896023,\n", - " -2.966673830171186,\n", - " 3.1154444808759694,\n", - " 2.91490203419451,\n", - " 2.712423055065066,\n", - " 2.5071488143700265,\n", - " 2.3030465495064876,\n", - " 2.097088732293975,\n", - " 1.8879208728389256,\n", - " 1.6768425673161458,\n", - " 1.4661728615561327,\n", - " 1.252490772547974,\n", - " 1.0362722080809879,\n", - " 0.8180699652393353,\n", - " 0.5981768941025124,\n", - " 0.37713970887469733,\n", - " 0.15242661132471982,\n", - " -0.07693161148689913,\n", - " -0.31178895916639765,\n", - " -0.5525239192125634,\n", - " -0.7994954845539253,\n", - " -1.051127006802208,\n", - " -1.3066996237302078,\n", - " -1.5641618627388874,\n", - " -1.8230201362843257,\n", - " -2.083562947844965,\n", - " -2.343956440953144,\n", - " -2.6053730278673286,\n", - " -2.8683519178696817,\n", - " -3.132152578823861,\n", - " 2.8872920953776977,\n", - " 2.6256610329712435,\n", - " 2.370652630686787,\n", - " 2.125004799211656,\n", - " 1.8905925632084184,\n", - " 1.6691819655543654,\n", - " 1.4592803138877657,\n", - " 1.2596139542773384,\n", - " 1.0691587884262208,\n", - " 0.8873064258216831,\n", - " 0.707508835586725,\n", - " 0.5328184901807979,\n", - " 0.36144053283622934,\n", - " 0.18987860634411735,\n", - " 0.01709917167703673,\n", - " -0.15873454633721307,\n", - " -0.3352062958570742,\n", - " -0.5149910998180319,\n", - " -0.696433516908304,\n", - " -0.8750539939475772,\n", - " -1.0527366877700064,\n", - " -1.231903705283048,\n", - " -1.404871568357883,\n", - " -1.5713943458855526,\n", - " -1.7353126371336791,\n", - " -1.8964828297641938,\n", - " -2.049993269190271,\n", - " -2.203707853911233,\n", - " -2.3523604545372736,\n", - " -2.4964254305150853,\n", - " -2.6372490611548,\n", - " -2.7763903012229925,\n", - " -2.917266393033113,\n", - " -3.0577780761105644,\n", - " 3.0807529782061907,\n", - " 2.930705937428023,\n", - " 2.775350053502727,\n", - " 2.6200312491092115,\n", - " 2.4593025944789892,\n", - " 2.295699743465874,\n", - " 2.138590317088304,\n", - " 1.982356416900639,\n", - " 1.8202116829819857,\n", - " 1.6720486152538656,\n", - " 1.512464468900243,\n", - " 1.371308781495767,\n", - " 1.2375597191527623,\n", - " 1.0767866005800673,\n", - " 0.9368242043807584,\n", - " 0.7943621153773274,\n", - " 0.6538999347279995,\n", - " 0.5188922036965969,\n", - " 0.38959452139157347,\n", - " 0.24513175512537463,\n", - " 0.11132974138554518,\n", - " -0.04274015501516999,\n", - " -0.16993756552221392,\n", - " -0.3069346539085933,\n", - " -0.44679321649734777,\n", - " -0.5971430038889,\n", - " -0.7485228461463075,\n", - " -0.8899898307589587,\n", - " -1.0387652880075307,\n", - " -1.1891912793457242,\n", - " -1.3600087554236135,\n", - " -1.5001991227564042,\n", - " -1.641892938892661,\n", - " -1.796905939130636,\n", - " -1.9317395407644775,\n", - " -2.0653263071332564,\n", - " -2.2032368918755725,\n", - " -2.344697883843954,\n", - " -2.4644733459138664,\n", - " -2.6101505914591283,\n", - " -2.7193594592940613,\n", - " -2.862680284501814,\n", - " -2.9965949633329236,\n", - " 3.138945021791405,\n", - " 3.0332380708322284,\n", - " 2.874815043594395,\n", - " 2.724193928724211,\n", - " 2.6034833484710624,\n", - " 2.4433131117179867,\n", - " 2.273076475329651,\n", - " 2.142631961520665,\n", - " 1.9975653095579686,\n", - " 1.865871925122896,\n", - " 1.6868536330810981,\n", - " 1.5988409429506418,\n", - " 1.4429123365656216,\n", - " 1.2923650088441336])" + "([0.009646977277334165,\n", + " 0.0021979858842479604,\n", + " 0.011385066678388848,\n", + " 0.0036578559616232834,\n", + " 0.001534106152954764,\n", + " 0.0037237462606044343,\n", + " 0.0003013562395182502,\n", + " 0.005760142857503463,\n", + " 0.005929500614331058,\n", + " 0.0064628871815816695],\n", + " [2.4181310614736207,\n", + " 1.9080227951332198,\n", + " -0.5016603343918788,\n", + " -2.9137114727849935,\n", + " -1.7222804801278566,\n", + " 2.727342904656236,\n", + " 2.834714809822022,\n", + " 1.5005701652238659,\n", + " -1.1597524510801358,\n", + " 2.9650693082434896])" ] }, - "execution_count": 54, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -841,7 +110,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": { "collapsed": true }, @@ -855,7 +124,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 8, "metadata": { "scrolled": false }, @@ -864,27 +133,27 @@ "name": "stdout", "output_type": "stream", "text": [ + "Started at 2017-05-19 10:47:47\n", "DataSet:\n", - " mode = DataMode.LOCAL\n", - " location = '2016-07-07/14-59-21'\n", + " location = 'data/2017-05-19/#001_{name}_10-47-47'\n", " | | | \n", " Setpoint | VNA_power_set | power | (14,)\n", - " Measured | frequency | frequency | (14, 201)\n", - " Measured | VNA_magnitude | magnitude | (14, 201)\n", - " Measured | VNA_phase | phase | (14, 201)\n", - "started at 2016-07-07 14:59:22\n" + " Setpoint | frequency_set | frequency | (14, 100)\n", + " Measured | VNA_magnitude | magnitude | (14, 100)\n", + " Measured | VNA_phase | phase | (14, 100)\n", + "Finished at 2017-05-19 10:47:48\n" ] } ], "source": [ - "data1 = qc.Loop(v1.power[-15:-1:1]).each(v1.trace).run(background = False)" + "data1 = qc.Loop(v1.power[-15:-1:1]).each(v1.trace).run()" ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 9, "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [ { @@ -893,6 +162,7 @@ "/* 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", @@ -951,6 +221,9 @@ " 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", @@ -962,7 +235,6 @@ " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", " }\n", " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " fig.waiting = false;\n", " };\n", "\n", " this.imageObj.onunload = function() {\n", @@ -1021,6 +293,15 @@ " 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", @@ -1077,8 +358,9 @@ " canvas_div.css('width', width)\n", " canvas_div.css('height', height)\n", "\n", - " canvas.attr('width', width);\n", - " canvas.attr('height', height);\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", @@ -1211,10 +493,10 @@ "}\n", "\n", "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", - " var x0 = msg['x0'];\n", - " var y0 = fig.canvas.height - msg['y0'];\n", - " var x1 = msg['x1'];\n", - " var y1 = fig.canvas.height - msg['y1'];\n", + " var x0 = msg['x0'] / mpl.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", + " var x1 = msg['x1'] / mpl.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", " x0 = Math.floor(x0) + 0.5;\n", " y0 = Math.floor(y0) + 0.5;\n", " x1 = Math.floor(x1) + 0.5;\n", @@ -1293,11 +575,13 @@ " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", " evt.data);\n", " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", " return;\n", " }\n", " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", " fig.imageObj.src = evt.data;\n", " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", " return;\n", " }\n", "\n", @@ -1346,6 +630,19 @@ " return {\"x\": x, \"y\": y};\n", "};\n", "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * http://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys (original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object')\n", + " obj[key] = original[key]\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", "mpl.figure.prototype.mouse_event = function(event, name) {\n", " var canvas_pos = mpl.findpos(event)\n", "\n", @@ -1355,11 +652,12 @@ " this.canvas_div.focus();\n", " }\n", "\n", - " var x = canvas_pos.x;\n", - " var y = canvas_pos.y;\n", + " var x = canvas_pos.x * mpl.ratio;\n", + " var y = canvas_pos.y * mpl.ratio;\n", "\n", " this.send_message(name, {x: x, y: y, button: event.button,\n", - " step: event.step});\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event)});\n", "\n", " /* This prevents the web browser from automatically changing to\n", " * the text insertion cursor when the button is pressed. We want\n", @@ -1399,7 +697,8 @@ "\n", " this._key_event_extra(event, name);\n", "\n", - " this.send_message(name, {key: value});\n", + " this.send_message(name, {key: value,\n", + " guiEvent: simpleKeys(event)});\n", " return false;\n", "}\n", "\n", @@ -1416,7 +715,7 @@ "};\n", "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", "\n", - "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", "\n", "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", " // Create a \"websocket\"-like object which calls the given IPython comm\n", @@ -1475,21 +774,29 @@ "};\n", "\n", "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " var width = fig.canvas.width/mpl.ratio\n", + " fig.root.unbind('remove')\n", + "\n", " // Update the output cell to use the data from the current canvas.\n", " fig.push_to_output();\n", " var dataURL = fig.canvas.toDataURL();\n", " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", " // the notebook keyboard shortcuts fail.\n", " IPython.keyboard_manager.enable()\n", - " $(fig.parent_element).html('');\n", - " fig.send_message('closing', {});\n", - " fig.ws.close()\n", + " $(fig.parent_element).html('');\n", + " fig.close_ws(fig, msg);\n", + "}\n", + "\n", + "mpl.figure.prototype.close_ws = function(fig, msg){\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", "}\n", "\n", "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width/mpl.ratio\n", " var dataURL = this.canvas.toDataURL();\n", - " this.cell_info[1]['text/html'] = '';\n", + " this.cell_info[1]['text/html'] = '';\n", "}\n", "\n", "mpl.figure.prototype.updated_canvas_event = function() {\n", @@ -1538,14 +845,20 @@ "\n", " // Add the close button to the window.\n", " var buttongrp = $('
');\n", - " var button = $('');\n", + " var button = $('');\n", " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", - " button.mouseover('Close figure', toolbar_mouse_event);\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", @@ -1572,12 +885,9 @@ " // Check for shift+enter\n", " if (event.shiftKey && event.which == 13) {\n", " this.canvas_div.blur();\n", - " event.shiftKey = false;\n", - " // Send a \"J\" for go to next cell\n", - " event.which = 74;\n", - " event.keyCode = 74;\n", - " manager.command_mode();\n", - " manager.handle_keydown(event);\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", @@ -1626,7 +936,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1638,10 +948,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 55, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -1652,7 +962,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -1661,6 +971,7 @@ "/* 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", @@ -1719,6 +1030,9 @@ " 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", @@ -1730,7 +1044,6 @@ " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", " }\n", " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " fig.waiting = false;\n", " };\n", "\n", " this.imageObj.onunload = function() {\n", @@ -1789,6 +1102,15 @@ " 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", @@ -1845,8 +1167,9 @@ " canvas_div.css('width', width)\n", " canvas_div.css('height', height)\n", "\n", - " canvas.attr('width', width);\n", - " canvas.attr('height', height);\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", @@ -1979,10 +1302,10 @@ "}\n", "\n", "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", - " var x0 = msg['x0'];\n", - " var y0 = fig.canvas.height - msg['y0'];\n", - " var x1 = msg['x1'];\n", - " var y1 = fig.canvas.height - msg['y1'];\n", + " var x0 = msg['x0'] / mpl.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", + " var x1 = msg['x1'] / mpl.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n", " x0 = Math.floor(x0) + 0.5;\n", " y0 = Math.floor(y0) + 0.5;\n", " x1 = Math.floor(x1) + 0.5;\n", @@ -2061,11 +1384,13 @@ " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", " evt.data);\n", " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", " return;\n", " }\n", " else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", " fig.imageObj.src = evt.data;\n", " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", " return;\n", " }\n", "\n", @@ -2114,6 +1439,19 @@ " return {\"x\": x, \"y\": y};\n", "};\n", "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * http://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys (original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object')\n", + " obj[key] = original[key]\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", "mpl.figure.prototype.mouse_event = function(event, name) {\n", " var canvas_pos = mpl.findpos(event)\n", "\n", @@ -2123,11 +1461,12 @@ " this.canvas_div.focus();\n", " }\n", "\n", - " var x = canvas_pos.x;\n", - " var y = canvas_pos.y;\n", + " var x = canvas_pos.x * mpl.ratio;\n", + " var y = canvas_pos.y * mpl.ratio;\n", "\n", " this.send_message(name, {x: x, y: y, button: event.button,\n", - " step: event.step});\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event)});\n", "\n", " /* This prevents the web browser from automatically changing to\n", " * the text insertion cursor when the button is pressed. We want\n", @@ -2167,7 +1506,8 @@ "\n", " this._key_event_extra(event, name);\n", "\n", - " this.send_message(name, {key: value});\n", + " this.send_message(name, {key: value,\n", + " guiEvent: simpleKeys(event)});\n", " return false;\n", "}\n", "\n", @@ -2184,7 +1524,7 @@ "};\n", "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", "\n", - "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", "\n", "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", " // Create a \"websocket\"-like object which calls the given IPython comm\n", @@ -2243,21 +1583,29 @@ "};\n", "\n", "mpl.figure.prototype.handle_close = function(fig, msg) {\n", + " var width = fig.canvas.width/mpl.ratio\n", + " fig.root.unbind('remove')\n", + "\n", " // Update the output cell to use the data from the current canvas.\n", " fig.push_to_output();\n", " var dataURL = fig.canvas.toDataURL();\n", " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", " // the notebook keyboard shortcuts fail.\n", " IPython.keyboard_manager.enable()\n", - " $(fig.parent_element).html('');\n", - " fig.send_message('closing', {});\n", - " fig.ws.close()\n", + " $(fig.parent_element).html('');\n", + " fig.close_ws(fig, msg);\n", + "}\n", + "\n", + "mpl.figure.prototype.close_ws = function(fig, msg){\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", "}\n", "\n", "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width/mpl.ratio\n", " var dataURL = this.canvas.toDataURL();\n", - " this.cell_info[1]['text/html'] = '';\n", + " this.cell_info[1]['text/html'] = '';\n", "}\n", "\n", "mpl.figure.prototype.updated_canvas_event = function() {\n", @@ -2306,14 +1654,20 @@ "\n", " // Add the close button to the window.\n", " var buttongrp = $('
');\n", - " var button = $('');\n", + " var button = $('');\n", " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", - " button.mouseover('Close figure', toolbar_mouse_event);\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", @@ -2340,12 +1694,9 @@ " // Check for shift+enter\n", " if (event.shiftKey && event.which == 13) {\n", " this.canvas_div.blur();\n", - " event.shiftKey = false;\n", - " // Send a \"J\" for go to next cell\n", - " event.which = 74;\n", - " event.keyCode = 74;\n", - " manager.command_mode();\n", - " manager.handle_keydown(event);\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", @@ -2394,7 +1745,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -2406,10 +1757,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 53, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -2420,12 +1771,33 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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\nDps2bRKRX3/9dfr06TExMT169Dh//nxWVlZ2dvb999/v7A46x5UWgrRxo8I9LS3Ndmf+/Pne3t4P\nPvhg06ZNt23b9tlnn40YMaIeuneBMXTcnJW/Ky6yiIiY/H3ZNhUAAKAOfPrppx07duzZs6ft4aBB\ng7Zv3/71119HRUU5t2POYhtrd8iNVpUJDAwUkd27dx8+fPitt96yhW3atCktLV29enX91u4iRl9/\nCnYAAIA6dPjw4aZNm1ZOrFbrwYMHndUf54qIiNBXgbzA6kYbMNns3bu3WbNmlZNmzZrt3bu3bnoE\nAACAhtKjR4/FixdnZWWdPHmyuLg4IyMjKyvrzjvvdHa/XEL1M2caVm0minfq1GnWrFkbNmzo06eP\niPz0008LFy4cOXJkHXcNAAAA9axbt26TJk169913baPsISEh06dPv/XWW53dL+ewX5ZqM22a+68q\n07p165deemn27Nl//vOfmzRpcvr06UcfffShhx6q884BAACgvkVHR0dHR1sslsaNG9fdlprXKTea\n427Xp0+f++67r7Cw8PTp023atPHy8qrbbgEAAKAhmUwVS21/9NFHQUFBvXv3dm5/nGXo0Cten+pG\nI+5HjhwJCAiw3W/SpElwcHDlr5aWlp4+fbply5Z11jsAAADUp/T09MWLF1dOjh49+vbbbzurP84V\nERGRl5d3pdrdjQr3jRs3bt26dfDgwd27d7dfn3rmzJl9+/atWrXq888//8tf/kLhDgAA4C7uu+++\n22+/3XbfarXu3Llzz549YWFhzu2Vs9ivRnVYu7vRVJlHH330zjvvfPfdd1955RU/P79mzZqVlJQc\nO3bMy8urb9++77777g17KQMAAIA7uvnmm2+++Wb7Q7PZPGvWrM2bN9+YezBFXLJXal5eXt6liZOX\ng7zqOe4hISGzZs0qLS3dvXv3iRMnPD09/f39Q0JCGjWqzcqSAAAAcCmNGzcuLS11di+cyTbunp6u\nrSrjVLW8ONXb2/uOO+6o264AAACggRUUFOzYscP+sLCw8P/+7/9CQkI++eSTW2+9tXv37k7sm7Nc\nGGVXRtzdaY47AAAArif/+c9/vvzyy8pJeHh4Tk6OiJw5c+bGLNyvNOLuXlNlAAAAcN3o27dv3759\nnd0L1+KyI+5MTAcAALgRff3111arXoeeP39+8+bNJ0+ebOAuOV1eJenpcnnVLiLWGt/qQW1G3L/6\n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\n3DtPnAgbOdKbJQMf3Nvf3V5dbRozRkSOyURdanKspyVtLdUp2RVRaQWrUg3KBcjrAAAAEB8m9V58\nG9w7OzuPHz+1d++I66/3ZvHAB3dD6uNbU8+9mKNt213LSqKSCzYsT/D/QQEAAOA85uFZSx7nkPE2\n0/suuH9jsTjefffIwoWj77//vAnunp0489LS+OCCAqtE3T9rXFNj46lTpy6KvMI4OSJwxwYAAIDz\njDLNe3tbqpMvetw7v/rqG4vFcs89J7vbvL00fIO7ZvTlrte2A+82iYi0F2Uv6yqFp9dvXRqI4wIA\nAMD5yr2FZiingOw8eTJMo/kqL+/Y6tUDWH34Bnft5OT6+mTna51xaX09MR0AAAAD4crrg43pgxhx\n77TZjm/YYL333gFv4YKB7xwAAAA4H7iaZFwtMQPT+Y23X2etZbWeevvtw9OmDSa1y3AecQcAAAB8\npUeD+yC6Zfp5c2rnsWOdX39tWbLkxNat/VtTheAOAACAEKK6RdX3je/L7rtPTp+2FRTYfvvbwW6r\nG8EdAAAAIS0mJsbbp6h63eP+5O9+98Utt5zcsWNQR3Y2gjsAAABCmve3rnrfKfP9mTP3vPnmqb/9\nzbJo0Teffz7wg+uB4A4AAICQ5t7+7inBez+pzL59+y4YO3bkTTd96+OPv3766aM5OYM6RBFhVhkA\nAABAVFO8u+v0+qvLhReGjRo1+oEHIr/+etTChYM8QoI7AAAAQlpzc7OXPe79Du4iIhI2alTYqFHh\nTz01/v33L4qNHfBx0ioDAACA4OTNILq7lBSP2b2fs0GeJUyv1+j1l27ffvKNNyz33NNps/V3CwR3\nAAAABAllUh/83I4ug3hwapcL9PqRycmXffGF7be/teXn92tdgjsAAACCRM852l0h3vm0VJ/E98EH\ndxEJ02hERPfww2NWrLBmZHi/IsEdAAAAQaKP3hhnfO+lv2l+MK0yvYSNGRMmEv7cc53Hj3u5CsFd\nbV9/Fm7dqyi+9rCi+MsGRdFWpigmethX51eK4tZLFMWwcMXp+YMm9en5wzH/2714+Jb/516syVas\nrldt85vDZuW+rtl/pXvxdOuH7kXNFbPdi9/98RvKzY6YrijGZyrO8OO1Dvfihaqt7tuv3JVs/Iui\n+GXG1+7FB1Wrr/+eopi1VL2v4ocUxdNtiuKyXyuKz9SqN3tip6KoOhPln9sUxetK3J85Jw+4PXbO\n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\ngjsAAABwHvj/flOHmmUdY54AAAAASUVORK5CYII=\n", + "text/plain": [ + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "qc.QtPlot(data1.VNA_magnitude)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } ], "metadata": { From 5cb9ff8a7c01b4c453a7617b7f9e89503f43efcd Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 19 May 2017 11:00:27 +0200 Subject: [PATCH 22/56] Znb 20 add setpoint unit --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 2b7831ad9676..f09dd5c86496 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -36,6 +36,7 @@ def __init__(self, name, instrument, start, stop, npts): self.set_sweep(start, stop, npts) self.names = ('magnitude', 'phase') self.units = ('dBm', 'rad') + self.setpoint_units = (('Hz',), ('Hz',)) self.setpoint_names = (('frequency',), ('frequency',)) def set_sweep(self, start, stop, npts): From a71499477982d44f153b66af5ab5aca63d46c0ba Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 19 May 2017 10:37:37 +0200 Subject: [PATCH 23/56] Fix: wip towards more channels in znb20 --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 138 ++++++++++-------- 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index f09dd5c86496..836fd5c66bb5 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -1,3 +1,5 @@ +from functools import partial + from qcodes import VisaInstrument from qcodes.utils import validators as vals from cmath import phase @@ -30,10 +32,11 @@ class FrequencySweep(MultiParameter): TODO: - ability to choose for abs or db in magnitude return """ - def __init__(self, name, instrument, start, stop, npts): + def __init__(self, name, instrument, start, stop, npts, channel): super().__init__(name, names=("", ""), shapes=((), ())) self._instrument = instrument self.set_sweep(start, stop, npts) + self._channel = channel self.names = ('magnitude', 'phase') self.units = ('dBm', 'rad') self.setpoint_units = (('Hz',), ('Hz',)) @@ -47,15 +50,15 @@ def set_sweep(self, start, stop, npts): self.shapes = ((npts,), (npts,)) def get(self): - self._instrument.write('SENS1:AVER:STAT ON') - self._instrument.write('AVER:CLE') + self._instrument.write('SENS{}:AVER:STAT ON'.format(self._channel)) + self._instrument.write('SENS{}:AVER:CLE'.format(self._channel)) self._instrument.cont_meas_off() # instrument averages over its last 'avg' number of sweeps # need to ensure averaged result is returned for avgcount in range(self._instrument.avg()): self._instrument.write('INIT:IMM; *WAI') - data_str = self._instrument.ask('CALC:DATA? SDAT').split(',') + data_str = self._instrument.ask('CALC{}:DATA? SDAT'.format(self._channel)).split(',') data_list = [float(v) for v in data_str] # data_list of complex numbers [re1,im1,re2,im2...] @@ -82,56 +85,52 @@ class ZNB20(VisaInstrument): def __init__(self, name, address, **kwargs): super().__init__(name=name, address=address, **kwargs) - - self.add_parameter(name='power', - label='Power', - unit='dBm', - get_cmd='SOUR:POW?', - set_cmd='SOUR:POW {:.4f}', - get_parser=int, - vals=vals.Numbers(-150, 25)) - - self.add_parameter(name='bandwidth', - label='Bandwidth', - unit='Hz', - get_cmd='SENS:BAND?', - set_cmd='SENS:BAND {:.4f}', - get_parser=int, - vals=vals.Numbers(1, 1e6)) - - self.add_parameter(name='avg', - label='Averages', - unit='', - get_cmd='AVER:COUN?', - set_cmd='AVER:COUN {:.4f}', - get_parser=int, - vals=vals.Numbers(1, 5000)) - - self.add_parameter(name='start', - get_cmd='SENS:FREQ:START?', - set_cmd=self._set_start, - get_parser=float) - - self.add_parameter(name='stop', - get_cmd='SENS:FREQ:STOP?', - set_cmd=self._set_stop, - get_parser=float) - - self.add_parameter(name='center', - get_cmd = 'SENS:FREQ:CENT?', - set_cmd = self._set_center, - get_parser=float) - - self.add_parameter(name='span', - get_cmd = 'SENS:FREQ:SPAN?', - set_cmd=self._set_span, - get_parser=float) - - self.add_parameter(name='npts', - get_cmd='SENS:SWE:POIN?', - set_cmd=self._set_npts, - get_parser=int) - + n = 0 + + for i in range(2): + for j in range(2): + self.add_parameter(name='power{}{}'.format(i, j), + label='Power{}{}'.format(i, j), + unit='dBm', + get_cmd='SOUR{}:POW?'.format(n), + set_cmd='SOUR{}:POW {:.4f}'.format(n), + get_parser=int, + vals=vals.Numbers(-150, 25)) + self.add_parameter(name='bandwidth{}{}.format(i, j)', + label='Bandwidth{}{}.format(i, j)', + unit='Hz', + get_cmd='SENS{}:BAND?'.format(n), + set_cmd='SENS{}:BAND {:.4f}'.format(n), + get_parser=int, + vals=vals.Numbers(1, 1e6)) + self.add_parameter(name='avg{}{}'.format(i ,j), + label='Averages{}{}'.format(i ,j), + unit='', + get_cmd='SENS{}:AVER:COUN?'.format(n), + set_cmd='SENS{}:AVER:COUN {:.4f}'.format(n), + get_parser=int, + vals=vals.Numbers(1, 5000)) + self.add_parameter(name='start{}{}'.format(i ,j), + get_cmd='SENS{}:FREQ:START?'.format(n), + set_cmd=partial(self._set_start, channel=n), + get_parser=float) + self.add_parameter(name='stop{}{}'.format(i ,j), + get_cmd='SENS{}:FREQ:STOP?'.format(n), + set_cmd=partial(self._set_stop, channel=n), + get_parser=float) + self.add_parameter(name='center{}{}'.format(i ,j), + get_cmd='SENS{}:FREQ:CENT?'.format(n), + set_cmd=partial(self._set_center, channel=n), + get_parser=float) + self.add_parameter(name='span{}{}'.format(i ,j), + get_cmd = 'SENS{}:FREQ:SPAN?'.format(n), + set_cmd=partial(self._set_span, channel=n), + get_parser=float) + self.add_parameter(name='npts{}{}'.format(i ,j), + get_cmd='SENS:SWE:POIN?', + set_cmd=partial(self._set_npts, channel=n), + get_parser=int) + n += 1 self.add_parameter(name='trace', start=self.start(), stop=self.stop(), @@ -152,27 +151,38 @@ def __init__(self, name, address, **kwargs): self.initialise() self.connect_message() - def _set_start(self, val): - self.write('SENS:FREQ:START {:.4f}'.format(val)) + def _setup_s_channels(self): + """ + Sets up 4 channels with a single trace in each. + Each channel will contain one trace. + """ + n = 0 + for i in range(2): + for j in range(2): + self.write("CALC{}:PAR:SDEF 'Trc1', 'S{}{}".format(n, i, j)) + n += 1 + + def _set_start(self, val, channel): + self.write('SENS{}:FREQ:START {:.4f}'.format(channel, val)) # update setpoints for FrequencySweep param self.trace.set_sweep(val, self.stop(), self.npts()) - def _set_stop(self, val): - self.write('SENS:FREQ:STOP {:.4f}'.format(val)) + def _set_stop(self, val, channel): + self.write('SENS{}:FREQ:STOP {:.4f}'.format(channel, val)) # update setpoints for FrequencySweep param self.trace.set_sweep(self.start(), val, self.npts()) - def _set_npts(self, val): - self.write('SENS:SWE:POIN {:.4f}'.format(val)) + def _set_npts(self, val, channel): + self.write('SENS{}:SWE:POIN {:.4f}'.format(channel, val)) # update setpoints for FrequencySweep param self.trace.set_sweep(self.start(), self.stop(), val) - def _set_span(self, val): - self.write('SENS:FREQ:SPAN {:.4f}'.format(val)) + def _set_span(self, val, channel): + self.write('SENS{}:FREQ:SPAN {:.4f}'.format(channel, val)) self.trace.set_sweep(self.start(), self.stop(), self.npts()) - def _set_center(self, val): - self.write('SENS:FREQ:CENT {:.4f}'.format(val)) + def _set_center(self, val, channel): + self.write('SENS{}:FREQ:CENT {:.4f}'.format(channel, val)) self.trace.set_sweep(self.start(), self.stop(), self.npts()) def initialise(self): From b3b31fa73e2eca53dcada40f277e7e8fe1610673 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 19 May 2017 14:23:09 +0200 Subject: [PATCH 24/56] Working snm for ZNB --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 98 +++++++++++++------ 1 file changed, 66 insertions(+), 32 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 836fd5c66bb5..bba45fed4516 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -32,11 +32,12 @@ class FrequencySweep(MultiParameter): TODO: - ability to choose for abs or db in magnitude return """ - def __init__(self, name, instrument, start, stop, npts, channel): + def __init__(self, name, instrument, start, stop, npts, channel, sindex): super().__init__(name, names=("", ""), shapes=((), ())) self._instrument = instrument self.set_sweep(start, stop, npts) self._channel = channel + self._sindex = sindex self.names = ('magnitude', 'phase') self.units = ('dBm', 'rad') self.setpoint_units = (('Hz',), ('Hz',)) @@ -56,7 +57,7 @@ def get(self): # instrument averages over its last 'avg' number of sweeps # need to ensure averaged result is returned - for avgcount in range(self._instrument.avg()): + for avgcount in range(getattr(self._instrument, 'avg{}{}'.format(*self._sindex))()): self._instrument.write('INIT:IMM; *WAI') data_str = self._instrument.ask('CALC{}:DATA? SDAT'.format(self._channel)).split(',') data_list = [float(v) for v in data_str] @@ -79,35 +80,37 @@ class ZNB20(VisaInstrument): Requires FrequencySweep parameter for taking a trace TODO: - - centre/span settable for frequwncy sweep + - centre/span settable for frequency sweep - check initialisation settings and test functions """ def __init__(self, name, address, **kwargs): super().__init__(name=name, address=address, **kwargs) - n = 0 - - for i in range(2): - for j in range(2): + n = 1 + self._sindex_to_channel = {} + self._channel_to_sindex = {} + for i in range(1,3): + self._sindex_to_channel[i] = {} + for j in range(1,3): self.add_parameter(name='power{}{}'.format(i, j), label='Power{}{}'.format(i, j), unit='dBm', get_cmd='SOUR{}:POW?'.format(n), - set_cmd='SOUR{}:POW {:.4f}'.format(n), + set_cmd='SOUR{}'.format(n)+':POW {:.4f}', get_parser=int, vals=vals.Numbers(-150, 25)) - self.add_parameter(name='bandwidth{}{}.format(i, j)', - label='Bandwidth{}{}.format(i, j)', + self.add_parameter(name='bandwidth{}{}'.format(i, j), + label='Bandwidth{}{}'.format(i, j), unit='Hz', get_cmd='SENS{}:BAND?'.format(n), - set_cmd='SENS{}:BAND {:.4f}'.format(n), + set_cmd='SENS{}'.format(n)+':BAND {:.4f}', get_parser=int, vals=vals.Numbers(1, 1e6)) self.add_parameter(name='avg{}{}'.format(i ,j), label='Averages{}{}'.format(i ,j), unit='', get_cmd='SENS{}:AVER:COUN?'.format(n), - set_cmd='SENS{}:AVER:COUN {:.4f}'.format(n), + set_cmd='SENS{}'.format(n)+':AVER:COUN {:.4f}', get_parser=int, vals=vals.Numbers(1, 5000)) self.add_parameter(name='start{}{}'.format(i ,j), @@ -130,12 +133,16 @@ def __init__(self, name, address, **kwargs): get_cmd='SENS:SWE:POIN?', set_cmd=partial(self._set_npts, channel=n), get_parser=int) - n += 1 - self.add_parameter(name='trace', - start=self.start(), - stop=self.stop(), - npts=self.npts(), - parameter_class=FrequencySweep) + self.add_parameter(name='trace{}{}'.format(i, j), + start=getattr(self, 'start{}{}'.format(i, j))(), + stop=getattr(self, 'stop{}{}'.format(i, j))(), + npts=getattr(self, 'npts{}{}'.format(i, j))(), + channel=n, + sindex=(i, j), + parameter_class=FrequencySweep) + self._sindex_to_channel[i][j] = n + self._channel_to_sindex[n] = (i, j) + n += 1 self.add_function('reset', call_cmd='*RST') self.add_function('tooltip_on', call_cmd='SYST:ERR:DISP ON') @@ -149,6 +156,7 @@ def __init__(self, name, address, **kwargs): self.add_function('rf_on', call_cmd='OUTP1 ON') self.initialise() + self._setup_s_channels() self.connect_message() def _setup_s_channels(self): @@ -156,34 +164,63 @@ def _setup_s_channels(self): Sets up 4 channels with a single trace in each. Each channel will contain one trace. """ - n = 0 - for i in range(2): - for j in range(2): - self.write("CALC{}:PAR:SDEF 'Trc1', 'S{}{}".format(n, i, j)) - n += 1 + for i in range(1,3): + for j in range(1,3): + n = self._sindex_to_channel[i][j] + self.write("CALC{}:PAR:SDEF 'Trc{}', 'S{}{}'".format(n, n, i, j)) def _set_start(self, val, channel): self.write('SENS{}:FREQ:START {:.4f}'.format(channel, val)) + i, j = self._channel_to_sindex[channel] + stop = getattr(self, 'stop{}{}'.format(i, j))() + npts = getattr(self, 'npts{}{}'.format(i, j))() + trace = getattr(self, 'trace{}{}'.format(i, j)) # update setpoints for FrequencySweep param - self.trace.set_sweep(val, self.stop(), self.npts()) + trace.set_sweep(val, stop, npts) def _set_stop(self, val, channel): self.write('SENS{}:FREQ:STOP {:.4f}'.format(channel, val)) + i, j = self._channel_to_sindex[channel] + start = getattr(self, 'start{}{}'.format(i, j))() + npts = getattr(self, 'npts{}{}'.format(i, j))() + trace = getattr(self, 'trace{}{}'.format(i, j)) # update setpoints for FrequencySweep param - self.trace.set_sweep(self.start(), val, self.npts()) + trace.set_sweep(start, val, npts) def _set_npts(self, val, channel): self.write('SENS{}:SWE:POIN {:.4f}'.format(channel, val)) + i, j = self._channel_to_sindex[channel] + start = getattr(self, 'start{}{}'.format(i, j))() + stop = getattr(self, 'stop{}{}'.format(i, j))() + trace = getattr(self, 'trace{}{}'.format(i, j)) # update setpoints for FrequencySweep param - self.trace.set_sweep(self.start(), self.stop(), val) + trace.set_sweep(start, stop, val) def _set_span(self, val, channel): self.write('SENS{}:FREQ:SPAN {:.4f}'.format(channel, val)) - self.trace.set_sweep(self.start(), self.stop(), self.npts()) + i, j = self._channel_to_sindex[channel] + start = getattr(self, 'start{}{}'.format(i, j))() + stop = getattr(self, 'stop{}{}'.format(i, j))() + npts = getattr(self, 'npts{}{}'.format(i, j))() + trace = getattr(self, 'trace{}{}'.format(i, j)) + trace.set_sweep(start, stop, npts) def _set_center(self, val, channel): self.write('SENS{}:FREQ:CENT {:.4f}'.format(channel, val)) - self.trace.set_sweep(self.start(), self.stop(), self.npts()) + i, j = self._channel_to_sindex[channel] + start = getattr(self, 'start{}{}'.format(i, j))() + stop = getattr(self, 'stop{}{}'.format(i, j))() + npts = getattr(self, 'npts{}{}'.format(i, j))() + trace = getattr(self, 'trace{}{}'.format(i, j)) + trace.set_sweep(start, stop, npts) + + def _set_default_values(self): + for i in range(1,3): + for j in range(1,3): + getattr(self, 'start{}{}'.format(i,j))(1e6) + getattr(self, 'stop{}{}'.format(i, j))(2e6) + getattr(self, 'npts{}{}'.format(i, j))(10) + getattr(self, 'power{}{}'.format(i, j))(-50) def initialise(self): self.write('*RST') @@ -192,7 +229,4 @@ def initialise(self): self.write('TRIG1:SEQ:SOUR IMM') self.write('SENS1:AVER:STAT ON') self.update_display_on() - self.start(1e6) - self.stop(2e6) - self.npts(10) - self.power(-50) + self._set_default_values() \ No newline at end of file From 35e13fddd801ad15180cce2d62cdb0eeca33f7f5 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 19 May 2017 14:23:42 +0200 Subject: [PATCH 25/56] refresh znb noteboo --- ...odes example with Rohde Schwarz ZN20.ipynb | 210 ++++++++++++------ 1 file changed, 145 insertions(+), 65 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb index 080c7cd5378d..c7fa050429ac 100644 --- a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb @@ -10,7 +10,9 @@ { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "%matplotlib nbagg\n", @@ -49,7 +51,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.10) in 0.11s\n" + "1\n", + "2\n", + "3\n", + "4\n", + "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.10) in 0.23s\n" ] } ], @@ -61,6 +67,26 @@ { "cell_type": "code", "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"'Trc4,S22'\\n\"" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v1.ask('CALC4:PAR:CAT?')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, "metadata": { "collapsed": true }, @@ -71,60 +97,115 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "([0.009646977277334165,\n", - " 0.0021979858842479604,\n", - " 0.011385066678388848,\n", - " 0.0036578559616232834,\n", - " 0.001534106152954764,\n", - " 0.0037237462606044343,\n", - " 0.0003013562395182502,\n", - " 0.005760142857503463,\n", - " 0.005929500614331058,\n", - " 0.0064628871815816695],\n", - " [2.4181310614736207,\n", - " 1.9080227951332198,\n", - " -0.5016603343918788,\n", - " -2.9137114727849935,\n", - " -1.7222804801278566,\n", - " 2.727342904656236,\n", - " 2.834714809822022,\n", - " 1.5005701652238659,\n", - " -1.1597524510801358,\n", - " 2.9650693082434896])" + "{'IDN': ,\n", + " 'avg11': ,\n", + " 'avg12': ,\n", + " 'avg21': ,\n", + " 'avg22': ,\n", + " 'bandwidth11': ,\n", + " 'bandwidth12': ,\n", + " 'bandwidth21': ,\n", + " 'bandwidth22': ,\n", + " 'center11': ,\n", + " 'center12': ,\n", + " 'center21': ,\n", + " 'center22': ,\n", + " 'npts11': ,\n", + " 'npts12': ,\n", + " 'npts21': ,\n", + " 'npts22': ,\n", + " 'power11': ,\n", + " 'power12': ,\n", + " 'power21': ,\n", + " 'power22': ,\n", + " 'span11': ,\n", + " 'span12': ,\n", + " 'span21': ,\n", + " 'span22': ,\n", + " 'start11': ,\n", + " 'start12': ,\n", + " 'start21': ,\n", + " 'start22': ,\n", + " 'stop11': ,\n", + " 'stop12': ,\n", + " 'stop21': ,\n", + " 'stop22': ,\n", + " 'timeout': ,\n", + " 'trace11': ,\n", + " 'trace12': ,\n", + " 'trace21': ,\n", + " 'trace22': }" ] }, - "execution_count": 5, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "v1.trace()" + "v1.parameters" ] }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([0.994883080464622,\n", + " 0.9931391040095283,\n", + " 0.996905045122227,\n", + " 0.9786384133401488,\n", + " 1.0027617818162764,\n", + " 1.0310496085724696,\n", + " 0.9925515317235981,\n", + " 1.0108186310605263,\n", + " 0.9925872120523569,\n", + " 0.9926692143467003],\n", + " [0.05036939097186489,\n", + " 0.01764821060448947,\n", + " 0.011866393565832002,\n", + " 0.019511389866488284,\n", + " 0.01837896283827168,\n", + " 0.023404214898075926,\n", + " 0.00574883490212149,\n", + " -0.013211149195320264,\n", + " 0.0023023030330283785,\n", + " 0.011257041625421755])" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "v1.start(10e6)\n", - "v1.stop(800e6)\n", - "v1.avg(2)\n", - "v1.npts(100)" + "v1.trace11()" ] }, { "cell_type": "code", "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "v1.start11(10e6)\n", + "v1.stop11(800e6)\n", + "v1.avg11(2)\n", + "v1.npts12(100)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, "metadata": { "scrolled": false }, @@ -133,25 +214,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "Started at 2017-05-19 10:47:47\n", + "Started at 2017-05-19 14:21:38\n", "DataSet:\n", - " location = 'data/2017-05-19/#001_{name}_10-47-47'\n", - " | | | \n", - " Setpoint | VNA_power_set | power | (14,)\n", - " Setpoint | frequency_set | frequency | (14, 100)\n", - " Measured | VNA_magnitude | magnitude | (14, 100)\n", - " Measured | VNA_phase | phase | (14, 100)\n", - "Finished at 2017-05-19 10:47:48\n" + " location = 'data/2017-05-19/#006_{name}_14-21-38'\n", + " | | | \n", + " Setpoint | VNA_power11_set | power11 | (15,)\n", + " Setpoint | frequency_set | frequency | (15, 10)\n", + " Measured | VNA_magnitude | magnitude | (15, 10)\n", + " Measured | VNA_phase | phase | (15, 10)\n", + "Finished at 2017-05-19 14:21:38\n" ] } ], "source": [ - "data1 = qc.Loop(v1.power[-15:-1:1]).each(v1.trace).run()" + "data1 = qc.Loop(v1.power11.sweep(-15,-1,1)).each(v1.trace11).run()" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": { "scrolled": false }, @@ -936,7 +1017,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -948,10 +1029,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -962,7 +1043,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -1745,7 +1826,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1757,10 +1838,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -1771,25 +1852,24 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nDps2bRKRX3/9dfr06TExMT169Dh//nxWVlZ2dvb999/v7A46x5UWgrRxo8I9LS3Ndmf+/Pne3t4P\nPvhg06ZNt23b9tlnn40YMaIeuneBMXTcnJW/Ky6yiIiY/H3ZNhUAAKAOfPrppx07duzZs6ft4aBB\ng7Zv3/71119HRUU5t2POYhtrd8iNVpUJDAwUkd27dx8+fPitt96yhW3atCktLV29enX91u4iRl9/\nCnYAAIA6dPjw4aZNm1ZOrFbrwYMHndUf54qIiNBXgbzA6kYbMNns3bu3WbNmlZNmzZrt3bu3bnoE\nAACAhtKjR4/FixdnZWWdPHmyuLg4IyMjKyvrzjvvdHa/XEL1M2caVm0minfq1GnWrFkbNmzo06eP\niPz0008LFy4cOXJkHXcNAAAA9axbt26TJk169913baPsISEh06dPv/XWW53dL+ewX5ZqM22a+68q\n07p165deemn27Nl//vOfmzRpcvr06UcfffShhx6q884BAACgvkVHR0dHR1sslsaNG9fdlprXKTea\n427Xp0+f++67r7Cw8PTp023atPHy8qrbbgEAAKAhmUwVS21/9NFHQUFBvXv3dm5/nGXo0Cten+pG\nI+5HjhwJCAiw3W/SpElwcHDlr5aWlp4+fbply5Z11jsAAADUp/T09MWLF1dOjh49+vbbbzurP84V\nERGRl5d3pdrdjQr3jRs3bt26dfDgwd27d7dfn3rmzJl9+/atWrXq888//8tf/kLhDgAA4C7uu+++\n22+/3XbfarXu3Llzz549YWFhzu2Vs9ivRnVYu7vRVJlHH330zjvvfPfdd1955RU/P79mzZqVlJQc\nO3bMy8urb9++77777g17KQMAAIA7uvnmm2+++Wb7Q7PZPGvWrM2bN9+YezBFXLJXal5eXt6liZOX\ng7zqOe4hISGzZs0qLS3dvXv3iRMnPD09/f39Q0JCGjWqzcqSAAAAcCmNGzcuLS11di+cyTbunp6u\nrSrjVLW8ONXb2/uOO+6o264AAACggRUUFOzYscP+sLCw8P/+7/9CQkI++eSTW2+9tXv37k7sm7Nc\nGGVXRtzdaY47AAAArif/+c9/vvzyy8pJeHh4Tk6OiJw5c+bGLNyvNOLuXlNlAAAAcN3o27dv3759\nnd0L1+KyI+5MTAcAALgRff3111arXoeeP39+8+bNJ0+ebOAuOV1eJenpcnnVLiLWGt/qQW1G3L/6\n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\n3DtPnAgbOdKbJQMf3Nvf3V5dbRozRkSOyURdanKspyVtLdUp2RVRaQWrUg3KBcjrAAAAEB8m9V58\nG9w7OzuPHz+1d++I66/3ZvHAB3dD6uNbU8+9mKNt213LSqKSCzYsT/D/QQEAAOA85uFZSx7nkPE2\n0/suuH9jsTjefffIwoWj77//vAnunp0489LS+OCCAqtE3T9rXFNj46lTpy6KvMI4OSJwxwYAAIDz\njDLNe3tbqpMvetw7v/rqG4vFcs89J7vbvL00fIO7ZvTlrte2A+82iYi0F2Uv6yqFp9dvXRqI4wIA\nAMD5yr2FZiingOw8eTJMo/kqL+/Y6tUDWH34Bnft5OT6+mTna51xaX09MR0AAAAD4crrg43pgxhx\n77TZjm/YYL333gFv4YKB7xwAAAA4H7iaZFwtMQPT+Y23X2etZbWeevvtw9OmDSa1y3AecQcAAAB8\npUeD+yC6Zfp5c2rnsWOdX39tWbLkxNat/VtTheAOAACAEKK6RdX3je/L7rtPTp+2FRTYfvvbwW6r\nG8EdAAAAIS0mJsbbp6h63eP+5O9+98Utt5zcsWNQR3Y2gjsAAABCmve3rnrfKfP9mTP3vPnmqb/9\nzbJo0Teffz7wg+uB4A4AAICQ5t7+7inBez+pzL59+y4YO3bkTTd96+OPv3766aM5OYM6RBFhVhkA\nAABAVFO8u+v0+qvLhReGjRo1+oEHIr/+etTChYM8QoI7AAAAQlpzc7OXPe79Du4iIhI2alTYqFHh\nTz01/v33L4qNHfBx0ioDAACA4OTNILq7lBSP2b2fs0GeJUyv1+j1l27ffvKNNyz33NNps/V3CwR3\nAAAABAllUh/83I4ug3hwapcL9PqRycmXffGF7be/teXn92tdgjsAAACCRM852l0h3vm0VJ/E98EH\ndxEJ02hERPfww2NWrLBmZHi/IsEdAAAAQaKP3hhnfO+lv2l+MK0yvYSNGRMmEv7cc53Hj3u5CsFd\nbV9/Fm7dqyi+9rCi+MsGRdFWpigmethX51eK4tZLFMWwcMXp+YMm9en5wzH/2714+Jb/516syVas\nrldt85vDZuW+rtl/pXvxdOuH7kXNFbPdi9/98RvKzY6YrijGZyrO8OO1Dvfihaqt7tuv3JVs/Iui\n+GXG1+7FB1Wrr/+eopi1VL2v4ocUxdNtiuKyXyuKz9SqN3tip6KoOhPln9sUxetK3J85Jw+4PXbO\n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\ngjsAAABwHvj/flOHmmUdY54AAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "qc.QtPlot(data1.VNA_magnitude)" ] }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "v1.close()" + ] + }, { "cell_type": "code", "execution_count": null, From c16540e3c96533923b33dc7709888762ede2527c Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 19 May 2017 16:43:44 +0200 Subject: [PATCH 26/56] Add autosacle and display to vna --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index bba45fed4516..a977a503e4e0 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -152,13 +152,20 @@ def __init__(self, name, address, **kwargs): self.add_function('update_display_once', call_cmd='SYST:DISP:UPD ONCE') self.add_function('update_display_on', call_cmd='SYST:DISP:UPD ON') self.add_function('update_display_off', call_cmd='SYST:DISP:UPD OFF') + self.add_function('display_sij_split', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID 2,2') self.add_function('rf_off', call_cmd='OUTP1 OFF') self.add_function('rf_on', call_cmd='OUTP1 ON') self.initialise() self._setup_s_channels() + self.autoscale_all() self.connect_message() + + def autoscale_all(self): + for i in range(1,5): + self.write('DISPlay:TRACe1:Y:SCALe:AUTO ONCE, "Trc{}"'.format(i)) + def _setup_s_channels(self): """ Sets up 4 channels with a single trace in each. From c88b38747e7a8996479bce66e75753dbfc9947b5 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 22 May 2017 17:20:53 +0200 Subject: [PATCH 27/56] Add validators for freq settings --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index a977a503e4e0..9eefacc5604e 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -89,6 +89,8 @@ def __init__(self, name, address, **kwargs): n = 1 self._sindex_to_channel = {} self._channel_to_sindex = {} + self._max_freq = 20e9 + self._min_freq = 100e3 for i in range(1,3): self._sindex_to_channel[i] = {} for j in range(1,3): @@ -116,19 +118,23 @@ def __init__(self, name, address, **kwargs): self.add_parameter(name='start{}{}'.format(i ,j), get_cmd='SENS{}:FREQ:START?'.format(n), set_cmd=partial(self._set_start, channel=n), - get_parser=float) + get_parser=float, + vals=vals.Numbers(self._min_freq, self._max_freq-10)) self.add_parameter(name='stop{}{}'.format(i ,j), get_cmd='SENS{}:FREQ:STOP?'.format(n), set_cmd=partial(self._set_stop, channel=n), - get_parser=float) + get_parser=float, + vals = vals.Numbers(self._min_freq+1, self._max_freq)) self.add_parameter(name='center{}{}'.format(i ,j), get_cmd='SENS{}:FREQ:CENT?'.format(n), set_cmd=partial(self._set_center, channel=n), - get_parser=float) + get_parser=float, + vals=vals.Numbers(self._min_freq + 0.5, self._max_freq-10)) self.add_parameter(name='span{}{}'.format(i ,j), get_cmd = 'SENS{}:FREQ:SPAN?'.format(n), set_cmd=partial(self._set_span, channel=n), - get_parser=float) + get_parser=float, + vals = vals.Numbers(1, self._max_freq - self._min_freq)) self.add_parameter(name='npts{}{}'.format(i ,j), get_cmd='SENS:SWE:POIN?', set_cmd=partial(self._set_npts, channel=n), @@ -153,6 +159,7 @@ def __init__(self, name, address, **kwargs): self.add_function('update_display_on', call_cmd='SYST:DISP:UPD ON') self.add_function('update_display_off', call_cmd='SYST:DISP:UPD OFF') self.add_function('display_sij_split', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID 2,2') + self.add_function('display_sij_overlay', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID 1,1') self.add_function('rf_off', call_cmd='OUTP1 OFF') self.add_function('rf_on', call_cmd='OUTP1 ON') From 2823d6fc2db5111865912079e25631092ed0ac9c Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 22 May 2017 17:21:16 +0200 Subject: [PATCH 28/56] Init for all channels --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 9eefacc5604e..3cebd09eb514 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -238,9 +238,10 @@ def _set_default_values(self): def initialise(self): self.write('*RST') - self.write('SENS1:SWE:TYPE LIN') - self.write('SENS1:SWE:TIME:AUTO ON') - self.write('TRIG1:SEQ:SOUR IMM') - self.write('SENS1:AVER:STAT ON') + for n in range(1,5): + self.write('SENS{}:SWE:TYPE LIN'.format(n)) + self.write('SENS{}:SWE:TIME:AUTO ON'.format(n)) + self.write('TRIG{}:SEQ:SOUR IMM'.format(n)) + self.write('SENS{}:AVER:STAT ON'.format(n)) self.update_display_on() self._set_default_values() \ No newline at end of file From 654db6c2a5e35167790c2620ec2f414e80890322 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 23 May 2017 11:37:07 +0200 Subject: [PATCH 29/56] Ensure that start= stop: + raise ValueError("Stop frequency must be larger than start frequency.") + # we get start as the vna may not be able to set it to the exact value provided + start = getattr(self, 'start{}{}'.format(i, j))() # update setpoints for FrequencySweep param - trace.set_sweep(val, stop, npts) + trace.set_sweep(start, stop, npts) def _set_stop(self, val, channel): - self.write('SENS{}:FREQ:STOP {:.4f}'.format(channel, val)) i, j = self._channel_to_sindex[channel] start = getattr(self, 'start{}{}'.format(i, j))() npts = getattr(self, 'npts{}{}'.format(i, j))() trace = getattr(self, 'trace{}{}'.format(i, j)) + if val <= start: + raise ValueError("Stop frequency must be larger than start frequency.") + self.write('SENS{}:FREQ:STOP {:.4f}'.format(channel, val)) + # we get stop as the vna may not be able to set it to the exact value provided + stop = getattr(self, 'stop{}{}'.format(i, j))() # update setpoints for FrequencySweep param - trace.set_sweep(start, val, npts) + trace.set_sweep(start, stop, npts) def _set_npts(self, val, channel): self.write('SENS{}:SWE:POIN {:.4f}'.format(channel, val)) From 8749e2d400a6c045bcfcfac3b26032b6df205b72 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 23 May 2017 12:57:44 +0200 Subject: [PATCH 30/56] Add warnings if start and stop cannot be set exactly --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 5c34a0e72966..00771fbcbf29 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -1,4 +1,5 @@ from functools import partial +import logging from qcodes import VisaInstrument from qcodes.utils import validators as vals @@ -6,6 +7,7 @@ import numpy as np from qcodes import MultiParameter, Parameter +log = logging.getLogger(__name__) class FrequencySweep(MultiParameter): """ @@ -193,6 +195,8 @@ def _set_start(self, val, channel): raise ValueError("Stop frequency must be larger than start frequency.") # we get start as the vna may not be able to set it to the exact value provided start = getattr(self, 'start{}{}'.format(i, j))() + if val != start: + log.warning("Could not set start to {} setting it to {}".format(val, start)) # update setpoints for FrequencySweep param trace.set_sweep(start, stop, npts) @@ -206,6 +210,8 @@ def _set_stop(self, val, channel): self.write('SENS{}:FREQ:STOP {:.4f}'.format(channel, val)) # we get stop as the vna may not be able to set it to the exact value provided stop = getattr(self, 'stop{}{}'.format(i, j))() + if val != stop: + log.warning("Could not set stop to {} setting it to {}".format(val, stop)) # update setpoints for FrequencySweep param trace.set_sweep(start, stop, npts) From 8a60e779ff83c3fb703c87bbcc6bae8f7db266c4 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 24 May 2017 10:02:11 +0200 Subject: [PATCH 31/56] Znb20 small improvements --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 00771fbcbf29..311cd422c828 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -28,11 +28,8 @@ class FrequencySweep(MultiParameter): setpoint arrays of the parameter to correspond with the sweep get(): executes a sweep and returns magnitude and phase arrays - get_ramping: Queries the value of self.ramp_state and - self.ramp_time. Returns a string. - TODO: - - ability to choose for abs or db in magnitude return + - ability to choose for linear or db in magnitude return """ def __init__(self, name, instrument, start, stop, npts, channel, sindex): super().__init__(name, names=("", ""), shapes=((), ())) @@ -40,8 +37,10 @@ def __init__(self, name, instrument, start, stop, npts, channel, sindex): self.set_sweep(start, stop, npts) self._channel = channel self._sindex = sindex - self.names = ('magnitude', 'phase') - self.units = ('dBm', 'rad') + sname = 's' + str(sindex[0]) + str(sindex[1]) + self.names = ('{}_magnitude'.format(sname) , '{}_phase'.format(sname)) + self.labels = ('{} magnitude'.format(sname), '{} phase'.format(sname)) + self.units = ('', 'rad') self.setpoint_units = (('Hz',), ('Hz',)) self.setpoint_names = (('frequency',), ('frequency',)) @@ -53,6 +52,8 @@ def set_sweep(self, start, stop, npts): self.shapes = ((npts,), (npts,)) def get(self): + if not self._instrument.rf_power(): + log.warning("RF output is off") self._instrument.write('SENS{}:AVER:STAT ON'.format(self._channel)) self._instrument.write('SENS{}:AVER:CLE'.format(self._channel)) self._instrument.cont_meas_off() @@ -151,6 +152,10 @@ def __init__(self, name, address, **kwargs): self._sindex_to_channel[i][j] = n self._channel_to_sindex[n] = (i, j) n += 1 + self.add_parameter(name='rf_power', + get_cmd='OUTP1?', + set_cmd='OUTP1 {}', + val_mapping={True: '1\n', False: '0\n'}) self.add_function('reset', call_cmd='*RST') self.add_function('tooltip_on', call_cmd='SYST:ERR:DISP ON') From 592865cc6736a87b4d77b7333fa542ae1e59a018 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 24 May 2017 10:03:20 +0200 Subject: [PATCH 32/56] Update Znb20 notebook --- ...odes example with Rohde Schwarz ZN20.ipynb | 1904 ------------ ...es example with Rohde Schwarz ZNB 20.ipynb | 2741 +++++++++++++++++ 2 files changed, 2741 insertions(+), 1904 deletions(-) delete mode 100644 docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb create mode 100644 docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb deleted file mode 100644 index c7fa050429ac..000000000000 --- a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZN20.ipynb +++ /dev/null @@ -1,1904 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Qcodes example with Rohde Schwarz ZN20" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "%matplotlib nbagg\n", - "import matplotlib.pyplot as plt\n", - "\n", - "from imp import reload\n", - "import qcodes as qc\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import qcodes.instrument_drivers.rohde_schwarz.ZNB20 as vna; reload(vna)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1\n", - "2\n", - "3\n", - "4\n", - "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.10) in 0.23s\n" - ] - } - ], - "source": [ - "# v1 =vna.ZNB20('VNA', 'TCPIP0::172.20.2.234::inst0::INSTR', server_name=None)\n", - "v1 =vna.ZNB20('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR', server_name=None)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"'Trc4,S22'\\n\"" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v1.ask('CALC4:PAR:CAT?')" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "station = qc.Station(v1)" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'IDN': ,\n", - " 'avg11': ,\n", - " 'avg12': ,\n", - " 'avg21': ,\n", - " 'avg22': ,\n", - " 'bandwidth11': ,\n", - " 'bandwidth12': ,\n", - " 'bandwidth21': ,\n", - " 'bandwidth22': ,\n", - " 'center11': ,\n", - " 'center12': ,\n", - " 'center21': ,\n", - " 'center22': ,\n", - " 'npts11': ,\n", - " 'npts12': ,\n", - " 'npts21': ,\n", - " 'npts22': ,\n", - " 'power11': ,\n", - " 'power12': ,\n", - " 'power21': ,\n", - " 'power22': ,\n", - " 'span11': ,\n", - " 'span12': ,\n", - " 'span21': ,\n", - " 'span22': ,\n", - " 'start11': ,\n", - " 'start12': ,\n", - " 'start21': ,\n", - " 'start22': ,\n", - " 'stop11': ,\n", - " 'stop12': ,\n", - " 'stop21': ,\n", - " 'stop22': ,\n", - " 'timeout': ,\n", - " 'trace11': ,\n", - " 'trace12': ,\n", - " 'trace21': ,\n", - " 'trace22': }" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v1.parameters" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "([0.994883080464622,\n", - " 0.9931391040095283,\n", - " 0.996905045122227,\n", - " 0.9786384133401488,\n", - " 1.0027617818162764,\n", - " 1.0310496085724696,\n", - " 0.9925515317235981,\n", - " 1.0108186310605263,\n", - " 0.9925872120523569,\n", - " 0.9926692143467003],\n", - " [0.05036939097186489,\n", - " 0.01764821060448947,\n", - " 0.011866393565832002,\n", - " 0.019511389866488284,\n", - " 0.01837896283827168,\n", - " 0.023404214898075926,\n", - " 0.00574883490212149,\n", - " -0.013211149195320264,\n", - " 0.0023023030330283785,\n", - " 0.011257041625421755])" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "v1.trace11()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "v1.start11(10e6)\n", - "v1.stop11(800e6)\n", - "v1.avg11(2)\n", - "v1.npts12(100)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Started at 2017-05-19 14:21:38\n", - "DataSet:\n", - " location = 'data/2017-05-19/#006_{name}_14-21-38'\n", - " | | | \n", - " Setpoint | VNA_power11_set | power11 | (15,)\n", - " Setpoint | frequency_set | frequency | (15, 10)\n", - " Measured | VNA_magnitude | magnitude | (15, 10)\n", - " Measured | VNA_phase | phase | (15, 10)\n", - "Finished at 2017-05-19 14:21:38\n" - ] - } - ], - "source": [ - "data1 = qc.Loop(v1.power11.sweep(-15,-1,1)).each(v1.trace11).run()" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "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": { - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qc.MatPlot(data1.VNA_magnitude)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "qc.QtPlot(data1.VNA_magnitude)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "v1.close()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "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.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb new file mode 100644 index 000000000000..77e040d78557 --- /dev/null +++ b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb @@ -0,0 +1,2741 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Qcodes example with Rohde Schwarz ZN20" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib nbagg\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import qcodes as qc" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import qcodes.instrument_drivers.rohde_schwarz.ZNB20 as vna" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.82) in 0.28s\n" + ] + } + ], + "source": [ + "vna = vna.ZNB20('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR', server_name=None)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "station = qc.Station(vna)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The QCoDes driver for the Rohde Schwarz ZNB 20 is setup with 4 channels each containing one trace and reprecenting the 4 standars S parameters (S11, S12, S21 and S22). For each S parameter you can define a frequency sweep as and the power of the rf source i.e for s11 sweep from 100 KHz to 6 MHz in 100 steps:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "vna.start11(100e3)\n", + "vna.stop11(6e6)\n", + "vna.npts11(100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With a power of -30 dBm" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.power11(-30)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can meassure a frequency trace, first remembering to turn on the rf source." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-05-24/#009_{name}_09-59-42'\n", + " | | | \n", + " Setpoint | frequency_set | frequency | (100,)\n", + " Measured | VNA_s11_magnitude | s11_magnitude | (100,)\n", + " Measured | VNA_s11_phase | s11_phase | (100,)\n", + "acquired at 2017-05-24 09:59:43\n" + ] + } + ], + "source": [ + "vna.rf_on()\n", + "data = qc.Measure(vna.trace11).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "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" + } + ], + "source": [ + "data = qc.Measure(vna.trace11).run()\n", + "plot = qc.MatPlot(subplots=(1,2))\n", + "plot.add(data.VNA_s11_magnitude, subplot=1)\n", + "plot.add(data.VNA_s11_phase, subplot=2)\n", + "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can display all 4 S parameters in a split view on the VNA display." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "vna.display_sij_split()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or we can display all 4 parameters in one view." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "vna.display_sij_overlay()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can autoscale the scale the y axis" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "vna.autoscale_all()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is possible to switch the display update on and off" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "vna.update_display_on()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.update_display_off()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And switch the rf output on and off" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "vna.rf_on()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.rf_off()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Doing a 2D sweep is supported too" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Started at 2017-05-24 09:59:47\n", + "DataSet:\n", + " location = 'data/2017-05-24/#011_{name}_09-59-47'\n", + " | | | \n", + " Setpoint | VNA_power11_set | power11 | (15,)\n", + " Setpoint | frequency_set | frequency | (15, 100)\n", + " Measured | VNA_s11_magnitude | s11_magnitude | (15, 100)\n", + " Measured | VNA_s11_phase | s11_phase | (15, 100)\n", + "Finished at 2017-05-24 09:59:49\n" + ] + } + ], + "source": [ + "vna.rf_on()\n", + "data1 = qc.Loop(vna.power11.sweep(-15,-1,1)).each(vna.trace11).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "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" + } + ], + "source": [ + "data = qc.Measure(vna.trace11).run()\n", + "plot = qc.MatPlot(subplots=(1,2))\n", + "plot.add(data.VNA_s11_magnitude, subplot=1)\n", + "plot.add(data.VNA_s11_phase, subplot=2)\n", + "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also measure the magniture in dB." + ] + }, + { + "cell_type": "code", + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -955,12 +1780,10 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-05-24/#010_{name}_09-59-44'\n", - " | | | \n", - " Setpoint | frequency_set | frequency | (100,)\n", - " Measured | VNA_s11_magnitude | s11_magnitude | (100,)\n", - " Measured | VNA_s11_phase | s11_phase | (100,)\n", - "acquired at 2017-05-24 09:59:44\n" + " location = 'data/2017-05-24/#049_{name}_14-35-58'\n", + " | | | \n", + " Measured | VNA_tracedb11 | tracedb11 | (100,)\n", + "acquired at 2017-05-24 14:35:58\n" ] }, { @@ -1743,7 +2566,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1754,11 +2577,9 @@ } ], "source": [ - "data = qc.Measure(vna.trace11).run()\n", - "plot = qc.MatPlot(subplots=(1,2))\n", - "plot.add(data.VNA_s11_magnitude, subplot=1)\n", - "plot.add(data.VNA_s11_phase, subplot=2)\n", - "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" + "vna.rf_on()\n", + "data = qc.Measure(vna.tracedb11).run()\n", + "plot = qc.MatPlot(data.VNA_tracedb11)" ] }, { @@ -1770,7 +2591,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -1786,7 +2607,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -1802,7 +2623,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -1818,7 +2639,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -1827,7 +2648,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": { "collapsed": true }, @@ -1845,7 +2666,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -1854,7 +2675,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": { "collapsed": true }, @@ -1872,22 +2693,22 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Started at 2017-05-24 09:59:47\n", + "Started at 2017-05-24 14:36:07\n", "DataSet:\n", - " location = 'data/2017-05-24/#011_{name}_09-59-47'\n", + " location = 'data/2017-05-24/#050_{name}_14-36-07'\n", " | | | \n", " Setpoint | VNA_power11_set | power11 | (15,)\n", " Setpoint | frequency_set | frequency | (15, 100)\n", " Measured | VNA_s11_magnitude | s11_magnitude | (15, 100)\n", " Measured | VNA_s11_phase | s11_phase | (15, 100)\n", - "Finished at 2017-05-24 09:59:49\n" + "Finished at 2017-05-24 14:36:10\n" ] } ], @@ -1898,7 +2719,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -2681,7 +3502,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -2698,15 +3519,6 @@ "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, From e93ee565fb6ec075115f2aee4de0b08b684d8682 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 24 May 2017 14:38:48 +0200 Subject: [PATCH 35/56] Correct initial values --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 599bcc8120ee..828b079bc994 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -257,8 +257,8 @@ def __init__(self, name, address, **kwargs): self.add_function('rf_off', call_cmd='OUTP1 OFF') self.add_function('rf_on', call_cmd='OUTP1 ON') - self.initialise() self._setup_s_channels() + self.initialise() self.autoscale_all() self.connect_message() @@ -361,4 +361,6 @@ def initialise(self): self.write('TRIG{}:SEQ:SOUR IMM'.format(n)) self.write('SENS{}:AVER:STAT ON'.format(n)) self.update_display_on() - self._set_default_values() \ No newline at end of file + self._set_default_values() + self.rf_off() + self.display_sij_split() \ No newline at end of file From 78efee7ab8582066cecd384585d33694d2c49f20 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 14 Jun 2017 12:47:02 +0200 Subject: [PATCH 36/56] Fix: ZNB20 use channels --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 372 +++++++++--------- 1 file changed, 186 insertions(+), 186 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 828b079bc994..2172682829a5 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -1,7 +1,7 @@ -from functools import partial import logging from qcodes import VisaInstrument +from qcodes import ChannelList, InstrumentChannel from qcodes.utils import validators as vals from cmath import phase import numpy as np @@ -9,6 +9,7 @@ log = logging.getLogger(__name__) + class FrequencySweepMagPhase(MultiParameter): """ Hardware controlled parameter class for Rohde Schwarz RSZNB20 trace. @@ -39,8 +40,8 @@ def __init__(self, name, instrument, start, stop, npts, channel, sindex): self.set_sweep(start, stop, npts) self._channel = channel self._sindex = sindex - sname = 's' + str(sindex[0]) + str(sindex[1]) - self.names = ('{}_magnitude'.format(sname) , '{}_phase'.format(sname)) + sname = 'S' + str(sindex[0]) + str(sindex[1]) + self.names = ('{}_magnitude'.format(sname), '{}_phase'.format(sname)) self.labels = ('{} magnitude'.format(sname), '{} phase'.format(sname)) self.units = ('', 'rad') self.setpoint_units = (('Hz',), ('Hz',)) @@ -54,15 +55,15 @@ def set_sweep(self, start, stop, npts): self.shapes = ((npts,), (npts,)) def get(self): - if not self._instrument.rf_power(): + if not self._instrument._parent.rf_power(): log.warning("RF output is off") self._instrument.write('SENS{}:AVER:STAT ON'.format(self._channel)) self._instrument.write('SENS{}:AVER:CLE'.format(self._channel)) - self._instrument.cont_meas_off() + self._instrument._parent.cont_meas_off() # instrument averages over its last 'avg' number of sweeps # need to ensure averaged result is returned - for avgcount in range(getattr(self._instrument, 'avg{}{}'.format(*self._sindex))()): + for avgcount in range(self._instrument.avg()): self._instrument.write('INIT:IMM; *WAI') data_str = self._instrument.ask('CALC{}:DATA? SDAT'.format(self._channel)).split(',') data_list = [float(v) for v in data_str] @@ -74,7 +75,7 @@ def get(self): complex_num = complex(comp[0], comp[1]) mag_array.append(abs(complex_num)) phase_array.append(phase(complex_num)) - self._instrument.cont_meas_on() + self._instrument._parent.cont_meas_on() return mag_array, phase_array @@ -99,7 +100,7 @@ class FrequencySweep(ArrayParameter): """ def __init__(self, name, instrument, start, stop, npts, channel, sindex): - sname = 's' + str(sindex[0]) + str(sindex[1]) + sname = 'S' + str(sindex[0]) + str(sindex[1]) super().__init__(name, shape=(npts,), instrument=instrument, unit='dB', @@ -118,26 +119,178 @@ def set_sweep(self, start, stop, npts): self.shape = (npts,) def get(self): - if not self._instrument.rf_power(): + if not self._instrument._parent.rf_power(): log.warning("RF output is off") - format_param = getattr(self._instrument, 'format{}{}'.format(*self._sindex)) - old_format = format_param() - format_param('dB') + old_format = self._instrument.format() + self._instrument.format('dB') self._instrument.write('SENS{}:AVER:STAT ON'.format(self._channel)) self._instrument.write('SENS{}:AVER:CLE'.format(self._channel)) - self._instrument.cont_meas_off() + self._instrument._parent.cont_meas_off() # instrument averages over its last 'avg' number of sweeps # need to ensure averaged result is returned - for avgcount in range(getattr(self._instrument, 'avg{}{}'.format(*self._sindex))()): + for avgcount in range(self._instrument.avg()): self._instrument.write('INIT:IMM; *WAI') data_str = self._instrument.ask('CALC{}:DATA? FDAT'.format(self._channel)) data = np.array(data_str.rstrip().split(',')).astype('float64') - self._instrument.cont_meas_on() - format_param(old_format) + self._instrument._parent.cont_meas_on() + self._instrument.format(old_format) return data + +class ZNB20Channel(InstrumentChannel): + + def __init__(self, parent, name, channel, indexes): + n = channel + i = indexes[0] + j = indexes[1] + self._instrument_channel = channel + super().__init__(parent, name) + + # map hardware channel to measurement + # hardware channels are mapped one to one to qcodes channels + # we are not using sub traces within channels. + self.write("CALC{}:PAR:SDEF 'Trc{}', '{}'".format(n, n, name)) + + self.add_parameter(name='power', + label='Power', + unit='dBm', + get_cmd='SOUR{}:POW?'.format(n), + set_cmd='SOUR{}'.format(n) + ':POW {:.4f}', + get_parser=int, + vals=vals.Numbers(-150, 25)) + self.add_parameter(name='bandwidth', + label='Bandwidth', + unit='Hz', + get_cmd='SENS{}:BAND?'.format(n), + set_cmd='SENS{}'.format(n) + ':BAND {:.4f}', + get_parser=int, + vals=vals.Numbers(1, 1e6)) + self.add_parameter(name='avg', + label='Averages', + unit='', + get_cmd='SENS{}:AVER:COUN?'.format(n), + set_cmd='SENS{}'.format(n) + ':AVER:COUN {:.4f}', + get_parser=int, + vals=vals.Numbers(1, 5000)) + self.add_parameter(name='start', + get_cmd='SENS{}:FREQ:START?'.format(n), + set_cmd=self._set_start, + get_parser=float, + vals=vals.Numbers(self._parent._min_freq, self._parent._max_freq - 10)) + self.add_parameter(name='stop', + get_cmd='SENS{}:FREQ:STOP?'.format(n), + set_cmd=self._set_stop, + get_parser=float, + vals=vals.Numbers(self._parent._min_freq + 1, self._parent._max_freq)) + self.add_parameter(name='center', + get_cmd='SENS{}:FREQ:CENT?'.format(n), + set_cmd=self._set_center, + get_parser=float, + vals=vals.Numbers(self._parent._min_freq + 0.5, self._parent._max_freq - 10)) + self.add_parameter(name='span', + get_cmd='SENS{}:FREQ:SPAN?'.format(n), + set_cmd=self._set_span, + get_parser=float, + vals=vals.Numbers(1, self._parent._max_freq - self._parent._min_freq)) + self.add_parameter(name='npts', + get_cmd='SENS:SWE:POIN?', + set_cmd=self._set_npts, + get_parser=int) + self.add_parameter(name='format', + get_cmd='CALC{}:FORM?'.format(n), + set_cmd='CALC{}:FORM {{}}'.format(n), + val_mapping={'dB': 'MLOG\n', + 'Linear Magnitude': 'MLIN\n', + 'Phase': 'PHAS\n', + 'Unwr Phase': 'UPH\n', + 'Polar': 'POL\n', + 'Smith': 'SMIT\n', + 'Inverse Smith': 'ISM\n', + 'SWR': 'SWR\n', + 'Real': 'REAL\n', + 'Imaginary': 'IMAG\n', + 'Delay': "GDEL\n", + 'Complex': "COMP\n" + }) + + self.add_parameter(name='trace', + start=self.start(), + stop=self.stop(), + npts=self.npts(), + channel=n, + sindex=(i, j), + parameter_class=FrequencySweepMagPhase) + self.add_parameter(name='tracedb', + start=self.start(), + stop=self.stop(), + npts=self.npts(), + channel=n, + sindex=(i, j), + parameter_class=FrequencySweep) + + self.add_function('autoscale', call_cmd='DISPlay:TRACe1:Y:SCALe:AUTO ONCE, "Trc{}"'.format(n)) + + def _set_start(self, val): + channel = self._instrument_channel + self.write('SENS{}:FREQ:START {:.4f}'.format(channel, val)) + stop = self.stop() + npts = self.npts() + + if val >= stop: + raise ValueError("Stop frequency must be larger than start frequency.") + # we get start as the vna may not be able to set it to the exact value provided + start = self.start() + if val != start: + log.warning("Could not set start to {} setting it to {}".format(val, start)) + # update setpoints for FrequencySweep param + self.trace.set_sweep(start, stop, npts) + self.tracedb.set_sweep(start, stop, npts) + + def _set_stop(self, val): + channel = self._instrument_channel + start = self.start() + npts = self.npts() + if val <= start: + raise ValueError("Stop frequency must be larger than start frequency.") + self.write('SENS{}:FREQ:STOP {:.4f}'.format(channel, val)) + # we get stop as the vna may not be able to set it to the exact value provided + stop = self.stop() + if val != stop: + log.warning("Could not set stop to {} setting it to {}".format(val, stop)) + # update setpoints for FrequencySweep param + self.trace.set_sweep(start, stop, npts) + self.tracedb.set_sweep(start, stop, npts) + + def _set_npts(self, val): + channel = self._instrument_channel + self.write('SENS{}:SWE:POIN {:.4f}'.format(channel, val)) + start = self.start() + stop = self.stop() + # update setpoints for FrequencySweep param + self.trace.set_sweep(start, stop, val) + self.tracedb.set_sweep(start, stop, val) + + def _set_span(self, val): + channel = self._instrument_channel + self.write('SENS{}:FREQ:SPAN {:.4f}'.format(channel, val)) + start = self.start() + stop = self.stop() + npts = self.npts() + self.trace.set_sweep(start, stop, npts) + self.tracedb.set_sweep(start, stop, npts) + + def _set_center(self, val): + channel = self._instrument_channel + self.write('SENS{}:FREQ:CENT {:.4f}'.format(channel, val)) + start = self.start() + stop = self.stop() + npts = self.npts() + self.trace.set_sweep(start, stop, npts) + self.tracedb.set_sweep(start, stop, npts) + + class ZNB20(VisaInstrument): """ qcodes driver for the Rohde & Schwarz ZNB20 virtual network analyser @@ -145,7 +298,6 @@ class ZNB20(VisaInstrument): Requires FrequencySweep parameter for taking a trace TODO: - - centre/span settable for frequency sweep - check initialisation settings and test functions """ def __init__(self, name, address, **kwargs): @@ -156,89 +308,18 @@ def __init__(self, name, address, **kwargs): self._channel_to_sindex = {} self._max_freq = 20e9 self._min_freq = 100e3 - for i in range(1,3): + channels = ChannelList(self, "VNAChannels", ZNB20Channel, snapshotable=False, oneindexed=True) + for i in range(1, 3): self._sindex_to_channel[i] = {} - for j in range(1,3): - self.add_parameter(name='power{}{}'.format(i, j), - label='Power{}{}'.format(i, j), - unit='dBm', - get_cmd='SOUR{}:POW?'.format(n), - set_cmd='SOUR{}'.format(n)+':POW {:.4f}', - get_parser=int, - vals=vals.Numbers(-150, 25)) - self.add_parameter(name='bandwidth{}{}'.format(i, j), - label='Bandwidth{}{}'.format(i, j), - unit='Hz', - get_cmd='SENS{}:BAND?'.format(n), - set_cmd='SENS{}'.format(n)+':BAND {:.4f}', - get_parser=int, - vals=vals.Numbers(1, 1e6)) - self.add_parameter(name='avg{}{}'.format(i ,j), - label='Averages{}{}'.format(i ,j), - unit='', - get_cmd='SENS{}:AVER:COUN?'.format(n), - set_cmd='SENS{}'.format(n)+':AVER:COUN {:.4f}', - get_parser=int, - vals=vals.Numbers(1, 5000)) - self.add_parameter(name='start{}{}'.format(i ,j), - get_cmd='SENS{}:FREQ:START?'.format(n), - set_cmd=partial(self._set_start, channel=n), - get_parser=float, - vals=vals.Numbers(self._min_freq, self._max_freq-10)) - self.add_parameter(name='stop{}{}'.format(i ,j), - get_cmd='SENS{}:FREQ:STOP?'.format(n), - set_cmd=partial(self._set_stop, channel=n), - get_parser=float, - vals = vals.Numbers(self._min_freq+1, self._max_freq)) - self.add_parameter(name='center{}{}'.format(i ,j), - get_cmd='SENS{}:FREQ:CENT?'.format(n), - set_cmd=partial(self._set_center, channel=n), - get_parser=float, - vals=vals.Numbers(self._min_freq + 0.5, self._max_freq-10)) - self.add_parameter(name='span{}{}'.format(i ,j), - get_cmd = 'SENS{}:FREQ:SPAN?'.format(n), - set_cmd=partial(self._set_span, channel=n), - get_parser=float, - vals = vals.Numbers(1, self._max_freq - self._min_freq)) - self.add_parameter(name='npts{}{}'.format(i ,j), - get_cmd='SENS:SWE:POIN?', - set_cmd=partial(self._set_npts, channel=n), - get_parser=int) - self.add_parameter(name='format{}{}'.format(i ,j), - get_cmd='CALC{}:FORM?'.format(n), - set_cmd='CALC{}:FORM {{}}'.format(n), - val_mapping={'dB': 'MLOG\n', - 'Linear Magnitude': 'MLIN\n', - 'Phase': 'PHAS\n', - 'Unwr Phase': 'UPH\n', - 'Polar': 'POL\n', - 'Smith': 'SMIT\n', - 'Inverse Smith': 'ISM\n', - 'SWR': 'SWR\n', - 'Real': 'REAL\n', - 'Imaginary': 'IMAG\n', - 'Delay': "GDEL\n", - 'Complex': "COMP\n" - }) - - self.add_parameter(name='trace{}{}'.format(i, j), - start=getattr(self, 'start{}{}'.format(i, j))(), - stop=getattr(self, 'stop{}{}'.format(i, j))(), - npts=getattr(self, 'npts{}{}'.format(i, j))(), - channel=n, - sindex=(i, j), - parameter_class=FrequencySweepMagPhase) - self.add_parameter(name='tracedb{}{}'.format(i, j), - start=getattr(self, 'start{}{}'.format(i, j))(), - stop=getattr(self, 'stop{}{}'.format(i, j))(), - npts=getattr(self, 'npts{}{}'.format(i, j))(), - channel=n, - sindex=(i, j), - parameter_class=FrequencySweep) + for j in range(1, 3): + ch_name = 'S' + str(i) + str(j) + channel = ZNB20Channel(self, ch_name, n, indexes=(i, j)) + channels.append(channel) self._sindex_to_channel[i][j] = n self._channel_to_sindex[n] = (i, j) n += 1 - + self.add_submodule("channels", channels) + self.channels.lock() self.add_parameter(name='rf_power', get_cmd='OUTP1?', set_cmd='OUTP1 {}', @@ -257,105 +338,24 @@ def __init__(self, name, address, **kwargs): self.add_function('rf_off', call_cmd='OUTP1 OFF') self.add_function('rf_on', call_cmd='OUTP1 ON') - self._setup_s_channels() self.initialise() self.autoscale_all() self.connect_message() - def autoscale_all(self): - for i in range(1,5): - self.write('DISPlay:TRACe1:Y:SCALe:AUTO ONCE, "Trc{}"'.format(i)) - - def _setup_s_channels(self): - """ - Sets up 4 channels with a single trace in each. - Each channel will contain one trace. - """ - for i in range(1,3): - for j in range(1,3): - n = self._sindex_to_channel[i][j] - self.write("CALC{}:PAR:SDEF 'Trc{}', 'S{}{}'".format(n, n, i, j)) - - def _set_start(self, val, channel): - self.write('SENS{}:FREQ:START {:.4f}'.format(channel, val)) - i, j = self._channel_to_sindex[channel] - stop = getattr(self, 'stop{}{}'.format(i, j))() - npts = getattr(self, 'npts{}{}'.format(i, j))() - - if val >= stop: - raise ValueError("Stop frequency must be larger than start frequency.") - # we get start as the vna may not be able to set it to the exact value provided - start = getattr(self, 'start{}{}'.format(i, j))() - if val != start: - log.warning("Could not set start to {} setting it to {}".format(val, start)) - # update setpoints for FrequencySweep param - trace = getattr(self, 'trace{}{}'.format(i, j)) - trace.set_sweep(start, stop, npts) - tracedb = getattr(self, 'tracedb{}{}'.format(i, j)) - tracedb.set_sweep(start, stop, npts) - - def _set_stop(self, val, channel): - i, j = self._channel_to_sindex[channel] - start = getattr(self, 'start{}{}'.format(i, j))() - npts = getattr(self, 'npts{}{}'.format(i, j))() - trace = getattr(self, 'trace{}{}'.format(i, j)) - if val <= start: - raise ValueError("Stop frequency must be larger than start frequency.") - self.write('SENS{}:FREQ:STOP {:.4f}'.format(channel, val)) - # we get stop as the vna may not be able to set it to the exact value provided - stop = getattr(self, 'stop{}{}'.format(i, j))() - if val != stop: - log.warning("Could not set stop to {} setting it to {}".format(val, stop)) - # update setpoints for FrequencySweep param - trace.set_sweep(start, stop, npts) - tracedb = getattr(self, 'tracedb{}{}'.format(i, j)) - tracedb.set_sweep(start, stop, npts) - - def _set_npts(self, val, channel): - self.write('SENS{}:SWE:POIN {:.4f}'.format(channel, val)) - i, j = self._channel_to_sindex[channel] - start = getattr(self, 'start{}{}'.format(i, j))() - stop = getattr(self, 'stop{}{}'.format(i, j))() - trace = getattr(self, 'trace{}{}'.format(i, j)) - # update setpoints for FrequencySweep param - trace.set_sweep(start, stop, val) - tracedb = getattr(self, 'tracedb{}{}'.format(i, j)) - tracedb.set_sweep(start, stop, val) - - def _set_span(self, val, channel): - self.write('SENS{}:FREQ:SPAN {:.4f}'.format(channel, val)) - i, j = self._channel_to_sindex[channel] - start = getattr(self, 'start{}{}'.format(i, j))() - stop = getattr(self, 'stop{}{}'.format(i, j))() - npts = getattr(self, 'npts{}{}'.format(i, j))() - trace = getattr(self, 'trace{}{}'.format(i, j)) - trace.set_sweep(start, stop, npts) - tracedb = getattr(self, 'tracedb{}{}'.format(i, j)) - tracedb.set_sweep(start, stop, npts) - - def _set_center(self, val, channel): - self.write('SENS{}:FREQ:CENT {:.4f}'.format(channel, val)) - i, j = self._channel_to_sindex[channel] - start = getattr(self, 'start{}{}'.format(i, j))() - stop = getattr(self, 'stop{}{}'.format(i, j))() - npts = getattr(self, 'npts{}{}'.format(i, j))() - trace = getattr(self, 'trace{}{}'.format(i, j)) - trace.set_sweep(start, stop, npts) - tracedb = getattr(self, 'tracedb{}{}'.format(i, j)) - tracedb.set_sweep(start, stop, npts) + for channel in self.channels: + channel.autoscale() def _set_default_values(self): - for i in range(1,3): - for j in range(1,3): - getattr(self, 'start{}{}'.format(i,j))(1e6) - getattr(self, 'stop{}{}'.format(i, j))(2e6) - getattr(self, 'npts{}{}'.format(i, j))(10) - getattr(self, 'power{}{}'.format(i, j))(-50) + for channel in self.channels: + channel.start(1e6) + channel.stop(2e6) + channel.npts(10) + channel.power(-50) def initialise(self): self.write('*RST') - for n in range(1,5): + for n in range(1, 5): self.write('SENS{}:SWE:TYPE LIN'.format(n)) self.write('SENS{}:SWE:TIME:AUTO ON'.format(n)) self.write('TRIG{}:SEQ:SOUR IMM'.format(n)) @@ -363,4 +363,4 @@ def initialise(self): self.update_display_on() self._set_default_values() self.rf_off() - self.display_sij_split() \ No newline at end of file + self.display_sij_split() From 6867d922344ac8e7f47fc64b74c4318450a5cd41 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 14 Jun 2017 13:28:15 +0200 Subject: [PATCH 37/56] Fix: znb simplify string formatting --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 2172682829a5..8d2f20fadf37 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -157,14 +157,14 @@ def __init__(self, parent, name, channel, indexes): label='Power', unit='dBm', get_cmd='SOUR{}:POW?'.format(n), - set_cmd='SOUR{}'.format(n) + ':POW {:.4f}', + set_cmd='SOUR{}:POW {{:.4f}}'.format(n), get_parser=int, vals=vals.Numbers(-150, 25)) self.add_parameter(name='bandwidth', label='Bandwidth', unit='Hz', get_cmd='SENS{}:BAND?'.format(n), - set_cmd='SENS{}'.format(n) + ':BAND {:.4f}', + set_cmd='SENS{}:BAND {{:.4f}}'.format(n), get_parser=int, vals=vals.Numbers(1, 1e6)) self.add_parameter(name='avg', From 64cf3b528bf967a69c9950535d161d70fc077815 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 14 Jun 2017 14:22:54 +0200 Subject: [PATCH 38/56] znb20 less redundant passing of parameters --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 8d2f20fadf37..904dc34af86c 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -34,15 +34,15 @@ class FrequencySweepMagPhase(MultiParameter): TODO: - ability to choose for linear or db in magnitude return """ - def __init__(self, name, instrument, start, stop, npts, channel, sindex): + def __init__(self, name, instrument, start, stop, npts, channel): super().__init__(name, names=("", ""), shapes=((), ())) self._instrument = instrument self.set_sweep(start, stop, npts) self._channel = channel - self._sindex = sindex - sname = 'S' + str(sindex[0]) + str(sindex[1]) - self.names = ('{}_magnitude'.format(sname), '{}_phase'.format(sname)) - self.labels = ('{} magnitude'.format(sname), '{} phase'.format(sname)) + self.names = ('{}_magnitude'.format(instrument._vna_parameter), + '{}_phase'.format(instrument._vna_parameter)) + self.labels = ('{} magnitude'.format(instrument._vna_parameter), + '{} phase'.format(instrument._vna_parameter)) self.units = ('', 'rad') self.setpoint_units = (('Hz',), ('Hz',)) self.setpoint_names = (('frequency',), ('frequency',)) @@ -99,17 +99,15 @@ class FrequencySweep(ArrayParameter): get(): executes a sweep and returns magnitude and phase arrays """ - def __init__(self, name, instrument, start, stop, npts, channel, sindex): - sname = 'S' + str(sindex[0]) + str(sindex[1]) + def __init__(self, name, instrument, start, stop, npts, channel): super().__init__(name, shape=(npts,), instrument=instrument, unit='dB', - label='{} magnitude'.format(sname), + label='{} magnitude'.format(instrument._vna_parameter), setpoint_units=('Hz',), setpoint_names=('frequency',)) self.set_sweep(start, stop, npts) self._channel = channel - self._sindex = sindex def set_sweep(self, start, stop, npts): # needed to update config of the software parameter on sweep change @@ -141,17 +139,19 @@ def get(self): class ZNB20Channel(InstrumentChannel): - def __init__(self, parent, name, channel, indexes): + def __init__(self, parent, name, channel): n = channel - i = indexes[0] - j = indexes[1] self._instrument_channel = channel + self._tracename = "Ch{}Trc1".format(channel) + self._vna_parameter = name super().__init__(parent, name) # map hardware channel to measurement # hardware channels are mapped one to one to qcodes channels # we are not using sub traces within channels. - self.write("CALC{}:PAR:SDEF 'Trc{}', '{}'".format(n, n, name)) + self.write("CALC{}:PAR:SDEF '{}', '{}'".format(self._instrument_channel, + self._tracename, + self._vna_parameter)) self.add_parameter(name='power', label='Power', @@ -220,17 +220,16 @@ def __init__(self, parent, name, channel, indexes): stop=self.stop(), npts=self.npts(), channel=n, - sindex=(i, j), parameter_class=FrequencySweepMagPhase) self.add_parameter(name='tracedb', start=self.start(), stop=self.stop(), npts=self.npts(), channel=n, - sindex=(i, j), parameter_class=FrequencySweep) - self.add_function('autoscale', call_cmd='DISPlay:TRACe1:Y:SCALe:AUTO ONCE, "Trc{}"'.format(n)) + self.add_function('autoscale', + call_cmd='DISPlay:TRACe1:Y:SCALe:AUTO ONCE, "{}"'.format(self._tracename)) def _set_start(self, val): channel = self._instrument_channel @@ -308,12 +307,13 @@ def __init__(self, name, address, **kwargs): self._channel_to_sindex = {} self._max_freq = 20e9 self._min_freq = 100e3 - channels = ChannelList(self, "VNAChannels", ZNB20Channel, snapshotable=False, oneindexed=True) + channels = ChannelList(self, "VNAChannels", ZNB20Channel, + snapshotable=True, oneindexed=True) for i in range(1, 3): self._sindex_to_channel[i] = {} for j in range(1, 3): ch_name = 'S' + str(i) + str(j) - channel = ZNB20Channel(self, ch_name, n, indexes=(i, j)) + channel = ZNB20Channel(self, ch_name, n) channels.append(channel) self._sindex_to_channel[i][j] = n self._channel_to_sindex[n] = (i, j) From 91acb8dc26fe2d4bbb210c7a243ed50251e5e43e Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 16 Jun 2017 16:15:16 +0200 Subject: [PATCH 39/56] znb call save_val in znb20 parameters --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 904dc34af86c..4564315fea46 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -76,6 +76,7 @@ def get(self): mag_array.append(abs(complex_num)) phase_array.append(phase(complex_num)) self._instrument._parent.cont_meas_on() + self._save_val((mag_array, phase_array)) return mag_array, phase_array @@ -134,6 +135,7 @@ def get(self): self._instrument._parent.cont_meas_on() self._instrument.format(old_format) + self._save_val(data) return data From 044e194f206e999d637a5b22e63d97f155f12b6f Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Wed, 21 Jun 2017 16:17:09 +0200 Subject: [PATCH 40/56] ZNB20 remove usage of 1 indexed --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 4564315fea46..1a88808eac8f 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -310,7 +310,7 @@ def __init__(self, name, address, **kwargs): self._max_freq = 20e9 self._min_freq = 100e3 channels = ChannelList(self, "VNAChannels", ZNB20Channel, - snapshotable=True, oneindexed=True) + snapshotable=True) for i in range(1, 3): self._sindex_to_channel[i] = {} for j in range(1, 3): From 5e122106f9e73d1c59ba6a22795264de8c1f3cc9 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 22 Jun 2017 13:19:01 +0200 Subject: [PATCH 41/56] Set number of points per channel --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 1a88808eac8f..fa5ed21d32bd 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -197,7 +197,7 @@ def __init__(self, parent, name, channel): get_parser=float, vals=vals.Numbers(1, self._parent._max_freq - self._parent._min_freq)) self.add_parameter(name='npts', - get_cmd='SENS:SWE:POIN?', + get_cmd='SENS{}:SWE:POIN?'.format(n), set_cmd=self._set_npts, get_parser=int) self.add_parameter(name='format', From 5b2503c48985ec68dcd636b05c1dc590e21f4156 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 22 Jun 2017 15:26:55 +0200 Subject: [PATCH 42/56] first pass at updating notebook to channels --- ...es example with Rohde Schwarz ZNB 20.ipynb | 172 ++++++++++-------- 1 file changed, 100 insertions(+), 72 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb index d43ef5c6c032..dfa7cd7e8e7f 100644 --- a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb @@ -10,10 +10,16 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "User schema at /Users/jhn/qcodesrc_schema.json not found.User settings won't be validated\n" + ] + } + ], "source": [ "%matplotlib nbagg\n", "import matplotlib.pyplot as plt\n", @@ -24,7 +30,9 @@ { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "import qcodes.instrument_drivers.rohde_schwarz.ZNB20 as vna" @@ -39,19 +47,29 @@ "name": "stdout", "output_type": "stream", "text": [ - "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.82) in 0.17s\n" + "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.82) in 0.29s\n" ] } ], "source": [ - "vna = vna.ZNB20('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR', server_name=None)" + "vna = vna.ZNB20('VNA', 'TCPIP0::192.168.15.101::inst0::INSTR', server_name=None)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "RF output is off\n", + "RF output is off\n", + "RF output is off\n" + ] + } + ], "source": [ "station = qc.Station(vna)" ] @@ -69,9 +87,9 @@ "metadata": {}, "outputs": [], "source": [ - "vna.start11(100e3)\n", - "vna.stop11(6e6)\n", - "vna.npts11(100)" + "vna.channels[0].start(100e3)\n", + "vna.channels[0].stop(6e6)\n", + "vna.channels[0].npts(100)" ] }, { @@ -89,7 +107,7 @@ }, "outputs": [], "source": [ - "vna.power11(-30)" + "vna.channels[0].power(-30)" ] }, { @@ -109,18 +127,18 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-05-24/#047_{name}_14-35-53'\n", - " | | | \n", - " Setpoint | frequency_set | frequency | (100,)\n", - " Measured | VNA_s11_magnitude | s11_magnitude | (100,)\n", - " Measured | VNA_s11_phase | s11_phase | (100,)\n", - "acquired at 2017-05-24 14:35:53\n" + " location = 'data/2017-06-22/#002_{name}_15-22-28'\n", + " | | | \n", + " Setpoint | frequency_set | frequency | (100,)\n", + " Measured | VNA_S11_S11_magnitude | S11_magnitude | (100,)\n", + " Measured | VNA_S11_S11_phase | S11_phase | (100,)\n", + "acquired at 2017-06-22 15:22:28\n" ] } ], "source": [ "vna.rf_on()\n", - "data = qc.Measure(vna.trace11).run()" + "data = qc.Measure(vna.channels[0].trace).run()" ] }, { @@ -908,7 +926,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -920,9 +938,9 @@ ], "source": [ "plot = qc.MatPlot(subplots=(1,2))\n", - "plot.add(data.VNA_s11_magnitude, subplot=1)\n", - "plot.add(data.VNA_s11_phase, subplot=2)\n", - "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" + "plot.add(data.VNA_S11_S11_magnitude, subplot=1)\n", + "plot.add(data.VNA_S11_S11_phase, subplot=2)\n", + "plot.tight_layout()" ] }, { @@ -934,20 +952,20 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ - "vna.span11(200e3)\n", - "vna.center11(1e6)\n", - "vna.npts11(100)" + "vna.channels[0].span(200e3)\n", + "vna.channels[0].center(1e6)\n", + "vna.channels[0].npts(100)" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 13, "metadata": { "scrolled": false }, @@ -957,12 +975,12 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-05-24/#048_{name}_14-35-56'\n", - " | | | \n", - " Setpoint | frequency_set | frequency | (100,)\n", - " Measured | VNA_s11_magnitude | s11_magnitude | (100,)\n", - " Measured | VNA_s11_phase | s11_phase | (100,)\n", - "acquired at 2017-05-24 14:35:56\n" + " location = 'data/2017-06-22/#003_{name}_15-24-33'\n", + " | | | \n", + " Setpoint | frequency_set | frequency | (100,)\n", + " Measured | VNA_S11_S11_magnitude | S11_magnitude | (100,)\n", + " Measured | VNA_S11_S11_phase | S11_phase | (100,)\n", + "acquired at 2017-06-22 15:24:33\n" ] }, { @@ -1745,7 +1763,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1756,10 +1774,10 @@ } ], "source": [ - "data = qc.Measure(vna.trace11).run()\n", + "data = qc.Measure(vna.channels[0].trace).run()\n", "plot = qc.MatPlot(subplots=(1,2))\n", - "plot.add(data.VNA_s11_magnitude, subplot=1)\n", - "plot.add(data.VNA_s11_phase, subplot=2)\n", + "plot.add(data.VNA_S11_S11_magnitude, subplot=1)\n", + "plot.add(data.VNA_S11_S11_phase, subplot=2)\n", "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" ] }, @@ -1772,7 +1790,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -1780,10 +1798,10 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-05-24/#049_{name}_14-35-58'\n", - " | | | \n", - " Measured | VNA_tracedb11 | tracedb11 | (100,)\n", - "acquired at 2017-05-24 14:35:58\n" + " location = 'data/2017-06-22/#005_{name}_15-24-58'\n", + " | | | \n", + " Measured | VNA_S11_tracedb | tracedb | (100,)\n", + "acquired at 2017-06-22 15:24:59\n" ] }, { @@ -2566,7 +2584,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -2578,8 +2596,8 @@ ], "source": [ "vna.rf_on()\n", - "data = qc.Measure(vna.tracedb11).run()\n", - "plot = qc.MatPlot(data.VNA_tracedb11)" + "data = qc.Measure(vna.channels[0].tracedb).run()\n", + "plot = qc.MatPlot(data.VNA_S11_tracedb)" ] }, { @@ -2591,8 +2609,10 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": {}, + "execution_count": 16, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "vna.display_sij_split()" @@ -2607,8 +2627,10 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": {}, + "execution_count": 17, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "vna.display_sij_overlay()" @@ -2623,8 +2645,10 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": {}, + "execution_count": 18, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "vna.autoscale_all()" @@ -2639,8 +2663,10 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": {}, + "execution_count": 19, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "vna.update_display_on()" @@ -2648,7 +2674,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 20, "metadata": { "collapsed": true }, @@ -2666,8 +2692,10 @@ }, { "cell_type": "code", - "execution_count": 17, - "metadata": {}, + "execution_count": 21, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "vna.rf_on()" @@ -2675,7 +2703,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 22, "metadata": { "collapsed": true }, @@ -2693,33 +2721,33 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Started at 2017-05-24 14:36:07\n", + "Started at 2017-06-22 15:26:01\n", "DataSet:\n", - " location = 'data/2017-05-24/#050_{name}_14-36-07'\n", - " | | | \n", - " Setpoint | VNA_power11_set | power11 | (15,)\n", - " Setpoint | frequency_set | frequency | (15, 100)\n", - " Measured | VNA_s11_magnitude | s11_magnitude | (15, 100)\n", - " Measured | VNA_s11_phase | s11_phase | (15, 100)\n", - "Finished at 2017-05-24 14:36:10\n" + " location = 'data/2017-06-22/#006_{name}_15-26-01'\n", + " | | | \n", + " Setpoint | VNA_S11_power_set | power | (21,)\n", + " Setpoint | frequency_set | frequency | (21, 100)\n", + " Measured | VNA_S11_S11_magnitude | S11_magnitude | (21, 100)\n", + " Measured | VNA_S11_S11_phase | S11_phase | (21, 100)\n", + "Finished at 2017-06-22 15:26:05\n" ] } ], "source": [ "vna.rf_on()\n", - "data1 = qc.Loop(vna.power11.sweep(-15,-1,1)).each(vna.trace11).run()" + "data1 = qc.Loop(vna.channels[0].power.sweep(-50,-30,1)).each(vna.channels[0].trace).run()" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -3502,7 +3530,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -3514,8 +3542,8 @@ ], "source": [ "plot = qc.MatPlot(subplots=(1,2))\n", - "plot.add(data1.VNA_s11_magnitude, subplot=1)\n", - "plot.add(data1.VNA_s11_phase, subplot=2)\n", + "plot.add(data1.VNA_S11_S11_magnitude, subplot=1)\n", + "plot.add(data1.VNA_S11_S11_phase, subplot=2)\n", "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" ] }, From fef30151034d80e1d7f52745c0b1500414c8e330 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Thu, 22 Jun 2017 17:18:58 +0200 Subject: [PATCH 43/56] Ensure the right parameter is measured --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index fa5ed21d32bd..5f9d6c19705b 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -57,6 +57,14 @@ def set_sweep(self, start, stop, npts): def get(self): if not self._instrument._parent.rf_power(): log.warning("RF output is off") + # it is possible that the instrument and qcodes disagree about + # which parameter is measured on this channel + instrument_parameter = self._instrument.vna_parameter()[1:-2] + # trime quotes and newline + if instrument_parameter != self._instrument._vna_parameter: + raise RuntimeError("Invalid parameter. Tried to measure " + "{} got {}".format(self._instrument._vna_parameter, + instrument_parameter)) self._instrument.write('SENS{}:AVER:STAT ON'.format(self._channel)) self._instrument.write('SENS{}:AVER:CLE'.format(self._channel)) self._instrument._parent.cont_meas_off() @@ -120,6 +128,14 @@ def set_sweep(self, start, stop, npts): def get(self): if not self._instrument._parent.rf_power(): log.warning("RF output is off") + # it is possible that the instrument and qcodes disagree about + # which parameter is measured on this channel + instrument_parameter = self._instrument.vna_parameter()[1:-2] + # trime quotes and newline + if instrument_parameter != self._instrument._vna_parameter: + raise RuntimeError("Invalid parameter. Tried to measure " + "{} got {}".format(self._instrument._vna_parameter, + instrument_parameter)) old_format = self._instrument.format() self._instrument.format('dB') self._instrument.write('SENS{}:AVER:STAT ON'.format(self._channel)) @@ -144,7 +160,7 @@ class ZNB20Channel(InstrumentChannel): def __init__(self, parent, name, channel): n = channel self._instrument_channel = channel - self._tracename = "Ch{}Trc1".format(channel) + self._tracename = "Trc{}".format(channel) self._vna_parameter = name super().__init__(parent, name) @@ -155,6 +171,11 @@ def __init__(self, parent, name, channel): self._tracename, self._vna_parameter)) + self.add_parameter(name='vna_parameter', + label='VNA parameter', + get_cmd="CALC{}:PAR:MEAS? '{}'".format(self._instrument_channel, + self._tracename)) + self.add_parameter(name='power', label='Power', unit='dBm', @@ -356,7 +377,6 @@ def _set_default_values(self): channel.power(-50) def initialise(self): - self.write('*RST') for n in range(1, 5): self.write('SENS{}:SWE:TYPE LIN'.format(n)) self.write('SENS{}:SWE:TIME:AUTO ON'.format(n)) From 80395cb0a19ae58e9806f55918539c1ad51cd09b Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 23 Jun 2017 10:21:02 +0200 Subject: [PATCH 44/56] Add support for instruments with variable number of ports --- qcodes/instrument_drivers/rohde_schwarz/ZNB20.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 5f9d6c19705b..2851f0dd54a5 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -330,11 +330,15 @@ def __init__(self, name, address, **kwargs): self._channel_to_sindex = {} self._max_freq = 20e9 self._min_freq = 100e3 + self.add_parameter(name='num_ports', + get_cmd='INST:PORT:COUN?', + get_parser=int) + num_ports = self.num_ports() channels = ChannelList(self, "VNAChannels", ZNB20Channel, snapshotable=True) - for i in range(1, 3): + for i in range(1, num_ports+1): self._sindex_to_channel[i] = {} - for j in range(1, 3): + for j in range(1, num_ports+1): ch_name = 'S' + str(i) + str(j) channel = ZNB20Channel(self, ch_name, n) channels.append(channel) @@ -343,6 +347,7 @@ def __init__(self, name, address, **kwargs): n += 1 self.add_submodule("channels", channels) self.channels.lock() + self.add_parameter(name='rf_power', get_cmd='OUTP1?', set_cmd='OUTP1 {}', @@ -356,7 +361,7 @@ def __init__(self, name, address, **kwargs): self.add_function('update_display_once', call_cmd='SYST:DISP:UPD ONCE') self.add_function('update_display_on', call_cmd='SYST:DISP:UPD ON') self.add_function('update_display_off', call_cmd='SYST:DISP:UPD OFF') - self.add_function('display_sij_split', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID 2,2') + self.add_function('display_sij_split', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID {},{}'.format(num_ports, num_ports)) self.add_function('display_sij_overlay', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID 1,1') self.add_function('rf_off', call_cmd='OUTP1 OFF') self.add_function('rf_on', call_cmd='OUTP1 ON') From a77783a7a01407648ccf351c851cce49d688370b Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 23 Jun 2017 11:45:11 +0200 Subject: [PATCH 45/56] Handle different max/min freqs --- .../instrument_drivers/rohde_schwarz/ZNB20.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py index 2851f0dd54a5..22cda6c514a8 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py @@ -328,8 +328,23 @@ def __init__(self, name, address, **kwargs): n = 1 self._sindex_to_channel = {} self._channel_to_sindex = {} - self._max_freq = 20e9 - self._min_freq = 100e3 + + # TODO(JHN) I could not find a way to get max and min freq from + # the API, if that is possible replace below with that + # See page 1025 in the manual. 7.3.15.10 for details of max/min freq + # no attempt to support ZNB40, not clear without one how the format + # is due to variants + model = self.get_idn()['model'].split('-')[0] + # format seems to be ZNB8-4Port + if model == 'ZNB4': + self._max_freq = 4.5e9 + self._min_freq = 9e3 + elif model == 'ZNB8': + self._max_freq = 8.5e9 + self._min_freq = 9e3 + elif model == 'ZNB20': + self._max_freq = 20e9 + self._min_freq = 100e3 self.add_parameter(name='num_ports', get_cmd='INST:PORT:COUN?', get_parser=int) From a820376d131382b9bdd3675cd886186503a849f8 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Fri, 23 Jun 2017 16:15:04 +0200 Subject: [PATCH 46/56] Rename driver and ensure that we are waiting on the right channel for avg --- .../rohde_schwarz/{ZNB20.py => ZNB.py} | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) rename qcodes/instrument_drivers/rohde_schwarz/{ZNB20.py => ZNB.py} (95%) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py similarity index 95% rename from qcodes/instrument_drivers/rohde_schwarz/ZNB20.py rename to qcodes/instrument_drivers/rohde_schwarz/ZNB.py index 22cda6c514a8..f89756b6eb32 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB20.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py @@ -12,7 +12,7 @@ class FrequencySweepMagPhase(MultiParameter): """ - Hardware controlled parameter class for Rohde Schwarz RSZNB20 trace. + Hardware controlled parameter class for Rohde Schwarz ZNB trace. Instrument returns an list of transmission data in the form of a list of complex numbers taken from a frequency sweep. @@ -39,8 +39,8 @@ def __init__(self, name, instrument, start, stop, npts, channel): self._instrument = instrument self.set_sweep(start, stop, npts) self._channel = channel - self.names = ('{}_magnitude'.format(instrument._vna_parameter), - '{}_phase'.format(instrument._vna_parameter)) + self.names = ('magnitude', + 'phase') self.labels = ('{} magnitude'.format(instrument._vna_parameter), '{} phase'.format(instrument._vna_parameter)) self.units = ('', 'rad') @@ -72,7 +72,7 @@ def get(self): # instrument averages over its last 'avg' number of sweeps # need to ensure averaged result is returned for avgcount in range(self._instrument.avg()): - self._instrument.write('INIT:IMM; *WAI') + self._instrument.write('INIT{}:IMM; *WAI'.format(self._channel)) data_str = self._instrument.ask('CALC{}:DATA? SDAT'.format(self._channel)).split(',') data_list = [float(v) for v in data_str] @@ -90,10 +90,10 @@ def get(self): class FrequencySweep(ArrayParameter): """ - Hardware controlled parameter class for Rohde Schwarz RSZNB20 trace. + Hardware controlled parameter class for Rohde Schwarz ZNB trace. - Instrument returns an array of transmission or reflection data depending on the - active measurement. + Instrument returns an array of transmission or reflection data depending + on the active measurement. Args: name: parameter name @@ -145,7 +145,7 @@ def get(self): # instrument averages over its last 'avg' number of sweeps # need to ensure averaged result is returned for avgcount in range(self._instrument.avg()): - self._instrument.write('INIT:IMM; *WAI') + self._instrument.write('INIT{}:IMM; *WAI'.format(self._channel)) data_str = self._instrument.ask('CALC{}:DATA? FDAT'.format(self._channel)) data = np.array(data_str.rstrip().split(',')).astype('float64') @@ -155,7 +155,7 @@ def get(self): return data -class ZNB20Channel(InstrumentChannel): +class ZNBChannel(InstrumentChannel): def __init__(self, parent, name, channel): n = channel @@ -194,7 +194,7 @@ def __init__(self, parent, name, channel): label='Averages', unit='', get_cmd='SENS{}:AVER:COUN?'.format(n), - set_cmd='SENS{}'.format(n) + ':AVER:COUN {:.4f}', + set_cmd='SENS{}:AVER:COUN {{:.4f}}'.format(n), get_parser=int, vals=vals.Numbers(1, 5000)) self.add_parameter(name='start', @@ -313,9 +313,11 @@ def _set_center(self, val): self.tracedb.set_sweep(start, stop, npts) -class ZNB20(VisaInstrument): +class ZNB(VisaInstrument): """ - qcodes driver for the Rohde & Schwarz ZNB20 virtual network analyser + qcodes driver for the Rohde & Schwarz ZNB8 and ZNB20 + virtual network analyser. It can probably be extended to ZNB4 and 40 + without too much work. Requires FrequencySweep parameter for taking a trace @@ -349,13 +351,13 @@ def __init__(self, name, address, **kwargs): get_cmd='INST:PORT:COUN?', get_parser=int) num_ports = self.num_ports() - channels = ChannelList(self, "VNAChannels", ZNB20Channel, + channels = ChannelList(self, "VNAChannels", ZNBChannel, snapshotable=True) for i in range(1, num_ports+1): self._sindex_to_channel[i] = {} for j in range(1, num_ports+1): ch_name = 'S' + str(i) + str(j) - channel = ZNB20Channel(self, ch_name, n) + channel = ZNBChannel(self, ch_name, n) channels.append(channel) self._sindex_to_channel[i][j] = n self._channel_to_sindex[n] = (i, j) From 9b72a72e03b92c209f389e8c57e97468f385928e Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 26 Jun 2017 10:29:32 +0200 Subject: [PATCH 47/56] fix: remove autoscale all and name freq axis Autoscale all is redundant since it can now be done with channels the setpoints need to be different for different channels since we may not use the same freq axis for all --- qcodes/instrument_drivers/rohde_schwarz/ZNB.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py index f89756b6eb32..81fea9a927cb 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py @@ -114,7 +114,7 @@ def __init__(self, name, instrument, start, stop, npts, channel): unit='dB', label='{} magnitude'.format(instrument._vna_parameter), setpoint_units=('Hz',), - setpoint_names=('frequency',)) + setpoint_names=('{} frequency'.format(instrument._vna_parameter),)) self.set_sweep(start, stop, npts) self._channel = channel @@ -384,12 +384,8 @@ def __init__(self, name, address, **kwargs): self.add_function('rf_on', call_cmd='OUTP1 ON') self.initialise() - self.autoscale_all() self.connect_message() - - def autoscale_all(self): - for channel in self.channels: - channel.autoscale() + self.channels.autoscale() def _set_default_values(self): for channel in self.channels: From 2cd121dc7c17b0b3c311be528fc920c13f35adee Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 26 Jun 2017 10:57:58 +0200 Subject: [PATCH 48/56] Update example notebook --- ...es example with Rohde Schwarz ZNB 20.ipynb | 3581 ----------- ...codes example with Rohde Schwarz ZNB.ipynb | 5342 +++++++++++++++++ 2 files changed, 5342 insertions(+), 3581 deletions(-) delete mode 100644 docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb create mode 100644 docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb deleted file mode 100644 index dfa7cd7e8e7f..000000000000 --- a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB 20.ipynb +++ /dev/null @@ -1,3581 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Qcodes example with Rohde Schwarz ZN20" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "User schema at /Users/jhn/qcodesrc_schema.json not found.User settings won't be validated\n" - ] - } - ], - "source": [ - "%matplotlib nbagg\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import qcodes as qc" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "import qcodes.instrument_drivers.rohde_schwarz.ZNB20 as vna" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Connected to: Rohde-Schwarz ZNB20-2Port (serial:1311601062101551, firmware:2.82) in 0.29s\n" - ] - } - ], - "source": [ - "vna = vna.ZNB20('VNA', 'TCPIP0::192.168.15.101::inst0::INSTR', server_name=None)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "RF output is off\n", - "RF output is off\n", - "RF output is off\n" - ] - } - ], - "source": [ - "station = qc.Station(vna)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The QCoDes driver for the Rohde Schwarz ZNB 20 is setup with 4 channels each containing one trace and reprecenting the 4 standars S parameters (S11, S12, S21 and S22). For each S parameter you can define a frequency sweep as and the power of the rf source i.e for s11 sweep from 100 KHz to 6 MHz in 100 steps:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "vna.channels[0].start(100e3)\n", - "vna.channels[0].stop(6e6)\n", - "vna.channels[0].npts(100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With a power of -30 dBm" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "vna.channels[0].power(-30)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can meassure a frequency trace, first remembering to turn on the rf source. This produces both a linear magnitude and phase" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DataSet:\n", - " location = 'data/2017-06-22/#002_{name}_15-22-28'\n", - " | | | \n", - " Setpoint | frequency_set | frequency | (100,)\n", - " Measured | VNA_S11_S11_magnitude | S11_magnitude | (100,)\n", - " Measured | VNA_S11_S11_phase | S11_phase | (100,)\n", - "acquired at 2017-06-22 15:22:28\n" - ] - } - ], - "source": [ - "vna.rf_on()\n", - "data = qc.Measure(vna.channels[0].trace).run()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "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" - } - ], - "source": [ - "data = qc.Measure(vna.channels[0].trace).run()\n", - "plot = qc.MatPlot(subplots=(1,2))\n", - "plot.add(data.VNA_S11_S11_magnitude, subplot=1)\n", - "plot.add(data.VNA_S11_S11_phase, subplot=2)\n", - "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also measure the magniture in dB." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "DataSet:\n", - " location = 'data/2017-06-22/#005_{name}_15-24-58'\n", - " | | | \n", - " Measured | VNA_S11_tracedb | tracedb | (100,)\n", - "acquired at 2017-06-22 15:24:59\n" - ] - }, - { - "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" - } - ], - "source": [ - "plot = qc.MatPlot(subplots=(1,2))\n", - "plot.add(data1.VNA_S11_S11_magnitude, subplot=1)\n", - "plot.add(data1.VNA_S11_S11_phase, subplot=2)\n", - "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "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.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb new file mode 100644 index 000000000000..2658596b7826 --- /dev/null +++ b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb @@ -0,0 +1,5342 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Qcodes example with Rohde Schwarz ZN20/8" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "User schema at /Users/jhn/qcodesrc_schema.json not found.User settings won't be validated\n" + ] + } + ], + "source": [ + "%matplotlib nbagg\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import qcodes as qc" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import qcodes.instrument_drivers.rohde_schwarz.ZNB as vna" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 1.04s\n" + ] + } + ], + "source": [ + "vna = vna.ZNB('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR', server_name=None)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To use the VNA we likely want to turn on RF Power, which is off by default for safty in this driver. The driver default to low power but for safety lets set it to -50 dBm. The rf can be turned on and off globally and the power adjusted individually for each channel." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jhn/src/Qcodes/qcodes/instrument/parameter.py:634: UserWarning: MultiParameters do not fully support set at this time.\n", + " warnings.warn('MultiParameters do not fully support set '\n" + ] + } + ], + "source": [ + "vna.channels.power(-50)\n", + "vna.rf_on()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we have turned on rf power it's likely that the display output is out of scale so lets force a autoscale for all channels" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.channels.autoscale()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "station = qc.Station(vna)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The QCoDes driver for the Rohde Schwarz ZNB(8/20) is setup with num_ports*num_ports channels each containing one trace and reprecenting the standard S parameters (S11, S12, S21, S22 etc). For each S parameter you can define a frequency sweep as and the power of the rf source i.e for s11 sweep from 100 KHz to 6 MHz in 100 steps:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.channels.S11.start(100e3)\n", + "vna.channels.S11.stop(6e6)\n", + "vna.channels.S11.npts(100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With a power of -30 dBm" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.channels.S11.power(-30)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can meassure a frequency trace, first remembering to turn on the rf source. This produces both a linear magnitude and phase" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-26/#010_{name}_10-40-16'\n", + " | | | \n", + " Setpoint | frequency_set | frequency | (100,)\n", + " Measured | VNA_S11_magnitude | magnitude | (100,)\n", + " Measured | VNA_S11_phase | phase | (100,)\n", + "acquired at 2017-06-26 10:40:17\n" + ] + } + ], + "source": [ + "vna.rf_on()\n", + "data = qc.Measure(vna.channels[0].trace).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "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" + } + ], + "source": [ + "data = qc.Measure(vna.channels.S11.trace).run()\n", + "plot = qc.MatPlot(subplots=(1,2))\n", + "plot.add(data.VNA_S11_magnitude, subplot=1)\n", + "plot.add(data.VNA_S11_phase, subplot=2)\n", + "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also measure the magniture in dB." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-26/#012_{name}_10-41-10'\n", + " | | | \n", + " Measured | VNA_S11_tracedb | tracedb | (100,)\n", + "acquired at 2017-06-26 10:41:11\n" + ] + }, + { + "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" + } + ], + "source": [ + "plot = qc.MatPlot(subplots=(1,2))\n", + "plot.add(data1.VNA_S11_magnitude, subplot=1)\n", + "plot.add(data1.VNA_S11_phase, subplot=2)\n", + "plot.fig.tight_layout(rect=(0, 0, 1, 0.95))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also capture db Traces for all channels in one QCoDeS measurement. Notice how start/stop number of points and number of averages can be set globally for all channels. " + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jhn/src/Qcodes/qcodes/instrument/parameter.py:634: UserWarning: MultiParameters do not fully support set at this time.\n", + " warnings.warn('MultiParameters do not fully support set '\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-26/#014_{name}_10-45-28'\n", + " | | | \n", + " Setpoint | S44 frequency_set | S44 frequency | (100,)\n", + " Measured | VNA_S11_tracedb | VNA_S11_tracedb | (100,)\n", + " Measured | VNA_S12_tracedb | VNA_S12_tracedb | (100,)\n", + " Measured | VNA_S13_tracedb | VNA_S13_tracedb | (100,)\n", + " Measured | VNA_S14_tracedb | VNA_S14_tracedb | (100,)\n", + " Measured | VNA_S21_tracedb | VNA_S21_tracedb | (100,)\n", + " Measured | VNA_S22_tracedb | VNA_S22_tracedb | (100,)\n", + " Measured | VNA_S23_tracedb | VNA_S23_tracedb | (100,)\n", + " Measured | VNA_S24_tracedb | VNA_S24_tracedb | (100,)\n", + " Measured | VNA_S31_tracedb | VNA_S31_tracedb | (100,)\n", + " Measured | VNA_S32_tracedb | VNA_S32_tracedb | (100,)\n", + " Measured | VNA_S33_tracedb | VNA_S33_tracedb | (100,)\n", + " Measured | VNA_S34_tracedb | VNA_S34_tracedb | (100,)\n", + " Measured | VNA_S41_tracedb | VNA_S41_tracedb | (100,)\n", + " Measured | VNA_S42_tracedb | VNA_S42_tracedb | (100,)\n", + " Measured | VNA_S43_tracedb | VNA_S43_tracedb | (100,)\n", + " Measured | VNA_S44_tracedb | VNA_S44_tracedb | (100,)\n", + "acquired at 2017-06-26 10:46:08\n" + ] + } + ], + "source": [ + "vna.channels.start(9e3)\n", + "vna.channels.stop(8.5e9)\n", + "vna.channels.npts(100)\n", + "vna.channels.avg(100)\n", + "data = qc.Measure(vna.channels.tracedb).run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we plot the parameters on 2 different scales assuming that no DUT is connected the single port parameters will be much different from the other parametes" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "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" + } + ], + "source": [ + "plot = qc.MatPlot(subplots=1)\n", + "plot.add(data.arrays['VNA_S11_tracedb'])\n", + "plot.add(data.arrays['VNA_S12_tracedb'])\n", + "plot.fig.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "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.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 092cac571c9a8fbb2fd814dbe4c48ddbb33d16f0 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 26 Jun 2017 11:08:16 +0200 Subject: [PATCH 49/56] add benchmark --- ...codes example with Rohde Schwarz ZNB.ipynb | 177 +++++++++++++++++- 1 file changed, 172 insertions(+), 5 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb index 2658596b7826..06d2ca5c2b97 100644 --- a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -29,8 +29,10 @@ }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, + "execution_count": 3, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "import qcodes.instrument_drivers.rohde_schwarz.ZNB as vna" @@ -38,14 +40,14 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 1.04s\n" + "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 0.89s\n" ] } ], @@ -5308,6 +5310,171 @@ "plot.fig.tight_layout()" ] }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## Benchmark" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First lets compare capturing S11 traces with the old singe channel driver" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "RF output is off\n", + "RF output is off\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "VNA_S11:\n", + "\tparameter value\n", + "--------------------------------------------------------------------------------\n", + "avg :\t100 \n", + "bandwidth :\t10000 (Hz)\n", + "center :\t1.5e+06 \n", + "format :\tdB \n", + "npts :\t10 \n", + "power :\t-50 (dBm)\n", + "span :\t1e+06 \n", + "start :\t1e+06 \n", + "stop :\t2e+06 \n", + "trace :\t([0.27868703321350874, 0.33923896980936663, 0.221794149935147...\n", + "tracedb :\t[ -4.53745436 -5.61322947 -10.91646951 -4.15237283 -7.3118...\n", + "vna_parameter :\t'S11'\n", + " \n" + ] + } + ], + "source": [ + "vna.channels.S11.print_readable_snapshot(update=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Starting with a simple trace with few points and a small number of avagages" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jhn/src/Qcodes/qcodes/instrument/parameter.py:634: UserWarning: MultiParameters do not fully support set at this time.\n", + " warnings.warn('MultiParameters do not fully support set '\n" + ] + } + ], + "source": [ + "vna.rf_on()\n", + "vna.channels.npts(10)\n", + "vna.channels.avg(1)\n", + "vna.channels.start(9e3)\n", + "vna.channels.stop(100e3)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-26/#016_{name}_11-03-39'\n", + " | | | \n", + " Setpoint | frequency_set | frequency | (10,)\n", + " Measured | VNA_S11_magnitude | magnitude | (10,)\n", + " Measured | VNA_S11_phase | phase | (10,)\n", + "acquired at 2017-06-26 11:03:39\n", + "CPU times: user 11.9 ms, sys: 3.45 ms, total: 15.4 ms\n", + "Wall time: 54.8 ms\n" + ] + } + ], + "source": [ + "%%time\n", + "data = qc.Measure(vna.channels.S11.trace).run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now lets try with a longer freq axis and more averages" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jhn/src/Qcodes/qcodes/instrument/parameter.py:634: UserWarning: MultiParameters do not fully support set at this time.\n", + " warnings.warn('MultiParameters do not fully support set '\n" + ] + } + ], + "source": [ + "vna.channels.npts(1000)\n", + "vna.channels.avg(1000)\n", + "vna.channels.start(9e3)\n", + "vna.channels.stop(8.5e9)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-26/#017_{name}_11-04-52'\n", + " | | | \n", + " Setpoint | frequency_set | frequency | (1000,)\n", + " Measured | VNA_S11_magnitude | magnitude | (1000,)\n", + " Measured | VNA_S11_phase | phase | (1000,)\n", + "acquired at 2017-06-26 11:07:08\n", + "CPU times: user 205 ms, sys: 147 ms, total: 351 ms\n", + "Wall time: 2min 16s\n" + ] + } + ], + "source": [ + "%%time\n", + "data = qc.Measure(vna.channels.S11.trace).run()" + ] + }, { "cell_type": "code", "execution_count": null, From ee8380f32d3f85449bf7b8f01ba41aa8bc4147e6 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 26 Jun 2017 14:58:18 +0200 Subject: [PATCH 50/56] Replace namedtuple by dict Make it possible to get channels by name even when not locked --- qcodes/instrument/channel.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qcodes/instrument/channel.py b/qcodes/instrument/channel.py index 039c7568a152..9868effa5af0 100644 --- a/qcodes/instrument/channel.py +++ b/qcodes/instrument/channel.py @@ -1,6 +1,5 @@ """ Base class for the channel of an instrument """ from typing import List, Tuple, Union -from collections import namedtuple from .base import InstrumentBase, Instrument from .parameter import MultiParameter, ArrayParameter @@ -169,6 +168,7 @@ def __init__(self, parent: Instrument, self._snapshotable = snapshotable self._paramclass = multichan_paramclass + self._channel_mapping = {} # provide lookup of channels by name # If a list of channels is not provided, define a list to store # channels. This will eventually become a locked tuple. if chan_list is None: @@ -177,6 +177,8 @@ def __init__(self, parent: Instrument, else: self._locked = True self._channels = tuple(chan_list) + self._channel_mapping = {channel.short_name: channel + for channel in self._channels} if not all(isinstance(chan, chan_type) for chan in self._channels): raise TypeError("All items in this channel list must be of " "type {}.".format(chan_type.__name__)) @@ -250,6 +252,7 @@ def append(self, obj: InstrumentChannel): "type. Adding {} to a list of {}" ".".format(type(obj).__name__, self._chan_type.__name__)) + self._channel_mapping[obj.short_name] = obj return self._channels.append(obj) def extend(self, objects): @@ -306,9 +309,7 @@ def lock(self): if self._locked: return - channeltuple = namedtuple('channels', [channel.short_name for channel - in self._channels]) - self._channels = channeltuple(*self._channels) + self._channels = tuple(self._channels) self._locked = True def snapshot_base(self, update: bool=False): @@ -406,8 +407,8 @@ def multi_func(*args, **kwargs): return multi_func try: - return getattr(self._channels, name) - except AttributeError: + return self._channel_mapping[name] + except KeyError: pass raise AttributeError('\'{}\' object has no attribute \'{}\'' @@ -418,6 +419,5 @@ def __dir__(self) -> list: if self._channels: names += list(self._channels[0].parameters.keys()) names += list(self._channels[0].functions.keys()) - if self._locked: - names += [channel.short_name for channel in self._channels] + names += [channel.short_name for channel in self._channels] return sorted(set(names)) From c1b314baee6000d493b1e78c1a210ab124b999ca Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 26 Jun 2017 15:42:32 +0200 Subject: [PATCH 51/56] make it possible to init znb without channels --- .../instrument_drivers/rohde_schwarz/ZNB.py | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py index 81fea9a927cb..5e201de63a57 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py @@ -60,7 +60,7 @@ def get(self): # it is possible that the instrument and qcodes disagree about # which parameter is measured on this channel instrument_parameter = self._instrument.vna_parameter()[1:-2] - # trime quotes and newline + # trim quotes and newline if instrument_parameter != self._instrument._vna_parameter: raise RuntimeError("Invalid parameter. Tried to measure " "{} got {}".format(self._instrument._vna_parameter, @@ -321,15 +321,19 @@ class ZNB(VisaInstrument): Requires FrequencySweep parameter for taking a trace + Args: + name: instrument name + address: Address of instrument probably in format + 'TCPIP0::192.168.15.100::inst0::INSTR' + init_s_params: Automatically setup channels matching S parameters + **kwargs: passed to base class + TODO: - check initialisation settings and test functions """ - def __init__(self, name, address, **kwargs): + def __init__(self, name: str, address: str, init_s_params: bool=True, **kwargs): super().__init__(name=name, address=address, **kwargs) - n = 1 - self._sindex_to_channel = {} - self._channel_to_sindex = {} # TODO(JHN) I could not find a way to get max and min freq from # the API, if that is possible replace below with that @@ -353,17 +357,16 @@ def __init__(self, name, address, **kwargs): num_ports = self.num_ports() channels = ChannelList(self, "VNAChannels", ZNBChannel, snapshotable=True) - for i in range(1, num_ports+1): - self._sindex_to_channel[i] = {} - for j in range(1, num_ports+1): - ch_name = 'S' + str(i) + str(j) - channel = ZNBChannel(self, ch_name, n) - channels.append(channel) - self._sindex_to_channel[i][j] = n - self._channel_to_sindex[n] = (i, j) - n += 1 self.add_submodule("channels", channels) - self.channels.lock() + if init_s_params: + n = 1 + for i in range(1, num_ports+1): + for j in range(1, num_ports+1): + ch_name = 'S' + str(i) + str(j) + self.add_channel(ch_name) + n += 1 + + self.channels.lock() self.add_parameter(name='rf_power', get_cmd='OUTP1?', @@ -379,13 +382,30 @@ def __init__(self, name, address, **kwargs): self.add_function('update_display_on', call_cmd='SYST:DISP:UPD ON') self.add_function('update_display_off', call_cmd='SYST:DISP:UPD OFF') self.add_function('display_sij_split', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID {},{}'.format(num_ports, num_ports)) - self.add_function('display_sij_overlay', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID 1,1') + self.add_function('display_single_window', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID 1,1') self.add_function('rf_off', call_cmd='OUTP1 OFF') self.add_function('rf_on', call_cmd='OUTP1 ON') self.initialise() self.connect_message() - self.channels.autoscale() + if init_s_params: + self.display_sij_split() + self.channels.autoscale() + + def display_grid(self, rows: int, cols: int): + """ + Display a grid of channels rows by cols + """ + self.write('DISP:LAY GRID;:DISP:LAY:GRID {},{}'.format(rows, cols)) + + + def add_channel(self, vna_parameter: str): + n_channels = len(self.channels) + channel = ZNBChannel(self, vna_parameter, n_channels + 1) + self.channels.append(channel) + if n_channels == 0: + # ensure that the new channel is displayed + self.display_single_window() def _set_default_values(self): for channel in self.channels: @@ -395,7 +415,7 @@ def _set_default_values(self): channel.power(-50) def initialise(self): - for n in range(1, 5): + for n in range(1, len(self.channels)): self.write('SENS{}:SWE:TYPE LIN'.format(n)) self.write('SENS{}:SWE:TIME:AUTO ON'.format(n)) self.write('TRIG{}:SEQ:SOUR IMM'.format(n)) @@ -403,4 +423,3 @@ def initialise(self): self.update_display_on() self._set_default_values() self.rf_off() - self.display_sij_split() From d108edf58394ae7704375065075506ee776e45b4 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 26 Jun 2017 15:56:31 +0200 Subject: [PATCH 52/56] Fix change init order --- .../instrument_drivers/rohde_schwarz/ZNB.py | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py index 5e201de63a57..597ffd1c988b 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py @@ -355,18 +355,6 @@ def __init__(self, name: str, address: str, init_s_params: bool=True, **kwargs): get_cmd='INST:PORT:COUN?', get_parser=int) num_ports = self.num_ports() - channels = ChannelList(self, "VNAChannels", ZNBChannel, - snapshotable=True) - self.add_submodule("channels", channels) - if init_s_params: - n = 1 - for i in range(1, num_ports+1): - for j in range(1, num_ports+1): - ch_name = 'S' + str(i) + str(j) - self.add_channel(ch_name) - n += 1 - - self.channels.lock() self.add_parameter(name='rf_power', get_cmd='OUTP1?', @@ -386,8 +374,21 @@ def __init__(self, name: str, address: str, init_s_params: bool=True, **kwargs): self.add_function('rf_off', call_cmd='OUTP1 OFF') self.add_function('rf_on', call_cmd='OUTP1 ON') + channels = ChannelList(self, "VNAChannels", ZNBChannel, + snapshotable=True) + self.add_submodule("channels", channels) + if init_s_params: + n = 1 + for i in range(1, num_ports+1): + for j in range(1, num_ports+1): + ch_name = 'S' + str(i) + str(j) + self.add_channel(ch_name) + n += 1 + self.channels.lock() + self.initialise() self.connect_message() + if init_s_params: self.display_sij_split() self.channels.autoscale() @@ -403,9 +404,7 @@ def add_channel(self, vna_parameter: str): n_channels = len(self.channels) channel = ZNBChannel(self, vna_parameter, n_channels + 1) self.channels.append(channel) - if n_channels == 0: - # ensure that the new channel is displayed - self.display_single_window() + def _set_default_values(self): for channel in self.channels: From e4a87525b4c3e56dde1fec1e178eb1a35f44ec02 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 26 Jun 2017 17:20:57 +0200 Subject: [PATCH 53/56] add option to clear channels and traces --- qcodes/instrument_drivers/rohde_schwarz/ZNB.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py index 597ffd1c988b..42b62ab9d9b4 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py @@ -373,7 +373,7 @@ def __init__(self, name: str, address: str, init_s_params: bool=True, **kwargs): self.add_function('display_single_window', call_cmd='DISP:LAY GRID;:DISP:LAY:GRID 1,1') self.add_function('rf_off', call_cmd='OUTP1 OFF') self.add_function('rf_on', call_cmd='OUTP1 ON') - + self.clear_channels() channels = ChannelList(self, "VNAChannels", ZNBChannel, snapshotable=True) self.add_submodule("channels", channels) @@ -404,6 +404,8 @@ def add_channel(self, vna_parameter: str): n_channels = len(self.channels) channel = ZNBChannel(self, vna_parameter, n_channels + 1) self.channels.append(channel) + if n_channels == 0: + self.display_single_window() def _set_default_values(self): @@ -422,3 +424,15 @@ def initialise(self): self.update_display_on() self._set_default_values() self.rf_off() + + def clear_channels(self): + """ + Remove all channels from the instrument and channel list and + unlock the channel list. + """ + self.write('CALCulate:PARameter:DELete:ALL') + for submodule in self.submodules.values(): + if isinstance(submodule, ChannelList): + submodule._channels = [] + submodule._channel_mapping = {} + submodule._locked = False From c2f68016f5e32a94bc67cbd5c1d60765c62859c5 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Mon, 26 Jun 2017 17:24:28 +0200 Subject: [PATCH 54/56] update example notebook --- ...codes example with Rohde Schwarz ZNB.ipynb | 436 +++++++++++++++--- 1 file changed, 371 insertions(+), 65 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb index 06d2ca5c2b97..6768796c66e2 100644 --- a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -29,30 +29,30 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ - "import qcodes.instrument_drivers.rohde_schwarz.ZNB as vna" + "import qcodes.instrument_drivers.rohde_schwarz.ZNB as ZNB" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 0.89s\n" + "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 0.41s\n" ] } ], "source": [ - "vna = vna.ZNB('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR', server_name=None)" + "vna = ZNB.ZNB('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR')" ] }, { @@ -64,7 +64,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -90,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 5, "metadata": { "collapsed": true }, @@ -101,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 6, "metadata": { "collapsed": true }, @@ -119,7 +119,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 7, "metadata": { "collapsed": true }, @@ -139,7 +139,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 8, "metadata": { "collapsed": true }, @@ -157,7 +157,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -165,12 +165,12 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-06-26/#010_{name}_10-40-16'\n", + " location = 'data/2017-06-26/#095_{name}_17-15-13'\n", " | | | \n", " Setpoint | frequency_set | frequency | (100,)\n", " Measured | VNA_S11_magnitude | magnitude | (100,)\n", " Measured | VNA_S11_phase | phase | (100,)\n", - "acquired at 2017-06-26 10:40:17\n" + "acquired at 2017-06-26 17:15:14\n" ] } ], @@ -181,7 +181,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -964,7 +964,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -990,7 +990,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 11, "metadata": { "collapsed": true }, @@ -1003,7 +1003,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 12, "metadata": { "scrolled": false }, @@ -1013,12 +1013,12 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-06-26/#011_{name}_10-41-01'\n", + " location = 'data/2017-06-26/#096_{name}_17-15-19'\n", " | | | \n", " Setpoint | frequency_set | frequency | (100,)\n", " Measured | VNA_S11_magnitude | magnitude | (100,)\n", " Measured | VNA_S11_phase | phase | (100,)\n", - "acquired at 2017-06-26 10:41:02\n" + "acquired at 2017-06-26 17:15:20\n" ] }, { @@ -1801,7 +1801,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1828,7 +1828,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -1836,10 +1836,10 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-06-26/#012_{name}_10-41-10'\n", + " location = 'data/2017-06-26/#097_{name}_17-15-23'\n", " | | | \n", " Measured | VNA_S11_tracedb | tracedb | (100,)\n", - "acquired at 2017-06-26 10:41:11\n" + "acquired at 2017-06-26 17:15:24\n" ] }, { @@ -2622,7 +2622,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -2647,7 +2647,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 14, "metadata": { "collapsed": true }, @@ -2665,13 +2665,22 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "vna.display_single_window()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, "metadata": { "collapsed": true }, "outputs": [], "source": [ - "vna.display_sij_overlay()" + "vna.display_sij_split()" ] }, { @@ -2683,7 +2692,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 17, "metadata": { "collapsed": true }, @@ -2694,7 +2703,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 18, "metadata": { "collapsed": true }, @@ -2714,7 +2723,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 19, "metadata": { "collapsed": true }, @@ -2725,7 +2734,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 20, "metadata": { "collapsed": true }, @@ -2743,7 +2752,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 21, "metadata": { "collapsed": true }, @@ -2754,7 +2763,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 22, "metadata": { "collapsed": true }, @@ -2772,22 +2781,22 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Started at 2017-06-26 10:44:20\n", + "Started at 2017-06-26 17:15:37\n", "DataSet:\n", - " location = 'data/2017-06-26/#013_{name}_10-44-20'\n", + " location = 'data/2017-06-26/#098_{name}_17-15-37'\n", " | | | \n", " Setpoint | VNA_S11_power_set | power | (21,)\n", " Setpoint | frequency_set | frequency | (21, 100)\n", " Measured | VNA_S11_magnitude | magnitude | (21, 100)\n", " Measured | VNA_S11_phase | phase | (21, 100)\n", - "Finished at 2017-06-26 10:44:47\n" + "Finished at 2017-06-26 17:16:05\n" ] } ], @@ -2798,7 +2807,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -3581,7 +3590,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -3607,7 +3616,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -3623,7 +3632,7 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-06-26/#014_{name}_10-45-28'\n", + " location = 'data/2017-06-26/#099_{name}_17-16-08'\n", " | | | \n", " Setpoint | S44 frequency_set | S44 frequency | (100,)\n", " Measured | VNA_S11_tracedb | VNA_S11_tracedb | (100,)\n", @@ -3642,7 +3651,7 @@ " Measured | VNA_S42_tracedb | VNA_S42_tracedb | (100,)\n", " Measured | VNA_S43_tracedb | VNA_S43_tracedb | (100,)\n", " Measured | VNA_S44_tracedb | VNA_S44_tracedb | (100,)\n", - "acquired at 2017-06-26 10:46:08\n" + "acquired at 2017-06-26 17:16:48\n" ] } ], @@ -3663,7 +3672,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -4446,7 +4455,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -4479,7 +4488,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -4495,12 +4504,12 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-06-26/#015_{name}_10-49-42'\n", + " location = 'data/2017-06-26/#100_{name}_17-17-11'\n", " | | | \n", " Setpoint | S12 frequency_set | S12 frequency | (100,)\n", " Measured | VNA_S11_tracedb | VNA_S11_tracedb | (100,)\n", " Measured | VNA_S12_tracedb | VNA_S12_tracedb | (100,)\n", - "acquired at 2017-06-26 10:49:47\n" + "acquired at 2017-06-26 17:17:15\n" ] } ], @@ -4510,7 +4519,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -5293,7 +5302,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -5310,6 +5319,110 @@ "plot.fig.tight_layout()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Without predefined channels\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is also possible to construct a VNA without predefined channels. You will likely want to remove any exising\n", + "channels manually or by a reset before doing this. The driver does not automatically call reset as this turns on rf at a power of -10 dBm" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 0.02s\n" + ] + } + ], + "source": [ + "vna.close()\n", + "vna = ZNB.ZNB('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR', init_s_params=False)\n", + "station = qc.Station(vna)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then we can add a channel, messuring with only one channel may speed up data acquisition." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.add_channel('S22')" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.cont_meas_on()\n", + "vna.display_single_window()" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-26/#101_{name}_17-17-26'\n", + " | | | \n", + " Measured | VNA_S22_tracedb | tracedb | (100,)\n", + "acquired at 2017-06-26 17:17:28\n" + ] + } + ], + "source": [ + "vna.rf_on()\n", + "data = qc.Measure(vna.channels.S22.tracedb).run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also clear the channels in an existing instrument" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.clear_channels()" + ] + }, { "cell_type": "markdown", "metadata": { @@ -5328,7 +5441,70 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "RF output is off\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 0.47s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n", + "RF output is off\n" + ] + } + ], + "source": [ + "vna.close()\n", + "vna = ZNB.ZNB('VNA', 'TCPIP0::192.168.15.100::inst0::INSTR', init_s_params=True)\n", + "station = qc.Station(vna)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -5346,7 +5522,7 @@ "VNA_S11:\n", "\tparameter value\n", "--------------------------------------------------------------------------------\n", - "avg :\t100 \n", + "avg :\t1 \n", "bandwidth :\t10000 (Hz)\n", "center :\t1.5e+06 \n", "format :\tdB \n", @@ -5355,8 +5531,8 @@ "span :\t1e+06 \n", "start :\t1e+06 \n", "stop :\t2e+06 \n", - "trace :\t([0.27868703321350874, 0.33923896980936663, 0.221794149935147...\n", - "tracedb :\t[ -4.53745436 -5.61322947 -10.91646951 -4.15237283 -7.3118...\n", + "trace :\t([0.39977125983817535, 0.1934386076395905, 2.3953314824736913...\n", + "tracedb :\t[ 4.65693625 -2.02685622 6.20248217 4.07671299 4.1240...\n", "vna_parameter :\t'S11'\n", " \n" ] @@ -5375,7 +5551,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -5397,7 +5573,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -5405,14 +5581,14 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-06-26/#016_{name}_11-03-39'\n", + " location = 'data/2017-06-26/#102_{name}_17-18-01'\n", " | | | \n", " Setpoint | frequency_set | frequency | (10,)\n", " Measured | VNA_S11_magnitude | magnitude | (10,)\n", " Measured | VNA_S11_phase | phase | (10,)\n", - "acquired at 2017-06-26 11:03:39\n", - "CPU times: user 11.9 ms, sys: 3.45 ms, total: 15.4 ms\n", - "Wall time: 54.8 ms\n" + "acquired at 2017-06-26 17:18:01\n", + "CPU times: user 50.5 ms, sys: 5.61 ms, total: 56.1 ms\n", + "Wall time: 96.4 ms\n" ] } ], @@ -5430,7 +5606,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -5451,7 +5627,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 43, "metadata": {}, "outputs": [ { @@ -5459,13 +5635,13 @@ "output_type": "stream", "text": [ "DataSet:\n", - " location = 'data/2017-06-26/#017_{name}_11-04-52'\n", + " location = 'data/2017-06-26/#103_{name}_17-18-04'\n", " | | | \n", " Setpoint | frequency_set | frequency | (1000,)\n", " Measured | VNA_S11_magnitude | magnitude | (1000,)\n", " Measured | VNA_S11_phase | phase | (1000,)\n", - "acquired at 2017-06-26 11:07:08\n", - "CPU times: user 205 ms, sys: 147 ms, total: 351 ms\n", + "acquired at 2017-06-26 17:20:20\n", + "CPU times: user 244 ms, sys: 142 ms, total: 386 ms\n", "Wall time: 2min 16s\n" ] } @@ -5475,6 +5651,136 @@ "data = qc.Measure(vna.channels.S11.trace).run()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lets now try with only one channel added" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "vna.clear_channels()\n", + "vna.add_channel('S11')" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jhn/src/Qcodes/qcodes/instrument/parameter.py:634: UserWarning: MultiParameters do not fully support set at this time.\n", + " warnings.warn('MultiParameters do not fully support set '\n" + ] + } + ], + "source": [ + "vna.rf_on()\n", + "vna.channels.npts(10)\n", + "vna.channels.avg(1)\n", + "vna.channels.start(9e3)\n", + "vna.channels.stop(100e3)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-26/#104_{name}_17-20-21'\n", + " | | | \n", + " Setpoint | frequency_set | frequency | (10,)\n", + " Measured | VNA_S11_magnitude | magnitude | (10,)\n", + " Measured | VNA_S11_phase | phase | (10,)\n", + "acquired at 2017-06-26 17:20:21\n", + "CPU times: user 17.3 ms, sys: 4.14 ms, total: 21.4 ms\n", + "Wall time: 31.6 ms\n" + ] + } + ], + "source": [ + "%%time\n", + "data = qc.Measure(vna.channels.S11.trace).run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As can be seen this is significantly faster than with all the channels added" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jhn/src/Qcodes/qcodes/instrument/parameter.py:634: UserWarning: MultiParameters do not fully support set at this time.\n", + " warnings.warn('MultiParameters do not fully support set '\n" + ] + } + ], + "source": [ + "vna.channels.npts(1000)\n", + "vna.channels.avg(1000)\n", + "vna.channels.start(9e3)\n", + "vna.channels.stop(8.5e9)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But for a long trace the difference is much smaller" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-26/#105_{name}_17-20-21'\n", + " | | | \n", + " Setpoint | frequency_set | frequency | (1000,)\n", + " Measured | VNA_S11_magnitude | magnitude | (1000,)\n", + " Measured | VNA_S11_phase | phase | (1000,)\n", + "acquired at 2017-06-26 17:22:36\n", + "CPU times: user 206 ms, sys: 140 ms, total: 347 ms\n", + "Wall time: 2min 15s\n" + ] + } + ], + "source": [ + "%%time\n", + "data = qc.Measure(vna.channels.S11.trace).run()" + ] + }, { "cell_type": "code", "execution_count": null, From 870f9d4896c76d6f58e13b93aa33723a49eec305 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 27 Jun 2017 11:03:13 +0200 Subject: [PATCH 55/56] Add support for changing format and rename trace paramters --- .../instrument_drivers/rohde_schwarz/ZNB.py | 69 ++++++++++++++----- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py index 42b62ab9d9b4..15af4d8881b8 100644 --- a/qcodes/instrument_drivers/rohde_schwarz/ZNB.py +++ b/qcodes/instrument_drivers/rohde_schwarz/ZNB.py @@ -59,8 +59,7 @@ def get(self): log.warning("RF output is off") # it is possible that the instrument and qcodes disagree about # which parameter is measured on this channel - instrument_parameter = self._instrument.vna_parameter()[1:-2] - # trim quotes and newline + instrument_parameter = self._instrument.vna_parameter() if instrument_parameter != self._instrument._vna_parameter: raise RuntimeError("Invalid parameter. Tried to measure " "{} got {}".format(self._instrument._vna_parameter, @@ -130,14 +129,11 @@ def get(self): log.warning("RF output is off") # it is possible that the instrument and qcodes disagree about # which parameter is measured on this channel - instrument_parameter = self._instrument.vna_parameter()[1:-2] - # trime quotes and newline + instrument_parameter = self._instrument.vna_parameter() if instrument_parameter != self._instrument._vna_parameter: raise RuntimeError("Invalid parameter. Tried to measure " "{} got {}".format(self._instrument._vna_parameter, instrument_parameter)) - old_format = self._instrument.format() - self._instrument.format('dB') self._instrument.write('SENS{}:AVER:STAT ON'.format(self._channel)) self._instrument.write('SENS{}:AVER:CLE'.format(self._channel)) self._instrument._parent.cont_meas_off() @@ -148,9 +144,12 @@ def get(self): self._instrument.write('INIT{}:IMM; *WAI'.format(self._channel)) data_str = self._instrument.ask('CALC{}:DATA? FDAT'.format(self._channel)) data = np.array(data_str.rstrip().split(',')).astype('float64') - + if self._instrument.format() in ['Polar', 'Complex', + 'Smith', 'Inverse Smith']: + log.warning("QCoDeS Dataset does not currently support Complex " + "values. Will discard the imaginary part.") + data = data[0::2] + 1j*data[1::2] self._instrument._parent.cont_meas_on() - self._instrument.format(old_format) self._save_val(data) return data @@ -174,8 +173,8 @@ def __init__(self, parent, name, channel): self.add_parameter(name='vna_parameter', label='VNA parameter', get_cmd="CALC{}:PAR:MEAS? '{}'".format(self._instrument_channel, - self._tracename)) - + self._tracename), + get_parser=self._strip) self.add_parameter(name='power', label='Power', unit='dBm', @@ -223,7 +222,7 @@ def __init__(self, parent, name, channel): get_parser=int) self.add_parameter(name='format', get_cmd='CALC{}:FORM?'.format(n), - set_cmd='CALC{}:FORM {{}}'.format(n), + set_cmd=self._set_format, val_mapping={'dB': 'MLOG\n', 'Linear Magnitude': 'MLIN\n', 'Phase': 'PHAS\n', @@ -238,13 +237,13 @@ def __init__(self, parent, name, channel): 'Complex': "COMP\n" }) - self.add_parameter(name='trace', + self.add_parameter(name='full_trace', start=self.start(), stop=self.stop(), npts=self.npts(), channel=n, parameter_class=FrequencySweepMagPhase) - self.add_parameter(name='tracedb', + self.add_parameter(name='trace', start=self.start(), stop=self.stop(), npts=self.npts(), @@ -254,6 +253,40 @@ def __init__(self, parent, name, channel): self.add_function('autoscale', call_cmd='DISPlay:TRACe1:Y:SCALe:AUTO ONCE, "{}"'.format(self._tracename)) + def _set_format(self, val): + unit_mapping = {'MLOG\n': 'dB', + 'MLIN\n': '', + 'PHAS\n': 'rad', + 'UPH\n': 'rad', + 'POL\n': '', + 'SMIT\n': '', + 'ISM\n': '', + 'SWR\n': 'U', + 'REAL\n': 'U', + 'IMAG\n': 'U', + 'GDEL\n': 'S', + 'COMP\n': ''} + label_mapping = {'MLOG\n': 'Magnitude', + 'MLIN\n': 'Magnitude', + 'PHAS\n': 'Phase', + 'UPH\n': 'Unwrapped phase', + 'POL\n': 'Complex Magnitude', + 'SMIT\n': 'Complex Magnitude', + 'ISM\n': 'Complex Magnitude', + 'SWR\n': 'Standing Wave Ratio', + 'REAL\n': 'Real Magnitude', + 'IMAG\n': 'Imaginary Magnitude', + 'GDEL\n': 'Delay', + 'COMP\n': 'Complex Magnitude'} + channel = self._instrument_channel + self.write('CALC{}:FORM {}'.format(channel, val)) + self.trace.unit = unit_mapping[val] + self.trace.label = "{} {}".format(self.vna_parameter(), label_mapping[val]) + + def _strip(self, var): + "Strip newline and quotes from instrument reply" + return var.rstrip()[1:-1] + def _set_start(self, val): channel = self._instrument_channel self.write('SENS{}:FREQ:START {:.4f}'.format(channel, val)) @@ -268,7 +301,7 @@ def _set_start(self, val): log.warning("Could not set start to {} setting it to {}".format(val, start)) # update setpoints for FrequencySweep param self.trace.set_sweep(start, stop, npts) - self.tracedb.set_sweep(start, stop, npts) + self.full_trace.set_sweep(start, stop, npts) def _set_stop(self, val): channel = self._instrument_channel @@ -283,7 +316,7 @@ def _set_stop(self, val): log.warning("Could not set stop to {} setting it to {}".format(val, stop)) # update setpoints for FrequencySweep param self.trace.set_sweep(start, stop, npts) - self.tracedb.set_sweep(start, stop, npts) + self.full_trace.set_sweep(start, stop, npts) def _set_npts(self, val): channel = self._instrument_channel @@ -292,7 +325,7 @@ def _set_npts(self, val): stop = self.stop() # update setpoints for FrequencySweep param self.trace.set_sweep(start, stop, val) - self.tracedb.set_sweep(start, stop, val) + self.full_trace.set_sweep(start, stop, val) def _set_span(self, val): channel = self._instrument_channel @@ -301,7 +334,7 @@ def _set_span(self, val): stop = self.stop() npts = self.npts() self.trace.set_sweep(start, stop, npts) - self.tracedb.set_sweep(start, stop, npts) + self.full_trace.set_sweep(start, stop, npts) def _set_center(self, val): channel = self._instrument_channel @@ -310,7 +343,7 @@ def _set_center(self, val): stop = self.stop() npts = self.npts() self.trace.set_sweep(start, stop, npts) - self.tracedb.set_sweep(start, stop, npts) + self.full_trace.set_sweep(start, stop, npts) class ZNB(VisaInstrument): From f7cc9bc8e278e2c5fa5a10470b06cb03cde40e19 Mon Sep 17 00:00:00 2001 From: Jens Hedegaard Nielsen Date: Tue, 27 Jun 2017 11:03:33 +0200 Subject: [PATCH 56/56] update example notebook --- ...codes example with Rohde Schwarz ZNB.ipynb | 2924 +++++++++++++++-- 1 file changed, 2685 insertions(+), 239 deletions(-) diff --git a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb index 6768796c66e2..5eb4ec63d7d3 100644 --- a/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb +++ b/docs/examples/driver_examples/Qcodes example with Rohde Schwarz ZNB.ipynb @@ -47,7 +47,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 0.41s\n" + "Connected to: Rohde-Schwarz ZNB8-4Port (serial:1311601044102194, firmware:2.84) in 0.56s\n" ] } ], @@ -81,6 +81,13 @@ "vna.rf_on()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The above warning about Multiparameter can be ignored for the most part. It only means that you cannot loop over the multiparameters such as vna.channel.avg" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -145,19 +152,2504 @@ }, "outputs": [], "source": [ - "vna.channels.S11.power(-30)" + "vna.channels.S11.power(-30)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can meassure a frequency trace, first remembering to turn on the rf source. By default this produces a dB magnitude scan." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-27/#073_{name}_10-40-43'\n", + " | | | \n", + " Measured | VNA_S11_trace | trace | (10,)\n", + "acquired at 2017-06-27 10:40:45\n" + ] + } + ], + "source": [ + "vna.rf_on()\n", + "data = qc.Measure(vna.channels.S11.trace).run()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "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" + } + ], + "source": [ + "data = qc.Measure(vna.channels.S11.trace).run()\n", + "plot = qc.MatPlot()\n", + "plot.add(data.VNA_S11_trace)\n", + "plot.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also measure the linear magniture." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataSet:\n", + " location = 'data/2017-06-27/#077_{name}_10-42-47'\n", + " | | | \n", + " Measured | VNA_S11_trace | trace | (100,)\n", + "acquired at 2017-06-27 10:42:59\n" + ] + }, + { + "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 = $('