Source code for qblox_instruments.qcodes_drivers.qcm_qrm

# ----------------------------------------------------------------------------
# Description    : QCM/QRM QCoDeS interface
# Git repository : https://gitlab.com/qblox/packages/software/qblox_instruments.git
# Copyright (C) Qblox BV (2020)
# ----------------------------------------------------------------------------


# -- include -----------------------------------------------------------------

from typing import Any, Callable, List, Union, Iterable
from functools import partial
from qcodes import validators as vals
from qcodes import Instrument, InstrumentChannel, Parameter
from qblox_instruments import InstrumentClass, InstrumentType
from qblox_instruments.qcodes_drivers.sequencer import Sequencer
from qblox_instruments.ieee488_2 import DummyBinnedAcquisitionData, DummyScopeAcquisitionData


# -- class -------------------------------------------------------------------

[docs]class QcmQrm(InstrumentChannel): """ This class represents a QCM/QRM module. It combines all module specific parameters and functions into a single QCoDes InstrumentChannel. """ # ------------------------------------------------------------------------
[docs] def __init__( self, parent: Instrument, name: str, slot_idx: int, ): """ Creates a QCM/QRM module class and adds all relevant parameters for the module. Parameters ---------- parent : Instrument The QCoDeS class to which this module belongs. name : str Name of this module channel slot_idx : int The index of this module in the parent instrument, representing which module is controlled by this class. Returns ---------- Raises ---------- """ # Initialize instrument channel super().__init__(parent, name) # Store sequencer index self._slot_idx = slot_idx # Add required parent attributes for the QCoDeS parameters to function for attr_name in QcmQrm._get_required_parent_attr_names(): self._register(attr_name) # Add module QCoDeS parameters self.add_parameter( "present", label="Module present status", docstring="Sets/gets module present status for slot {} in the " "Cluster.", unit="", vals=vals.Bool(), get_parser=bool, get_cmd=self._get_modules_present, ) # Add QCM/QRM QCoDeS parameters try: self.parent._present_at_init(self.slot_idx) add_qcodes_params(self, num_seq=6) except KeyError: pass
# ------------------------------------------------------------------------ @property def slot_idx(self) -> int: """ Get slot index. Parameters ---------- Returns ---------- int Slot index Raises ---------- """ return self._slot_idx # ------------------------------------------------------------------------ @property def module_type(self) -> InstrumentType: """ Get module type (e.g. QRM, QCM). Parameters ---------- Returns ---------- InstrumentType Module type Raises ---------- KeyError Module is not available. """ return self.parent._module_type(self.slot_idx) # ------------------------------------------------------------------------ @property def is_qcm_type(self) -> bool: """ Return if module is of type QCM. Parameters ---------- Returns ---------- bool True if module is of type QCM. Raises ---------- KeyError Module is not available. """ return self.parent._is_qcm_type(self.slot_idx) # ------------------------------------------------------------------------ @property def is_qrm_type(self) -> bool: """ Return if module is of type QRM. Parameters ---------- Returns ---------- bool: True if module is of type QRM. Raises ---------- KeyError Module is not available. """ return self.parent._is_qrm_type(self.slot_idx) # ------------------------------------------------------------------------ @property def is_rf_type(self) -> bool: """ Return if module is of type QCM-RF or QRM-RF. Parameters ---------- Returns ---------- bool: True if module is of type QCM-RF or QRM-RF. Raises ---------- KeyError Module is not available. """ return self.parent._is_rf_type(self.slot_idx) # ------------------------------------------------------------------------ @property def sequencers(self) -> List: """ Get list of sequencers submodules. Parameters ---------- Returns ---------- list List of sequencer submodules. Raises ---------- """ return list(self.submodules.values()) # ------------------------------------------------------------------------ @staticmethod def _get_required_parent_attr_names() -> List: """ Return list of parent attributes names that are required for the QCoDeS parameters to function, so that the can be registered to this object using the _register method. Parameters ---------- Returns ---------- List List of parent attribute names to register. Raises ---------- """ # Constants NUM_LO = 2 # Maximum number of LOs NUM_IN = 2 # Maximum number of inputs NUM_OUT = 4 # Maximum number of outputs # Module present attribute attr_names = [] attr_names.append("_get_modules_present") # LO attributes for operation in ["set", "get"]: for idx in range(0, NUM_LO): attr_names.append("_{}_lo_freq_{}".format(operation, idx)) attr_names.append("_{}_lo_pwr_{}".format(operation, idx)) attr_names.append("_{}_lo_enable_{}".format(operation, idx)) # Input attributes for operation in ["set", "get"]: for idx in range(0, NUM_IN): attr_names.append("_{}_in_amp_gain_{}".format(operation, idx)) for idx in range(0, round(NUM_IN/2)): attr_names.append("_{}_in_att_{}".format(operation, idx)) # Output attributes for operation in ["set", "get"]: for idx in range(0, NUM_OUT): attr_names.append("_{}_out_amp_offset_{}".format(operation, idx)) attr_names.append("_{}_dac_offset_{}".format(operation, idx)) for idx in range(0, round(NUM_OUT/2)): attr_names.append("_{}_out_att_{}".format(operation, idx)) # Scope acquisition attributes for operation in ["set", "get"]: attr_names.append("_{}_acq_scope_config".format(operation)) attr_names.append("_{}_acq_scope_config_val".format(operation)) # Sequencer program attributes attr_names.append("get_assembler_status") attr_names.append("get_assembler_log") # Sequencer attributes attr_names += Sequencer._get_required_parent_attr_names() return attr_names # ------------------------------------------------------------------------ def _register(self, attr_name: str) -> None: """ Register parent attribute to this sequencer using functools.partial to pre-select the slot index. If the attribute does not exist in the parent class, a method that raises a `NotImplementedError` exception is registered instead. The docstring of the parent attribute is also copied to the registered attribute. Parameters ---------- attr_name : str Attribute name of parent to register. Returns ---------- Raises ---------- """ if hasattr(self.parent, attr_name): parent_attr = getattr(self.parent, attr_name) partial_func = partial(parent_attr, self.slot_idx) partial_func.__doc__ = ( "Important:\n" + "This method calls {0} using functools.partial to set the " + "slot index. The following docstring is of {1}.{0}:\n\n" ).format(attr_name, type(self.parent).__name__) partial_func.__doc__ += parent_attr.__doc__ setattr(self, attr_name, partial_func) else: def raise_not_implemented_error(*args, **kwargs) -> None: raise NotImplementedError( '{} does not have "{}" attribute.'.format(self.parent.name, attr_name) ) setattr(self, attr_name, raise_not_implemented_error) # ------------------------------------------------------------------------ def _invalidate_qcodes_parameter_cache(self) -> None: """ Marks the cache of all QCoDeS parameters in the module, including in any sequencers the module might have, as invalid. Parameters ---------- Returns ---------- Raises ---------- """ invalidate_qcodes_parameter_cache(self) # ------------------------------------------------------------------------ def __getitem__( self, key: str ) -> Union[InstrumentChannel, Parameter, Callable[..., Any]]: """ Get sequencer or parameter using string based lookup. Parameters ---------- key : str Sequencer, parameter or function to retrieve. Returns ---------- Union[InstrumentChannel, Parameter, Callable[..., Any]] Sequencer, parameter or function. Raises ---------- KeyError Sequencer, parameter or function does not exist. """ return get_item(self, key)
# -- functions ---------------------------------------------------------------
[docs]def add_qcodes_params( parent: Union[Instrument, QcmQrm], num_seq: int ) -> None: """ Add all QCoDeS parameters for a single QCM/QRM module. Parameters ---------- parent : Union[Instrument, QcmQrm] Parent object to which the parameters need to be added. num_seq : int Number of sequencers to add as submodules. Returns ---------- Raises ---------- """ # -- Reference source (Pulsars only) ------------------------------------- if parent.root_instrument.instrument_class == InstrumentClass.PULSAR: parent.add_parameter( "reference_source", label="Reference source", docstring="Sets/gets reference source ('internal' = internal " "10 MHz, 'external' = external 10 MHz).", unit="", vals=vals.Bool(), val_mapping={"internal": True, "external": False}, set_parser=bool, get_parser=bool, set_cmd=parent._set_reference_source, get_cmd=parent._get_reference_source, ) # -- LO frequencies (RF-modules only) ------------------------------------ if parent.is_rf_type: if parent.is_qrm_type: parent.add_parameter( "out0_in0_lo_freq", label="Local oscillator frequency", docstring="Sets/gets the local oscillator frequency for " "output 0 and input 0.", unit="Hz", vals=vals.Numbers(2e9, 18e9), set_parser=int, get_parser=int, set_cmd=parent._set_lo_freq_1, get_cmd=parent._get_lo_freq_1, ) else: parent.add_parameter( "out0_lo_freq", label="Output 0 local oscillator frequency", docstring="Sets/gets the local oscillator frequency for " "output 0.", unit="Hz", vals=vals.Numbers(2e9, 18e9), set_parser=int, get_parser=int, set_cmd=parent._set_lo_freq_0, get_cmd=parent._get_lo_freq_0, ) parent.add_parameter( "out1_lo_freq", label="Output 1 local oscillator frequency", docstring="Sets/gets the local oscillator frequency for " "output 1.", unit="Hz", vals=vals.Numbers(2e9, 18e9), set_parser=int, get_parser=int, set_cmd=parent._set_lo_freq_1, get_cmd=parent._get_lo_freq_1, ) # -- LO enables (RF-modules only) ---------------------------------------- if parent.is_rf_type: if parent.is_qrm_type: parent.add_parameter( "out0_in0_lo_en", label="Local oscillator enable", docstring="Sets/gets the local oscillator enable for " "output 0 and input 0.", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=parent._set_lo_enable_1, get_cmd=parent._get_lo_enable_1, ) else: parent.add_parameter( "out0_lo_en", label="Output 0 local oscillator enable", docstring="Sets/gets the local oscillator enable for " "output 0.", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=parent._set_lo_enable_0, get_cmd=parent._get_lo_enable_0, ) parent.add_parameter( "out1_lo_en", label="Output 1 local oscillator enable", docstring="Sets/gets the local oscillator enable for " "output 1.", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=parent._set_lo_enable_1, get_cmd=parent._get_lo_enable_1, ) # -- Attenuation settings (RF-modules only) ------------------------------ if parent.is_rf_type: if parent.is_qrm_type: parent.add_parameter( "in0_att", label="Input 0 attenuation", docstring="Sets/gets input attenuation in a range of 0dB to 30dB with a resolution of 2dB per step.", unit="dB", vals=vals.Multiples(2, min_value=0, max_value=30), set_parser=int, get_parser=int, set_cmd=parent._set_in_att_0, get_cmd=parent._get_in_att_0, ) parent.add_parameter( "out0_att", label="Output 0 attenuation", docstring="Sets/gets output attenuation in a range of 0dB to 60dB with a resolution of 2dB per step.", unit="dB", vals=vals.Multiples(2, min_value=0, max_value=60), set_parser=int, get_parser=int, set_cmd=parent._set_out_att_0, get_cmd=parent._get_out_att_0, ) if parent.is_qcm_type: parent.add_parameter( "out1_att", label="Output 1 attenuation", docstring="Sets/gets output attenuation in a range of 0dB to 60dB with a resolution of 2dB per step.", unit="dB", vals=vals.Multiples(2, min_value=0, max_value=60), set_parser=int, get_parser=int, set_cmd=parent._set_out_att_1, get_cmd=parent._get_out_att_1, ) # -- Input gain (QRM baseband modules only) ------------------------------ if not parent.is_rf_type and parent.is_qrm_type: parent.add_parameter( "in0_gain", label="Input 0 gain", docstring="Sets/gets input 0 gain in a range of -6dB to 26dB " "with a resolution of 1dB per step.", unit="dB", vals=vals.Numbers(-6, 26), set_parser=int, get_parser=int, set_cmd=parent._set_in_amp_gain_0, get_cmd=parent._get_in_amp_gain_0, ) parent.add_parameter( "in1_gain", label="Input 1 gain", docstring="Sets/gets input 1 gain in a range of -6dB to 26dB " "with a resolution of 1dB per step.", unit="dB", vals=vals.Numbers(-6, 26), set_parser=int, get_parser=int, set_cmd=parent._set_in_amp_gain_1, get_cmd=parent._get_in_amp_gain_1, ) # -- Output offsets (All modules) ---------------------------------------- if parent.is_rf_type: parent.add_parameter( "out0_offset_path0", label="Output 0 offset for path 0", docstring="Sets/gets output 0 offset for path 0.", unit="mV", vals=vals.Numbers(-84.0, 73.0), set_parser=float, get_parser=float, set_cmd=parent._set_out_amp_offset_0, get_cmd=parent._get_out_amp_offset_0, ) parent.add_parameter( "out0_offset_path1", label="Output 0 offset for path 1", docstring="Sets/gets output 0 offset for path 1.", unit="mV", vals=vals.Numbers(-84.0, 73.0), set_parser=float, get_parser=float, set_cmd=parent._set_out_amp_offset_1, get_cmd=parent._get_out_amp_offset_1, ) if parent.is_qcm_type: parent.add_parameter( "out1_offset_path0", label="Output 1 offset for path 0", docstring="Sets/gets output 1 offset for path 0.", unit="mV", vals=vals.Numbers(-84.0, 73.0), set_parser=float, get_parser=float, set_cmd=parent._set_out_amp_offset_2, get_cmd=parent._get_out_amp_offset_2, ) parent.add_parameter( "out1_offset_path1", label="Output 1 offset for path 1", docstring="Sets/gets output 1 offset for path 1.", unit="mV", vals=vals.Numbers(-84.0, 73.0), set_parser=float, get_parser=float, set_cmd=parent._set_out_amp_offset_3, get_cmd=parent._get_out_amp_offset_3, ) else: parent.add_parameter( "out0_offset", label="Output 0 offset", docstring="Sets/gets output 0 offset", unit="V", vals=vals.Numbers(-2.5, 2.5) if parent.is_qcm_type else vals.Numbers(-0.5, 0.5), set_parser=float, get_parser=float, set_cmd=parent._set_dac_offset_0, get_cmd=parent._get_dac_offset_0, ) parent.add_parameter( "out1_offset", label="Output 1 offset", docstring="Sets/gets output 1 offset.", unit="V", vals=vals.Numbers(-2.5, 2.5) if parent.is_qcm_type else vals.Numbers(-0.5, 0.5), set_parser=float, get_parser=float, set_cmd=parent._set_dac_offset_1, get_cmd=parent._get_dac_offset_1, ) if parent.is_qcm_type: parent.add_parameter( "out2_offset", label="Output 2 offset", docstring="Sets/gets output 2 offset.", unit="V", vals=vals.Numbers(-2.5, 2.5), set_parser=float, get_parser=float, set_cmd=parent._set_dac_offset_2, get_cmd=parent._get_dac_offset_2, ) parent.add_parameter( "out3_offset", label="Output 3 offset", docstring="Sets/gets output 3 offset.", unit="V", vals=vals.Numbers(-2.5, 2.5), set_parser=float, get_parser=float, set_cmd=parent._set_dac_offset_3, get_cmd=parent._get_dac_offset_3, ) # -- Scope acquisition settings (QRM modules only) ----------------------- if parent.is_qrm_type: parent.add_parameter( "scope_acq_trigger_mode_path0", label="Scope acquisition trigger mode for input path 0", docstring="Sets/gets scope acquisition trigger mode for input " "path 0 ('sequencer' = triggered by sequencer, " "'level' = triggered by input level).", unit="", vals=vals.Bool(), val_mapping={"level": True, "sequencer": False}, set_parser=bool, get_parser=bool, set_cmd=partial( parent._set_acq_scope_config_val, "trig_mode_acq_path_0" ), get_cmd=partial( parent._get_acq_scope_config_val, "trig_mode_acq_path_0" ), ) parent.add_parameter( "scope_acq_trigger_mode_path1", label="Scope acquisition trigger mode for input path 1", docstring="Sets/gets scope acquisition trigger mode for input " "path 1 ('sequencer' = triggered by sequencer, " "'level' = triggered by input level).", unit="", vals=vals.Bool(), val_mapping={"level": True, "sequencer": False}, set_parser=bool, get_parser=bool, set_cmd=partial( parent._set_acq_scope_config_val, "trig_mode_acq_path_1" ), get_cmd=partial( parent._get_acq_scope_config_val, "trig_mode_acq_path_1" ), ) parent.add_parameter( "scope_acq_trigger_level_path0", label="Scope acquisition trigger level for input path 0", docstring="Sets/gets scope acquisition trigger level when using " "input level trigger mode for input path 0.", unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial( parent._set_acq_scope_config_val, "trig_lvl_acq_path_0_float" ), get_cmd=partial( parent._get_acq_scope_config_val, "trig_lvl_acq_path_0_float" ), ) parent.add_parameter( "scope_acq_trigger_level_path1", label="Scope acquisition trigger level for input path 1", docstring="Sets/gets scope acquisition trigger level when using " "input level trigger mode for input path 1.", unit="", vals=vals.Numbers(-1.0, 1.0), set_parser=float, get_parser=float, set_cmd=partial( parent._set_acq_scope_config_val, "trig_lvl_acq_path_1_float" ), get_cmd=partial( parent._get_acq_scope_config_val, "trig_lvl_acq_path_1_float" ), ) parent.add_parameter( "scope_acq_sequencer_select", label="Scope acquisition sequencer select", docstring="Sets/gets sequencer select that specifies which " "sequencer triggers the scope acquisition when using " "sequencer trigger mode.", unit="", vals=vals.Numbers(0, num_seq), set_parser=int, get_parser=int, set_cmd=partial( parent._set_acq_scope_config_val, "sel_acq" ), get_cmd=partial( parent._get_acq_scope_config_val, "sel_acq" ), ) parent.add_parameter( "scope_acq_avg_mode_en_path0", label="Scope acquisition averaging mode enable for input path 0", docstring="Sets/gets scope acquisition averaging mode enable for " "input path 0.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial( parent._set_acq_scope_config_val, "avg_en_acq_path_0" ), get_cmd=partial( parent._get_acq_scope_config_val, "avg_en_acq_path_0" ), ) parent.add_parameter( "scope_acq_avg_mode_en_path1", label="Scope acquisition averaging mode enable for input path 0", docstring="Sets/gets scope acquisition averaging mode enable for " "input path 0.", unit="", vals=vals.Bool(), set_parser=bool, get_parser=bool, set_cmd=partial( parent._set_acq_scope_config_val, "avg_en_acq_path_1" ), get_cmd=partial( parent._get_acq_scope_config_val, "avg_en_acq_path_1" ), ) # Add sequencers for seq_idx in range(0, num_seq): seq = Sequencer(parent, "sequencer{}".format(seq_idx), seq_idx) parent.add_submodule("sequencer{}".format(seq_idx), seq)
# ----------------------------------------------------------------------------
[docs]def invalidate_qcodes_parameter_cache(parent: Union[Instrument, QcmQrm]) -> None: """ Marks the cache of all QCoDeS parameters in the module as invalid, including in any sequencer submodules the module might have. Parameters ---------- parent : Union[Instrument, QcmQrm] Parent module object for which to invalidate the QCoDeS parameters. Returns ---------- Raises ---------- """ # Invalidate module parameters for param in parent.parameters.values(): param.cache.invalidate() # Invalidate sequencer parameters for seq in parent.sequencers: seq._invalidate_qcodes_parameter_cache()
# ----------------------------------------------------------------------------
[docs]def get_item( parent: Union[Instrument, QcmQrm], key: str ) -> Union[InstrumentChannel, Parameter, Callable[[Any], Any]]: """ Get submodule or parameter using string based lookup. Parameters ---------- parent : Union[Instrument, QcmQrm] Parent module object to search. key : str submodule, parameter or function to retrieve. Returns ---------- Union[InstrumentChannel, Parameter, Callable[[Any], Any]] Submodule, parameter or function. Raises ---------- KeyError Submodule, parameter or function does not exist. """ # Check for submodule try: return parent.submodules[key] except KeyError: try: return parent.parameters[key] except KeyError: return parent.functions[key]