def _connect(self) -> None: """Connects the device to the data server. Instantiates the device controller from :mod:`zhinst-toolkit`, sets up the data server and connects the device the data server. This method is called from `__init__` of the :class:`BaseInstrument` class. """ self._controller = tk.HDAWG( self._name, self._serial, interface=self._interface, host=self._host, port=self._port, api=self._api, ) self._controller.setup() self._controller.connect_device(nodetree=False) self.connect_message() self._get_nodetree_dict() # initialize ChannelList of AWGs channel_list = ChannelList(self, "awgs", AWG) for i in range(4): channel_list.append(AWG(f"awg-{i}", i, self, self._controller)) channel_list.lock() self.add_submodule("awgs", channel_list)
def __init__( self, name: str = 'mdac', num_channels: int = 10, **kwargs): """ Create a dummy instrument that can be used for testing Args: name: name for the instrument gates: list of names that is used to create parameters for the instrument """ super().__init__(name, **kwargs) # make gates channels = ChannelList(self, "channels", MockDACChannel) for n in range(num_channels): num = str(n + 1).zfill(2) chan_name = f"ch{num}" channel = MockDACChannel(parent=self, name=chan_name, num=num) channels.append(channel) self.add_submodule(chan_name, channel) self.add_submodule("channels", channels)
def _init_readout_channels(self): # init submodules for ReadoutChannels channel_list = ChannelList(self, "channels", Channel) for i in range(10): channel_list.append(Channel(f"ch-{i}", i, self, self._controller)) channel_list.lock() self.add_submodule("channels", channel_list)
def _init_awg_channels(self): # initialize ChannelList of AWGs channel_list = ChannelList(self, "awgs", AWG) for i in range(4): channel_list.append(AWG(f"awg-{i}", i, self, self._controller)) channel_list.lock() self.add_submodule("awgs", channel_list)
def __init__(self, name, **kwargs): super().__init__(name, **kwargs) channels = ChannelList(self, "TempSensors", DummyChannel, snapshotable=False) for chan_name in ('A', 'B', 'C', 'D', 'E', 'F'): channel = DummyChannel(self, f'Chan{chan_name}', chan_name) channels.append(channel) self.add_submodule(chan_name, channel) self.add_submodule("channels", channels)
def _init_qachannels(self): # init submodules for QAChannels num_qachannels = self._controller.num_qachannels() channel_list = ChannelList(self, "qachannels", QAChannel) for i in range(num_qachannels): channel_list.append( QAChannel(f"qachannel-{i}", i, self, self._controller)) channel_list.lock() self.add_submodule("qachannels", channel_list)
def __init__(self, name, address, silent=False, **kwargs): """ Args: name (string): The name of the instrument used internally by QCoDeS. Must be unique. address (string): The VISA resource name. silent (Optional[bool]): If True, no connect message is printed. """ warnings.warn( "This driver is old and will be removed " "from QCoDeS soon. Please use the " "WaveformGenerator_33XXX from the file " "instrument_drivers/Keysight/KeysightAgilent_33XXX" " instead.", UserWarning) super().__init__(name, address, **kwargs) channels = ChannelList(self, "Channels", KeysightChannel, snapshotable=False) for i in range(1, 3): channel = KeysightChannel(self, 'ch{}'.format(i), i) channels.append(channel) channels.lock() self.add_submodule('channels', channels) self.add_parameter('sync_source', label='Source of sync function', set_cmd='OUTPut:SYNC:SOURce {}', get_cmd='OUTPut:SYNC:SOURce?', val_mapping={ 1: 'CH1', 2: 'CH2' }, vals=vals.Enum(1, 2)) self.add_parameter('sync_output', label='Sync output state', set_cmd='OUTPut:SYNC {}', get_cmd='OUTPut:SYNC?', val_mapping={ 'ON': 1, 'OFF': 0 }, vals=vals.Enum('ON', 'OFF')) self.add_function('force_trigger', call_cmd='*TRG') self.add_function('sync_channel_phases', call_cmd='PHAS:SYNC') if not silent: self.connect_message()
def _init_integrations(self): # init submodules for Integration Units num_integrations = self._readout.device.num_integrations_per_qachannel( ) channel_list = ChannelList(self, "integrations", Integration) for i in range(num_integrations): channel_list.append( Integration(f"integration-{i}", i, self, self._readout)) channel_list.lock() self.add_submodule("integrations", channel_list) self._readout._init_integrations()
def __init__(self, name, DACChannelClass: Type[DACChannelInterface]): assert issubclass(DACChannelClass, DACChannelInterface) super().__init__(name) channels = ChannelList(self, "Channels", DACChannelClass, snapshotable=False) for chan_id in range(0, 64): chan_name = f"nt channel {chan_id}" channel = DACChannelClass(self, chan_name, chan_id) channels.append(channel) self.add_submodule(chan_name, channel) self.add_submodule("nt_channels", channels)
def test_channel_tuple_snapshot_enabled(empty_instrument): channels = ChannelList(empty_instrument, "ListElem", DummyChannel, snapshotable=True) for chan_name in ("A", "B", "C", "D", "E", "F"): channel = DummyChannel(empty_instrument, f"Chan{chan_name}", chan_name) channels.append(channel) empty_instrument.add_submodule("channels", channels) snapshot = empty_instrument.channels.snapshot() assert snapshot["snapshotable"] is True assert len(snapshot.keys()) == 3 assert "channels" in snapshot.keys()
def __init__(self, name, address, **kwargs): super().__init__(name, address, 5, '\r\n', **kwargs) # configure the port if 'ASRL' not in address: self.visa_handle.baud_rate = 38400 # USB has no baud rate parameter self.visa_handle.stop_bits = pyvisa.constants.StopBits.one self.visa_handle.parity = pyvisa.constants.Parity.none self.visa_handle.read_termination = '\r\n' self.parameters.pop('IDN') # Get rid of this parameter # for security check the ID from the device self.idn = self.ask("ver") log.debug("Main version:", self.idn) if not self.idn.startswith("attocube ANC300"): raise RuntimeError("Invalid device ID found: " + str(self.idn)) # Now request all serial numbers of the axis modules. The first 6 chars are the id # of the module type. This will be used to enable special parameters. # Instantiate the axis channels only for available axis axischannels = ChannelList(self, "Anc300Channels", Anc300Axis, snapshotable=False) for ax in range(1, 7 + 1): try: tmp = self.ask('getser {}'.format(ax)) name = 'axis{}'.format(ax) axischan = Anc300Axis(self, name, ax, tmp[:6]) axischannels.append(axischan) self.add_submodule(name, axischan) except: pass axischannels.lock() self.add_submodule('axis_channels', axischannels) # instantiate the trigger channels even if they could not be tested triggerchannels = ChannelList(self, "Anc300Trigger", Anc300TriggerOut, snapshotable=False) for ax in [1, 2, 3]: name = 'trigger{}'.format(ax) trigchan = Anc300TriggerOut(self, name, ax) triggerchannels.append(trigchan) self.add_submodule(name, trigchan) triggerchannels.lock() self.add_submodule('trigger_channels', triggerchannels)
def _make_dci_with_list(): for i in range(10): pass dci = Instrument(name="dciwl") channels = ChannelList(dci, "ListElem", DummyChannel, snapshotable=False) for chan_name in ("A", "B", "C", "D", "E", "F"): channel = DummyChannel(dci, f"Chan{chan_name}", chan_name) channels.append(channel) dci.add_submodule(chan_name, channel) dci.add_submodule("channels", channels) try: yield dci finally: dci.close()
def __init__(self, name, channel_names=None, **kwargs): super().__init__(name, **kwargs) channels = ChannelList(self, "TempSensors", DummyChannel, snapshotable=False) if channel_names is None: channel_ids = ("A", "B", "C", "D", "E", "F") channel_names = tuple(f"Chan{chan_name}" for chan_name in channel_ids) else: channel_ids = channel_names for chan_name, chan_id in zip(channel_names, channel_ids): channel = DummyChannel(self, chan_name, chan_id) channels.append(channel) self.add_submodule(chan_id, channel) self.add_submodule("channels", channels.to_channel_tuple())
def __init__(self, name, address, port=23): super().__init__(name, address, port) self.flush_connection() channels = ChannelList(self, "Channels", MC_channel, snapshotable=False) _chanlist = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] _max_channel_number = int(self.IDN()['model'][3]) _chanlist = _chanlist[0:_max_channel_number] for c in _chanlist: channel = MC_channel(self, f'channel_{c}', c) channels.append(channel) self.add_submodule(f'channel_{c}', channel) channels.lock() self.add_submodule('channels', channels) self.connect_message()
def __init__(self, name: str, address: str, port: int = 23): super().__init__(name, address, port) self.flush_connection() channels = ChannelList(self, "Channels", MC_channel, snapshotable=False) _chanlist = ['a', 'b'] _max_channel_number = int(self.IDN()['model'][3]) _chanlist = _chanlist[0:_max_channel_number] for c in _chanlist: channel = MC_channel(self, f'channel_{c}', c) channels.append(channel) self.add_submodule(f"channel_{c}", channel) self.add_submodule("channels", channels.to_channel_tuple()) self.connect_message()
def _add_submodules_recursively(self, parent, treedict: Dict) -> None: """ Recursively add submodules (ZINodes) for each node in the ZI node tree. At the leaves create a parameter. Create a ChannelList as submodules whenever a node is enumerated, e.g. 'dev8030/sigouts/*/on'. Arguments: parent (InstrumentChannel): parent QCoDeS object, either Instrument(-Channel) or ZINode treedict (dict): dictionary specifying the (sub-)tree of the ZI node hirarchy """ for key, value in treedict.items(): if all(isinstance(k, int) for k in value.keys()): # if enumerated node if "Node" in list(value.values())[0].keys(): # if at leave, don't create ChannelList but parameter with "{key}{i}" for k in value.keys(): self._add_parameter_from_dict(parent, f"{key}{k}", value[k]) else: # else, create ChannelList to hold all enumerated ZINodes channel_list = ChannelList(parent, key, ZINode) for k in value.keys(): ch_name = f"{key}{k}" ch = ZINode(parent, ch_name) channel_list.append(ch) self._add_submodules_recursively(ch, treedict[key][k]) channel_list.lock() parent.add_submodule(key, channel_list) else: # if not enumerated ZINode if "Node" in value.keys(): # if at leave add a parameter to the node self._add_parameter_from_dict(parent, key, value) else: # if not at leave, create ZINode as submodule module = ZINode(parent, key) parent.add_submodule(key, module) self._add_submodules_recursively(module, treedict[key])
def add_channels(self): channels = ChannelList( self, "Channels", self.CHANNEL_CLASS, snapshotable=False) _chanlist = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] self._deprecated_attributes = { f'channel_{k}': k for k in _chanlist } _max_channel_number = self.get_number_of_channels() _chanlist = _chanlist[0:_max_channel_number] for c in _chanlist: channel = self.CHANNEL_CLASS(self, f'channel_{c}', c) channels.append(channel) attribute_name = f'channel_{c}' self.add_submodule(attribute_name, channel) self.add_submodule(c, channel) self._deprecated_attributes[attribute_name] = c channels.lock() self.add_submodule('channels', channels)
def __init__(self, name: str, library: ANC350v3Lib, inst_no: int = 0): super().__init__(name) if isinstance(library, ANC350v4Lib): self._version_no = 4 elif isinstance(library, ANC350v3Lib): self._version_no = 3 else: raise NotImplementedError( "Only version 3 and 4 of ANC350's driver-DLL are currently " "supported") self._lib = library self._device_no = inst_no self._device_handle = self._lib.connect(inst_no) axischannels = ChannelList(self, "Anc350Axis", Anc350Axis) for nr, axis in enumerate(['x', 'y', 'z']): axis_name = f"{axis}_axis" axischannel = Anc350Axis(parent=self, name=axis_name, axis=nr) axischannels.append(axischannel) self.add_submodule(axis_name, axischannel) axischannels.lock() self.add_submodule("axis_channels", axischannels)
def _connect(self) -> None: """Connects the device to the data server. Instantiates the device controller from :mod:`zhinst-toolkit`, sets up the data server and connects the device the data server. This method is called from `__init__` of the :class:`BaseInstrument` class. """ self._controller = tk.UHFQA( self._name, self._serial, interface=self._interface, host=self._host, port=self._port, api=self._api, ) self._controller.setup() self._controller.connect_device(nodetree=False) self.connect_message() self._get_nodetree_dict() # init submodules for ReadoutChannels and AWG channel_list = ChannelList(self, "channels", Channel) for i in range(10): channel_list.append(Channel(f"ch-{i}", i, self, self._controller)) channel_list.lock() self.add_submodule("channels", channel_list) self.add_submodule("awg", AWG("awg", self, self._controller)) # add custom parameters as QCoDeS parameters self.add_parameter( "crosstalk_matrix", docstring= "The 10x10 crosstalk suppression matrix that multiplies the 10 signal paths. Can be set only partially.", get_cmd=self._controller.crosstalk_matrix, set_cmd=self._controller.crosstalk_matrix, label="Crosstalk Matrix", ) sources = [ "Crosstalk", "Integration", "Threshold", "Crosstalk Correlation", "Threshold Correlation", "Rotation", ] self.add_parameter( "result_source", docstring= f"The signal source for QA Results. Has to be one of {sources}.", get_cmd=self._controller.result_source, set_cmd=self._controller.result_source, label="Result Source", vals=vals.Enum(*sources), ) self.add_parameter( "integration_time", docstring= "The integration time used for demodulation in seconds. Can be up to 2.27 us when using weighted integration and up to 50 us in spectroscopy mode.", get_cmd=self._controller.integration_time, set_cmd=self._controller.integration_time, label="Integration Time", vals=vals.Numbers(0, 50e-6), ) self.add_parameter( "averaging_mode", docstring=self._controller.averaging_mode._description, get_cmd=self._controller.averaging_mode, set_cmd=self._controller.averaging_mode, label="Averaging Mode", vals=vals.Enum("Cyclic", "Sequential"), )
def __init__(self, name, model, chassis, slot, channel_idxs: List[int] = None, triggers=8, **kwargs): super().__init__(name, model, chassis, slot, triggers, **kwargs) if channel_idxs is None: channel_idxs = model_channel_idxs[self.model] self.channel_idxs = channel_idxs # Create instance of keysight SD_AOU class self.awg = logclass(keysightSD1.SD_AOU)() # Create an instance of keysight SD_Wave class self.wave = logclass(keysightSD1.SD_Wave)() # Open the device using the specified chassis and slot number self.initialize(chassis=chassis, slot=slot) self.add_parameter('clock_output', val_mapping={ 'off': 0, 'on': 1 }, set_function=self.awg.clockIOconfig, docstring="Turn clock connector output on or off.") self.add_parameter( 'trigger_io', label='trigger io', get_function=self.awg.triggerIOread, set_function=self.awg.triggerIOwrite, docstring='The trigger input value, 0 (OFF) or 1 (ON)', vals=vals.Enum(0, 1)) self.add_parameter('trigger_direction', label=f'trigger direction', val_mapping={ 'in': 1, 'out': 0 }, set_function=self.awg.triggerIOconfig, docstring='Determines if trig i/o should be used ' 'as a trigger input or trigger output.') self.add_parameter('clock_frequency', label='clock frequency', unit='Hz', get_function=self.awg.clockGetFrequency, set_function=self.awg.clockSetFrequency, docstring='The real hardware clock frequency in Hz', vals=vals.Numbers(100e6, 500e6)) self.add_parameter( 'clock_sync_frequency', label='clock sync frequency', unit='Hz', get_function=self.awg.clockGetSyncFrequency, docstring='The frequency of the internal CLKsync in Hz') channels = ChannelList(self, name='channels', chan_type=AWGChannel) for ch in self.channel_idxs: channel = AWGChannel(self, name=f'ch{ch}', id=ch) setattr(self, f'ch{ch}', channel) channels.append(channel) self.add_submodule('channels', channels)
def __init__(self, name, address, num_chans=48, update_currents=True, **kwargs): """ Instantiates the instrument. Args: name (str): The instrument name used by qcodes address (str): The VISA name of the resource num_chans (int): Number of channels to assign. Default: 48 update_currents (bool): Whether to query all channels for their current current value on startup. Default: True. Returns: QDac object """ super().__init__(name, address, **kwargs) self._output_n_lines = 50 handle = self.visa_handle self._get_status_performed = False # This is the baud rate on power-up. It can be changed later but # you must start out with this value. handle.baud_rate = 480600 handle.parity = visa.constants.Parity(0) handle.data_bits = 8 self.set_terminator('\n') # TODO: do we want a method for write termination too? handle.write_termination = '\n' # TODO: do we need a query delay for robust operation? self._write_response = '' if self._get_firmware_version() < 0.170202: raise RuntimeError(''' Obsolete QDAC Software version detected. QCoDeS only supports version 0.170202 or newer. Contact [email protected] for an update. ''') self.num_chans = num_chans # Assigned slopes. Entries will eventually be [chan, slope] self._slopes = [] # Function generators (used in _set_voltage) self._fgs = set(range(1, 9)) self._assigned_fgs = {} # {chan: fg} # Sync channels self._syncoutputs = [] # Entries: [chan, syncchannel] self.chan_range = range(1, 1 + self.num_chans) self.channel_validator = vals.Ints(1, self.num_chans) channels = ChannelList(self, "Channels", QDacChannel, snapshotable=False, multichan_paramclass=QDacMultiChannelParameter) for i in self.chan_range: channel = QDacChannel(self, f'chan{i:02}', i) channels.append(channel) # Should raise valueerror if name is invalid (silently fails now) self.add_submodule(f'ch{i:02}', channel) channels.lock() self.add_submodule('channels', channels) for board in range(6): for sensor in range(3): label = f'Board {board}, Temperature {sensor}' self.add_parameter(name=f'temp{board}_{sensor}', label=label, unit='C', get_cmd=f'tem {board} {sensor}', get_parser=self._num_verbose) self.add_parameter(name='cal', set_cmd='cal {}', vals=self.channel_validator) # TO-DO: maybe it's too dangerous to have this settable. # And perhaps ON is a better verbose mode default? self.add_parameter(name='verbose', set_cmd='ver {}', val_mapping={ True: 1, False: 0 }) self.add_parameter(name='fast_voltage_set', label='fast voltage set', get_cmd=None, set_cmd=None, vals=vals.Bool(), initial_value=False, docstring=""""Deprecated with no functionality""") # Initialise the instrument, all channels DC (unbind func. generators) for chan in self.chan_range: # Note: this call does NOT change the voltage on the channel self.write(f'wav {chan} 0 1 0') self.verbose.set(False) self.connect_message() log.info('[*] Querying all channels for voltages and currents...') self._update_cache(readcurrents=update_currents) self._update_currents = update_currents log.info('[+] Done')
def __init__(self, name: str, address: str, timeout: float = 20, **kwargs: Any): """ Initialises the DS4000. Args: name: Name of the instrument used by QCoDeS address: Instrument address as used by VISA timeout: visa timeout, in secs. long default (180) to accommodate large waveforms """ # Init VisaInstrument. device_clear MUST NOT be issued, otherwise communications hangs # due a bug in firmware super().__init__(name, address, device_clear=False, timeout=timeout, **kwargs) self.connect_message() self._check_firmware_version() # functions self.add_function('run', call_cmd=':RUN', docstring='Start acquisition') self.add_function('stop', call_cmd=':STOP', docstring='Stop acquisition') self.add_function('single', call_cmd=':SINGle', docstring='Single trace acquisition') self.add_function('force_trigger', call_cmd='TFORce', docstring='Force trigger event') self.add_function("auto_scale", call_cmd=":AUToscale", docstring="Perform autoscale") # general parameters self.add_parameter('trigger_type', label='Type of the trigger', get_cmd=':TRIGger:MODE?', set_cmd=':TRIGger:MODE {}', vals=vals.Enum('EDGE', 'PULS', 'RUNT', 'NEDG', 'SLOP', 'VID', 'PATT', 'RS232', 'IIC', 'SPI', 'CAN', 'FLEX', 'USB')) self.add_parameter('trigger_mode', label='Mode of the trigger', get_cmd=':TRIGger:SWEep?', set_cmd=':TRIGger:SWEep {}', vals=vals.Enum('AUTO', 'NORM', 'SING')) self.add_parameter("time_base", label="Horizontal time base", get_cmd=":TIMebase:MAIN:SCALe?", set_cmd=":TIMebase:MAIN:SCALe {}", get_parser=float, unit="s/div") self.add_parameter("sample_point_count", label="Number of the waveform points", get_cmd=":WAVeform:POINts?", set_cmd=":WAVeform:POINts {}", get_parser=int, vals=Ints(min_value=1)) self.add_parameter("enable_auto_scale", label="Enable or disable autoscale", get_cmd=":SYSTem:AUToscale?", set_cmd=":SYSTem:AUToscale {}", get_parser=bool, vals=Bool()) channels = ChannelList(self, "Channels", RigolDS4000Channel, snapshotable=False) for channel_number in range(1, 5): channel = RigolDS4000Channel(self, f"ch{channel_number}", channel_number) channels.append(channel) channels.lock() self.add_submodule('channels', channels)
def __init__(self, name: str, address: str, max_frequency: float, reset: bool = False, **kwargs: Any): super().__init__(name, address, terminator='\n', **kwargs) self._max_frequency = max_frequency # Reference commands self.add_parameter(name='frequency', label='Frequency', unit='Hz', get_cmd='FREQ?', set_cmd='FREQ {}', get_parser=float, vals=Numbers(min_value=1e-3, max_value=self._max_frequency)) self.add_parameter(name='sine_outdc', label='Sine out dc level', unit='V', get_cmd='SOFF?', set_cmd='SOFF {}', get_parser=float, vals=Numbers(min_value=-5, max_value=5)) self.add_parameter(name='amplitude', label='Amplitude', unit='V', get_cmd='SLVL?', set_cmd='SLVL {}', get_parser=float, vals=Numbers(min_value=0, max_value=2)) self.add_parameter(name='harmonic', label='Harmonic', get_cmd='HARM?', get_parser=int, set_cmd='HARM {:d}', vals=Ints(min_value=1, max_value=99)) self.add_parameter(name='phase', label='Phase', unit='deg', get_cmd='PHAS?', set_cmd='PHAS {}', get_parser=float, vals=Numbers(min_value=-3.6e5, max_value=3.6e5)) # Signal commands self.add_parameter(name='sensitivity', label='Sensitivity', get_cmd='SCAL?', set_cmd='SCAL {:d}', get_parser=self._get_sensitivity, set_parser=self._set_sensitivity) self.add_parameter(name='filter_slope', label='Filter slope', unit='dB/oct', get_cmd='OFSL?', set_cmd='OFSL {}', val_mapping={ 6: 0, 12: 1, 18: 2, 24: 3 }) self.add_parameter(name='sync_filter', label='Sync filter', get_cmd='SYNC?', set_cmd='SYNC {}', val_mapping={ 'OFF': 0, 'ON': 1 }) self.add_parameter(name='noise_bandwidth', label='Noise bandwidth', unit='Hz', get_cmd='ENBW?', get_parser=float) self.add_parameter(name='signal_strength', label='Signal strength indicator', get_cmd='ILVL?', get_parser=int) self.add_parameter(name='signal_input', label='Signal input', get_cmd='IVMD?', get_parser=self._get_input_config, set_cmd='IVMD {}', set_parser=self._set_input_config, vals=Enum(*self._INPUT_SIGNAL_TO_N.keys())) self.add_parameter(name='input_range', label='Input range', unit='V', get_cmd='IRNG?', set_cmd='IRNG {}', val_mapping={ 1: 0, 300e-3: 1, 100e-3: 2, 30e-3: 3, 10e-3: 4 }) self.add_parameter(name='input_config', label='Input configuration', get_cmd='ISRC?', set_cmd='ISRC {}', val_mapping={ 'a': 0, 'a-b': 1 }) self.add_parameter(name='input_shield', label='Input shield', get_cmd='IGND?', set_cmd='IGND {}', val_mapping={ 'float': 0, 'ground': 1 }) self.add_parameter(name='input_gain', label='Input gain', unit='ohm', get_cmd='ICUR?', set_cmd='ICUR {}', val_mapping={ 1e6: 0, 100e6: 1 }) self.add_parameter(name='adv_filter', label='Advanced filter', get_cmd='ADVFILT?', set_cmd='ADVFILT {}', val_mapping={ 'OFF': 0, 'ON': 1 }) self.add_parameter(name='input_coupling', label='Input coupling', get_cmd='ICPL?', set_cmd='ICPL {}', val_mapping={ 'ac': 0, 'dc': 1 }) self.add_parameter(name='time_constant', label='Time constant', unit='s', get_cmd='OFLT?', set_cmd='OFLT {}', val_mapping={ 1e-6: 0, 3e-6: 1, 10e-6: 2, 30e-6: 3, 100e-6: 4, 300e-6: 5, 1e-3: 6, 3e-3: 7, 10e-3: 8, 30e-3: 9, 100e-3: 10, 300e-3: 11, 1: 12, 3: 13, 10: 14, 30: 15, 100: 16, 300: 17, 1e3: 18, 3e3: 19, 10e3: 20, 30e3: 21 }) self.add_parameter( name="external_reference_trigger", label="External reference trigger mode", get_cmd="RTRG?", set_cmd="RTRG {}", val_mapping={ "SIN": 0, "POS": 1, "POSTTL": 1, "NEG": 2, "NEGTTL": 2, }, docstring="The triggering mode for synchronization of the " "internal reference signal with the externally provided " "one") self.add_parameter(name="reference_source", label="Reference source", get_cmd="RSRC?", set_cmd="RSRC {}", val_mapping={ "INT": 0, "EXT": 1, "DUAL": 2, "CHOP": 3 }, docstring="The source of the reference signal") self.add_parameter( name="external_reference_trigger_input_resistance", label="External reference trigger input resistance", get_cmd="REFZ?", set_cmd="REFZ {}", val_mapping={ "50": 0, "50OHMS": 0, 0: 0, "1M": 1, "1MEG": 1, 1: 1, }, docstring="Input resistance of the input for the external " "reference signal") # Auto functions self.add_function('auto_range', call_cmd='ARNG') self.add_function('auto_scale', call_cmd='ASCL') self.add_function('auto_phase', call_cmd='APHS') # Data transfer # first 4 parameters from a list of 16 below. self.add_parameter('X', label='In-phase Magnitude', get_cmd='OUTP? 0', get_parser=float, unit='V') self.add_parameter('Y', label='Out-phase Magnitude', get_cmd='OUTP? 1', get_parser=float, unit='V') self.add_parameter('R', label='Magnitude', get_cmd='OUTP? 2', get_parser=float, unit='V') self.add_parameter('P', label='Phase', get_cmd='OUTP? 3', get_parser=float, unit='deg') # CH1/CH2 Output Commands self.add_parameter('X_offset', label='X offset ', unit='%', get_cmd='COFP? 0', set_cmd='COFP 0, {}', get_parser=float, vals=Numbers(min_value=-999.99, max_value=999.99)) self.add_parameter('Y_offset', label='Y offset', unit='%', get_cmd='COFP? 1', set_cmd='COFP 1, {}', get_parser=float, vals=Numbers(min_value=-999.99, max_value=999.99)) self.add_parameter('R_offset', label='R offset', unit='%', get_cmd='COFP? 2', set_cmd='COFP 2, {}', get_parser=float, vals=Numbers(min_value=-999.99, max_value=999.99)) self.add_parameter('X_expand', label='X expand multiplier', get_cmd='CEXP? 0', set_cmd='CEXP 0, {}', val_mapping={ 'OFF': '0', 'X10': '1', 'X100': '2' }) self.add_parameter('Y_expand', label='Y expand multiplier', get_cmd='CEXP? 1', set_cmd='CEXP 1, {}', val_mapping={ 'OFF': 0, 'X10': 1, 'X100': 2 }) self.add_parameter('R_expand', label='R expand multiplier', get_cmd='CEXP? 2', set_cmd='CEXP 2, {}', val_mapping={ 'OFF': 0, 'X10': 1, 'X100': 2 }) # Aux input/output for i in [0, 1, 2, 3]: self.add_parameter(f'aux_in{i}', label=f'Aux input {i}', get_cmd=f'OAUX? {i}', get_parser=float, unit='V') self.add_parameter(f'aux_out{i}', label=f'Aux output {i}', get_cmd=f'AUXV? {i}', get_parser=float, set_cmd=f'AUXV {i}, {{}}', unit='V') # Data channels: # 'DAT1' (green), 'DAT2' (blue), 'DAT3' (yellow), 'DAT4' (orange) data_channels = ChannelList(self, "data_channels", SR86xDataChannel, snapshotable=False) for num, color in zip(range(self._N_DATA_CHANNELS), ('green', 'blue', 'yellow', 'orange')): cmd_id = f"{num}" cmd_id_name = f"DAT{num + 1}" ch_name = f"data_channel_{num + 1}" data_channel = SR86xDataChannel(self, ch_name, cmd_id, cmd_id_name, color) data_channels.append(data_channel) self.add_submodule(ch_name, data_channel) data_channels.lock() self.add_submodule("data_channels", data_channels) # Interface self.add_function('reset', call_cmd='*RST') self.add_function('disable_front_panel', call_cmd='OVRM 0') self.add_function('enable_front_panel', call_cmd='OVRM 1') buffer = SR86xBuffer(self, f"{self.name}_buffer") self.add_submodule("buffer", buffer) self.input_config() self.connect_message()
def __init__( self, name: str, address: str, timeout: float = 20, channels: int = 4, silence_pyvisapy_warning: bool = False, **kwargs: Any, ): """ Initialises the oscilloscope. Args: name: Name of the instrument used by QCoDeS address: Instrument address as used by VISA timeout: Visa timeout, in secs. channels: The number of channels on the scope. silence_pyvisapy_warning: Don't warn about pyvisa-py at startup """ super().__init__(name, address, timeout=timeout, terminator="\n", **kwargs) self.connect_message() # Check if we are using pyvisa-py as our visa lib and warn users that # this may cause long digitize operations to fail if (self.visa_handle.visalib.library_path == "py" and not silence_pyvisapy_warning): self.log.warning( "Timeout not handled correctly in pyvisa_py. This may cause" " long acquisitions to fail. Either use ni/keysight visalib" " or set timeout to longer than longest expected acquisition" " time.") # switch the response header off else none of our parameters will work self.write(":SYSTem:HEADer OFF") # Then set up the data format used to retrieve waveforms self.write(":WAVEFORM:FORMAT WORD") self.write(":WAVEFORM:BYTEORDER LSBFirst") self.write(":WAVEFORM:STREAMING ON") # Query the oscilloscope parameters # Set sample rate, bandwidth and memory depth limits self._query_capabilities() # Number of channels can't be queried on most older scopes. Use a parameter # for now. self.no_channels = channels # Run state self.run_mode = Parameter( name="run_mode", instrument=self, label="run mode", get_cmd=":RST?", vals=vals.Enum("RUN", "STOP", "SING"), ) # Timing Parameters self.timebase_range = Parameter( name="timebase_range", instrument=self, label="Range of the time axis", unit="s", get_cmd=":TIM:RANG?", set_cmd=":TIM:RANG {}", vals=vals.Numbers(5e-12, 20), get_parser=float, ) self.timebase_position = Parameter( name="timebase_position", instrument=self, label="Offset of the time axis", unit="s", get_cmd=":TIM:POS?", set_cmd=":TIM:POS {}", vals=vals.Numbers(), get_parser=float, ) self.timebase_roll_enabled = Parameter( name="timebase_roll_enabled", instrument=self, label="Is rolling mode enabled", get_cmd=":TIM:ROLL:ENABLE?", set_cmd=":TIM:ROLL:ENABLE {}", val_mapping={ True: 1, False: 0 }, ) # Trigger self.trigger_mode = Parameter( name="trigger_mode", instrument=self, label="Trigger mode", get_cmd=":TRIG:MODE?", ) self.trigger_sweep = Parameter( name="trigger_sweep", instrument=self, label="Trigger sweep mode", get_cmd=":TRIG:SWE?", set_cmd=":TRIG:SWE {}", vals=vals.Enum("AUTO", "TRIG"), ) self.trigger_state = Parameter( name="trigger_state", instrument=self, label="Trigger state", get_cmd=":AST?", vals=vals.Enum("ARM", "TRIG", "ATRIG", "ADONE"), snapshot_value=False, ) # Edge trigger parameters # Note that for now we only support parameterized edge triggers - this may # be something worth expanding. # To set trigger level, use the "trigger_level" parameter in each channel self.trigger_edge_source = Parameter( name="trigger_edge_source", instrument=self, label="Source channel for the edge trigger", get_cmd=":TRIGger:EDGE:SOURce?", set_cmd=":TRIGger:EDGE:SOURce {}", vals=vals.Enum(*([f"CHAN{i}" for i in range(1, 4 + 1)] + [f"DIG{i}" for i in range(16 + 1)] + ["AUX", "LINE"])), ) self.trigger_edge_slope = Parameter( name="trigger_edge_slope", instrument=self, label="slope of the edge trigger", get_cmd=":TRIGger:EDGE:SLOPe?", set_cmd=":TRIGger:EDGE:SLOPe {}", vals=vals.Enum("POS", "POSITIVE", "NEG", "NEGATIVE", "EITH"), ) self.trigger_level_aux = Parameter( name="trigger_level_aux", instrument=self, label="Tirgger level AUX", unit="V", get_cmd=":TRIGger:LEVel? AUX", set_cmd=":TRIGger:LEVel AUX,{}", get_parser=float, vals=vals.Numbers(), ) # Aquisition # If sample points, rate and timebase_scale are set in an # incomensurate way, the scope only displays part of the waveform self.acquire_points = Parameter( name="acquire_points", instrument=self, label="sample points", get_cmd=":ACQ:POIN?", set_cmd=":ACQ:POIN {}", get_parser=int, vals=vals.Numbers(min_value=self.min_pts, max_value=self.max_pts), ) self.sample_rate = Parameter( name="sample_rate", instrument=self, label="sample rate", get_cmd=":ACQ:SRAT?", set_cmd=":ACQ:SRAT {}", unit="Hz", get_parser=float, vals=vals.Numbers(min_value=self.min_srat, max_value=self.max_srat), ) # Note: newer scopes allow a per-channel bandwidth. This is not implemented yet. self.bandwidth = Parameter( name="bandwidth", instrument=self, label="bandwidth", get_cmd=":ACQ:BAND?", set_cmd=":ACQ:BAND {}", unit="Hz", get_parser=float, vals=vals.Numbers(min_value=self.min_bw, max_value=self.max_bw), ) self.acquire_interpolate = Parameter( name="acquire_interpolate", instrument=self, get_cmd=":ACQ:INTerpolate?", set_cmd=":ACQuire:INTerpolate {}", vals=vals.Enum(0, 1, "INT1", "INT2", "INT4", "INT8", "INT16", "INT32"), ) self.acquire_mode = Parameter( name="acquire_mode", instrument=self, label="Acquisition mode", get_cmd="ACQuire:MODE?", set_cmd="ACQuire:MODE {}", vals=vals.Enum( "ETIMe", "RTIMe", "PDETect", "HRESolution", "SEGMented", "SEGPdetect", "SEGHres", ), ) self.average = Parameter( name="average", instrument=self, label="Averages", get_cmd=self._get_avg, set_cmd=self._set_avg, vals=vals.Ints(min_value=1, max_value=10486575), ) # Automatically digitize before acquiring a trace self.auto_digitize: Parameter = Parameter( name="auto_digitize", instrument=self, label="Auto digitize", set_cmd=None, get_cmd=None, val_mapping=create_on_off_val_mapping(), docstring=( "Digitize before each waveform download. " "If you need to acquire from multiple channels simultaneously " "or you wish to acquire with the scope running freely, " "set this value to False."), initial_value=True, ) self.cache_setpoints: Parameter = Parameter( name="cache_setpoints", instrument=self, label="Cache setpoints", set_cmd=None, get_cmd=None, val_mapping=create_on_off_val_mapping(), docstring= ("Cache setpoints. If false, the preamble is queried before each" " acquisition, which may add latency to measurements. If you" " are taking repeated measurements, set this to True and update" " setpoints manually by calling `instr.chX.update_setpoints()`."), initial_value=False, ) # Channels _channels = ChannelList(self, "channels", InfiniiumChannel, snapshotable=False) for i in range(1, self.no_channels + 1): channel = InfiniiumChannel(self, f"chan{i}", i) _channels.append(channel) self.add_submodule(f"ch{i}", channel) self.add_submodule("channels", _channels.to_channel_tuple()) # Functions _functions = ChannelList(self, "functions", InfiniiumFunction, snapshotable=False) for i in range(1, 16 + 1): function = InfiniiumFunction(self, f"func{i}", i) _functions.append(function) self.add_submodule(f"func{i}", function) # Have to call channel list "funcs" here as functions is a # reserved name in Instrument. self.add_submodule("funcs", _functions.to_channel_tuple()) # Submodules meassubsys = UnboundMeasurement(self, "measure") self.add_submodule("measure", meassubsys)
def __init__(self, name: str, address: str, num_channels: int, timeout: float = 10, **kwargs) -> None: """ Args: name: The name used internally by QCoDeS in the DataSet address: The VISA resource name of the instrument timeout: The VISA timeout time (in seconds) num_channels: Number of channels on the AWG """ self.num_channels = num_channels super().__init__(name, address, timeout=timeout, terminator='\n', **kwargs) # The 'model' value begins with 'AWG' self.model = self.IDN()['model'][3:] if self.model not in ['70001A', '70002A', '5208']: raise ValueError('Unknown model type: {}. Are you using ' 'the right driver for your instrument?' ''.format(self.model)) self.add_parameter('current_directory', label='Current file system directory', set_cmd='MMEMory:CDIRectory "{}"', get_cmd='MMEMory:CDIRectory?', vals=vals.Strings()) self.add_parameter('mode', label='Instrument operation mode', set_cmd='INSTrument:MODE {}', get_cmd='INSTrument:MODE?', vals=vals.Enum('AWG', 'FGEN')) ################################################## # Clock parameters self.add_parameter('sample_rate', label='Clock sample rate', set_cmd='CLOCk:SRATe {}', get_cmd='CLOCk:SRATe?', unit='Sa/s', get_parser=float, vals=SRValidator(self)) self.add_parameter('clock_source', label='Clock source', set_cmd='CLOCk:SOURce {}', get_cmd='CLOCk:SOURce?', val_mapping={ 'Internal': 'INT', 'Internal, 10 MHZ ref.': 'EFIX', 'Internal, variable ref.': 'EVAR', 'External': 'EXT' }) self.add_parameter('clock_external_frequency', label='External clock frequency', set_cmd='CLOCk:ECLock:FREQuency {}', get_cmd='CLOCk:ECLock:FREQuency?', get_parser=float, unit='Hz', vals=vals.Numbers(6.25e9, 12.5e9)) # We deem 2 channels too few for a channel list if self.num_channels > 2: chanlist = ChannelList(self, 'Channels', AWGChannel, snapshotable=False) for ch_num in range(1, num_channels + 1): ch_name = 'ch{}'.format(ch_num) channel = AWGChannel(self, ch_name, ch_num) self.add_submodule(ch_name, channel) if self.num_channels > 2: chanlist.append(channel) if self.num_channels > 2: chanlist.lock() self.add_submodule('channels', chanlist) # Folder on the AWG where to files are uplaoded by default self.wfmxFileFolder = "\\Users\\OEM\\Documents" self.seqxFileFolder = "\\Users\\OEM\Documents" self.current_directory(self.wfmxFileFolder) self.connect_message()
def __init__(self, name, address, silent=False, **kwargs): """ Args: name (string): The name of the instrument used internally by QCoDeS. Must be unique. address (string): The VISA resource name. silent (Optional[bool]): If True, no connect message is printed. """ super().__init__(name, address, **kwargs) def errorparser(rawmssg): """ Parses the error message. Args: rawmssg (str): The raw return value of 'SYSTem:ERRor?' Returns: tuple (int, str): The error code and the error message. """ code = int(rawmssg.split(',')[0]) mssg = rawmssg.split(',')[1].strip().replace('"', '') return code, mssg channels = ChannelList(self, "Channels", KeysightChannel, snapshotable=False) for i in range(1, 3): channel = KeysightChannel(self, 'ch{}'.format(i), i) channels.append(channel) channels.lock() self.add_submodule('channels', channels) self.add_parameter('sync_source', label='Source of sync function', set_cmd='OUTPut:SYNC:SOURce {}', get_cmd='OUTPut:SYNC:SOURce?', val_mapping={ 1: 'CH1', 2: 'CH2' }, vals=vals.Enum(1, 2)) self.add_parameter('sync_output', label='Sync output state', set_cmd='OUTPut:SYNC {}', get_cmd='OUTPut:SYNC?', val_mapping={ 'ON': 1, 'OFF': 0 }, vals=vals.Enum('ON', 'OFF')) self.add_parameter('error', label='Error message', get_cmd='SYSTem:ERRor?', get_parser=errorparser) self.add_function('force_trigger', call_cmd='*TRG') self.add_function('sync_channel_phases', call_cmd='PHAS:SYNC') if not silent: self.connect_message()
class PulseBlasterDDS(Instrument): """ This is the qcodes driver for the SpinCore PulseBlasterDDS-II-300 """ # pb_start_programming options TX_PHASE_REGS = 2 RX_PHASE_REGS = 3 # COS_PHASE_REGS = 4 # RadioProcessor boards ONLY # SIN_PHASE_REGS = 5 # RadioProcessor boards ONLY # pb_write_register options BASE_ADDR = 0x40000 FLAG_STATES = BASE_ADDR + 0x8 START_LOCATION = BASE_ADDR + 0x7 # pb_dds_load options DEVICE_DDS = 0x099000 DEVICE_SHAPE = 0x099001 N_CHANNELS = 2 N_FREQ_REGS = 16 N_PHASE_REGS = 8 N_AMPLITUDE_REGS = 4 DEFAULT_DDS_INST = (0, 0, 0, 0, 0) PROGRAM_INSTRUCTIONS_MAP = { 'CONTINUE': 0, #inst_data=Not used 'STOP': 1, #inst_data=Not used 'LOOP': 2, #inst_data=Number of desired loops 'END_LOOP': 3, #inst_data=Address of instruction originating loop 'JSR': 4, #inst_data=Address of first instruction in subroutine 'RTS': 5, #inst_data=Not Used 'BRANCH': 6, #inst_data=Address of instruction to branch to 'LONG_DELAY': 7, #inst_data=Number of desired repetitions 'WAIT': 8 } #inst_data=Not used def __init__(self, name, board_number=0, initialize=True, **kwargs): """ Initialize the pulseblaster DDS Args: name: Name of instrument **kwargs: Additional keyword arguments passed to Instrument """ super().__init__(name, **kwargs) # Create an empty list of lists of instructions [[], [], ...] self.instructions = [[] for _ in range(self.N_CHANNELS)] ######################################################################## ### Parameters ### ######################################################################## self.add_parameter('core_clock', label='Core clock', set_cmd=self.set_core_clock, vals=Numbers(), docstring='The core clock of the PulseBlasterDDS') self.add_parameter('board_number', set_cmd=None, initial_value=board_number) self.output_channels = ChannelList(self, name='output_channels', chan_type=InstrumentChannel) for ch_idx in range(self.N_CHANNELS): ch_name = f'ch{ch_idx+1}' output_channel = InstrumentChannel(self, ch_name) output_channel.idx = ch_idx self.output_channels.append(output_channel) output_channel.add_parameter( 'frequencies', label=f'{ch_name} frequency', unit='Hz', # set_cmd=partial(self.set_frequencies, ch_idx), set_cmd=None, vals=Lists(Numbers())) output_channel.add_parameter( 'phases', label=f'{ch_name} phase', unit='deg', # set_cmd=partial(self.set_phases, ch_idx), set_cmd=None, vals=Lists(Numbers())) output_channel.add_parameter( 'amplitudes', label=f'{ch_name} amplitude', unit='V', # set_cmd=partial(self.set_amplitudes, ch_idx), set_cmd=None, vals=Lists(Numbers())) self.add_parameter('instruction_sequence', set_cmd=None, initial_value=[], vals=Lists(), snapshot_value=False) # Initialize the DDS self.setup(initialize=initialize) ########################################################################### ### DDS Board commands ### ########################################################################### # Just wrapped from spinapi.py # @staticmethod def get_error(): """ Print library error as UTF-8 encoded string. """ return str(api.pb_get_error()) @staticmethod @error_parse def get_version(): """ Return the current version of the spinapi library being used. """ return api.pb_get_version() @staticmethod @error_parse def count_boards(): """ Print the number of boards detected in the system. """ return api.pb_count_boards() @staticmethod @error_parse def initialize(): """Initialize currently selected board.""" return api.pb_init() @staticmethod @error_parse def set_debug(debug): """ Enables logging to a log.txt file in the current directory """ return api.pb_set_debug(debug) @staticmethod @error_parse def select_board(board_number): """ Select a specific board number """ return api.pb_select_board(board_number) @staticmethod @error_parse def set_defaults(): """ Set board defaults. Must be called before using any other board functions.""" return api.pb_set_defaults() def set_core_clock(self, clock): """ Sets the core clock reference value Note: This does not change anything in the device, it just provides the library with the value given """ self.select_board(self.board_number()) api.pb_core_clock(clock) @staticmethod @error_parse def write_register(address, value): """ Write to one of two core registers in the DDS Args: address (int) : the address of the register value (int) : the value to be written to the register """ return api.pb_write_register(address, value) ########################################################################### ### DDS Control commands ### ########################################################################### def setup(self, initialize=False): """ Sets up the DDS Args: initialize (Bool): Initialize the DDS (should only be done once at the start). False by default """ assert self.count_boards() > 0, "PB Error: Can't find board" self.select_board(self.board_number()) if initialize: self.initialize() @error_parse def start(self): self.setup(initialize=False) # Reprogram instruction sequence as it may have been overwritten by stop self.program_instruction_sequence(self.instruction_sequence()) self.reset() # This api function does not seem to do much return api.pb_start() @error_parse def reset(self): self.select_board(self.board_number()) return api.pb_reset() @error_parse def stop(self): self.setup(initialize=False) # Must overwrite the sequence (api.pb_stop does not work) stop_instruction = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'stop', 0, 20000) self.program_instruction_sequence([stop_instruction]) self.reset() # This api function does not seem to do much, might be due to # firmware (see manual) return api.pb_stop() @error_parse def close(self): self.select_board(self.board_number()) return api.pb_close() def add_instruction(self, inst, channel): self.instructions[channel].append(inst) @error_parse def start_programming(self, mode): """ Start a programming sequence Args: mode (int) : one of (PULSE_PROGRAM, FREQ_REGS, etc.) """ self.select_board(self.board_number()) return api.pb_start_programming(mode) @error_parse def instruction_sine_pulse(self, freq0, phase0, amp0, dds_en0, phase_reset0, freq1, phase1, amp1, dds_en1, phase_reset1, flags, inst, inst_data, length): """ During start_programming(PULSE_PROGRAM) add an unshaped sine pulse to program Args: inst (tuple) : a tuple to program the board, must be of form: (int freq0, int phase0, int amp0, int dds_en0, int phase_reset0, int freq1, int phase1, int amp1, int dds_en1, int phase_reset1, int flags, int inst, int inst_data, double length) dds_en should be api.TX_ENABLE or api.TX_DISABLE """ length = round(length * api.s) if isinstance(inst, str): inst = self.PROGRAM_INSTRUCTIONS_MAP[inst.upper()] self.select_board(self.board_number()) return api.pb_inst_dds2(freq0, phase0, amp0, dds_en0, phase_reset0, freq1, phase1, amp1, dds_en1, phase_reset1, flags, inst, inst_data, length) @error_parse def instruction_shaped_sine_pulse(self, *inst): """ During start_programming(PULSE_PROGRAM) add a shaped pulse to program, if desired Args: inst (tuple) : a tuple to program the board in the (FREQ0, PHASE0, ...) """ inst = inst[:-1] + (round(inst[-1] * api.s), ) self.select_board(self.board_number()) return api.pb_inst_dds2_shape(*inst) @error_parse def dds_load(self, data, device): """ Load a 1024 point waveform into one of two devices Args: data (list) : a 1024 point waveform. Each point ranges from -1.0 to 1.0 device (int) : Either DEVICE_DDS (the default shape for all output) or DEVICE_SHAPE (an alternative shape register) """ self.select_board(self.board_number()) return api.pb_dds_load(device) @error_parse def stop_programming(self): """ End a programming sequence """ self.select_board(self.board_number()) return api.pb_stop_programming() @error_parse def select_dds(self, channel): self.select_board(self.board_number()) return api.pb_select_dds(channel) def program_instruction_sequence(self, instruction_sequence): """ An all-in-one method to send a sequence of instructions to the board Args: instruction_sequence (list) : a list of instructions to program the board the instructions should be tuples i.e. (FREQ0, PHASE0, ...) """ self.start_programming(api.PULSE_PROGRAM) for instruction in instruction_sequence: self.instruction_sine_pulse(*instruction) self.stop_programming() @error_parse def set_shape_defaults(self, channel): """ Resets the shape for the specified channel Args: channel (int) : Either DDS0 (0) or DDS1 (1) """ self.select_dds(channel) return api.pb_set_shape_defaults() def load_waveform(self, data, device, channel): """ Loads a waveform onto the DDS Args: data (arr) : an array of 1024 points representing a single period of the waveform. Points range between -1.0 and 1.0 device (int) : Either DEVICE_DDS or DEVICE_SHAPE channel (int) : Either DDS0 (0) or DDS1 (1) """ self.select_dds(channel) return self.dds_load(data, device) @error_parse def set_envelope_freq(self, freq, channel, register): """ Sets the frequency for an envelope register Args: freq (float) : the frequency in Hz for the envelope register register (int) : the register number channel (int) : Either DDS0 (0) or DDS1 (1) """ # Scale the frequency to Hertz, as the underlying api assumes MHz freq *= api.Hz self.select_dds(channel) return api.pb_dds_set_envelope_freq(freq, register) ########################################################################### ### Set/Get commands for parameters ### ########################################################################### def set_frequencies(self, channel, frequencies): """ Sets the DDS frequency for the specified channel and register Args: frequency (list(double)): the frequency in Hz for a channel register channel (int) : Either DDS0 (0) or DDS1 (1) """ # Scale the frequency to Hertz, as the underlying api assumes MHz frequencies = np.array(frequencies) * api.Hz # Update channel frequencies self.select_dds(channel) self.start_programming(api.FREQ_REGS) for frequency in frequencies: error_parse(api.pb_set_freq)(frequency) self.stop_programming() def set_phases(self, channel, phases): """ Sets the DDS phase for the specified channel and register Args: phases (list(double)): List of phases for a channel register channel (int) : Either DDS0 (0) or DDS1 (1) """ self.select_dds(channel) self.start_programming(self.TX_PHASE_REGS) for phase in phases: error_parse(api.pb_set_phase)(phase) self.stop_programming() def set_amplitudes(self, channel, amplitudes): """ Sets the DDS amplitude for the specified channel and register Args: channel (int) : Either DDS0 (0) or DDS1 (1) amplitudes (list(double)): Register amplitudes in Volt for a channel """ self.select_dds(channel) # Amplitudes does not need to start programming for register, amplitude in enumerate(amplitudes): # Looking at the DDS output, an amplitude of 1 apparently # corresponds to 0.6V output. This further decreases if the # frequency is below 1 MHz, but above 1MHz it is fairly constant. amplitude /= 0.6 error_parse(api.pb_set_amp)(amplitude, register)
def __init__(self, name, address, update_currents=False, **kwargs): """ Instantiates the instrument. Args: name (str): The instrument name used by qcodes address (str): The VISA name of the resource update_currents (bool): Whether to query all channels for their current sensor value on startup, which takes about 0.5 sec per channel. Default: False. Returns: QDac object """ super().__init__(name, address, **kwargs) handle = self.visa_handle self._get_status_performed = False # Communication setup + firmware check handle.baud_rate = 480600 handle.parity = visa.constants.Parity(0) handle.data_bits = 8 self.set_terminator('\n') handle.write_termination = '\n' self._write_response = '' firmware_version = self._get_firmware_version() if firmware_version < 1.07: LOG.warning(f"Firmware version: {firmware_version}") raise RuntimeError(''' No QDevil QDAC detected or the firmware version is obsolete. This driver only supports version 1.07 or newer. Please contact [email protected] for a firmware update. ''') # Initialse basic information and internal book keeping self.num_chans = self._get_number_of_channels() num_boards = int(self.num_chans / 8) self._output_n_lines = self.num_chans + 2 self._chan_range = range(1, 1 + self.num_chans) self.channel_validator = vals.Ints(1, self.num_chans) self._reset_bookkeeping() # Add channels (and channel parameters) channels = ChannelList(self, "Channels", QDacChannel, snapshotable=False, multichan_paramclass=QDacMultiChannelParameter) for i in self._chan_range: channel = QDacChannel(self, f'chan{i:02}', i) channels.append(channel) self.add_submodule(f'ch{i:02}', channel) channels.lock() self.add_submodule('channels', channels) # Updatechannel sync port validator according to number of boards self._num_syns = max(num_boards - 1, 1) for chan in self._chan_range: self.channels[chan - 1].sync.vals = vals.Ints(0, self._num_syns) # Add non-channel parameters for board in range(num_boards): for sensor in range(3): label = f'Board {board}, Temperature {sensor}' self.add_parameter(name=f'temp{board}_{sensor}', label=label, unit='C', get_cmd=f'tem {board} {sensor}', get_parser=self._num_verbose) self.add_parameter(name='cal', set_cmd='cal {}', vals=vals.Ints(0, self.num_chans)) self.add_parameter(name='mode_force', label='Mode force', get_cmd=None, set_cmd=None, vals=vals.Bool(), initial_value=False) # Due to a firmware bug in 1.07 voltage ranges are always reported # vebosely. So for future compatibility we set verbose True self.write('ver 1') self._update_voltage_ranges() # The driver require verbose mode off except for the above command self.write('ver 0') self._verbose = False # Just so that the code can check the state self.connect_message() LOG.info('[*] Querying all channels for voltages and currents...') self._update_cache(update_currents=update_currents) self._update_currents = update_currents self._load_state() LOG.info('[+] Done')
def __init__(self,name:str, address:str, num_chans:int=16, init_start:bool=False, synchronous_enable:bool=True, synchronous_delay:float=1, synchronous_threshold:float=1e-5, **kwargs: Any) -> None: """ Instantiate the instrument. Args: name: The instrument name used by qcodes address: The VISA name of the resource num_chans: Number of channels to assign. Default: 16 init_start: If true set all channels to 0V, 1.2V range and switch then on. synchronous_enable: If true, block the communication until the set voltage is reached. The communication is block through a simple while loop with a waiting time "synchronous_delay" at each iteration until the set voltage and the measured voltage difference is below "synchronous_threshold". synchronous_delay: Time between to voltage measurement in second. synchronous_threshold: Threshold to unblock communication in volt. Returns: ITest object """ super().__init__(name, address=address, terminator='\n', device_clear=False, **kwargs) self.idn = self.get_idn() self.num_chans = num_chans self.chan_range = range(1,self.num_chans+1) # Create the channels channels = ChannelList(parent=self, name='Channels', chan_type=iTestChannel, multichan_paramclass=iTestMultiChannelParameter) for i in self.chan_range: channel = iTestChannel(self, name='chan{:02}'.format(i), chan_num=i) channel.synchronous_enable(synchronous_enable) channel.synchronous_delay(synchronous_delay) channel.synchronous_threshold(synchronous_threshold) channels.append(channel) self.add_submodule('ch{:02}'.format(i),channel) channels.lock() self.add_submodule('channels',channels) if init_start: for channel in self.channels: channel.v.set(0) channel.v_range(1.2) channel.start() self.connect_message()
def __init__(self, name, address, timeout=2, **kwargs): """ Initialises the Rigol DS1000 Args: name (str) of the instrument address (string) Visa address for the instrument timeout (float) visa timeout """ super().__init__(name, address, device_clear=False, timeout=timeout, **kwargs) self.connect_message() # functions self.add_function('run', call_cmd=':RUN', docstring='Start acquisition') self.add_function('stop', call_cmd=':STOP', docstring='Stop acquisition') self.add_function('single', call_cmd=':SINGle', docstring='Single trace acquisition') self.add_function('force_trigger', call_cmd='TFORce', docstring='Force trigger event') #acquire mode parameters self.add_parameter("acquire_type", label='type of aquire being used by oscilloscope', get_cmd=':ACQ:TYPE?', set_cmd='ACQ:TYPE {}', vals=vals.Enum('NORMAL', 'AVERAGE', 'PEAKDETECT')) self.add_parameter("acquire_mode", label="mode of aquisition being used by oscillioscope", get_cmd=':ACQ:MODE?', set_cmd=':ACQ:MODE {}', vals = vals.Enum('RTIM', 'ETIM') ) self.add_parameter("acquire_averages", label="the average number in averages mode", get_cmd=':ACQ:AVER?', set_cmd=':ACQ:AVER {}', vals=vals.Enum('2', '4', '8', '16', '32', '64', '128', '256')) #note to self: aquire sampling rate parameters to go under channel class self.add_parameter("acquire_mem_depth", label='depth of memory being used by oscilloscope', get_cmd=':ACQ:MEMDepth?', set_cmd=':ACQ:MEMDepth {}', vals = vals.Enum('LONG', 'NORM')) #display parameters self.add_parameter("display_type", label='display type between samples, either vector or dot', get_cmd=':DISP:TYPE?', set_cmd=':DISP:TYPE {}', vals = vals.Enum('VECT', 'DOTS')) self.add_parameter("display_grid", label='controls/queries if the oscilloscope display has a grid', get_cmd=':DISP:GRID?', set_cmd=':DISP:GRID {}', vals = vals.Enum('FULL', 'HALF', 'NONE')) self.add_parameter("display_persist", label="controls/queries if the waveform record point persists or refreshes", get_cmd=':DISP:PERS?', set_cmd=':DiSP:PERS {}', vals = vals.Enum('ON', 'OFF')) self.add_parameter("display_mnud", label="timing for menus hiding automatically", get_cmd=':DISP:MNUD?', set_cmd=':DISP:MNUD {}', vals = vals.Enum('1', '2', '5', '10', '20', 'Infinite') ) self.add_parameter("display_mnus", label="status of the menus", get_cmd=':DISP:MNUS?', set_cmd=':DISP:MNUS {}', vals = vals.Enum('ON', 'OFF')) #note to self - add display clear parameter self.add_parameter("display_brightness", label="set and get the brightness of the grid for the oscilloscope", get_cmd=':DISP:BRIG?', set_cmd=':DISP:BRIG {}', get_parser=int, vals=vals.Ints(0,32)) self.add_parameter("display_intensity", label="set the brightness of the waveform", get_cmd=':DISP:INT?', set_cmd=':DISP:INT {}', get_parser=int, vals=vals.Ints(0,32)) # Channel parameters channels = ChannelList(self, "Channels", RigolDS1000Channel, snapshotable=False) for channel_number in [1, 2]: channel = RigolDS1000Channel(self, "ch{}".format(channel_number), channel_number) channels.append(channel) for channel_number in ['MATH', 'FFT']: channel = RigolDS1000Channel(self, "ch_{}".format(channel_number), channel_number) channels.append(channel) channels.lock() self.add_submodule('channels', channels) #timebase parameters self.add_parameter("time_base_mode", label="the scan mode of the horizontal time base", get_cmd=":TIM:MODE?", set_cmd=":TIM:MODE {}", vals = vals.Enum('MAIN', 'DEL')) #note to self, decide how to deal with timebase offset parameter. self.add_parameter("time_base_offset", label="controls the main and delayed mode offset", get_cmd=":TIM:OFFS?", set_cmd=":TIM:OFFS {}", get_parser=float, unit="s") self.add_parameter("time_base_scale", label="the scale of the horizontal time base", get_cmd=":TIM:SCAL?", set_cmd=":TIM:SCAL {}", get_parser=float, unit="s/div") self.add_parameter("time_base_format", label="the format of the time base", get_cmd=":TIM:FORM?", set_cmd=":TIM:FORM {}", vals=vals.Enum('X-Y', 'Y-T', 'SCANNING') ) #trigger commands self.add_parameter("trigger_mode", label="sets and queries the trigger mode", get_cmd=":TRIG:MODE?", set_cmd=":TRIG:MODE {}", vals = vals.Enum('EDGE', 'PULSE', 'VIDEO', 'SLOPE', 'PATTERN','DURATION', 'ALTERNATION') ) self.add_parameter("trigger_source", label="sets and queries the trigger source", get_cmd = ":TRIG:{}:SOUR?".format(self.trigger_mode()), set_cmd = ":TRIG:{}:SOUR {}".format(self.trigger_mode(), "{}") #get_cmd = self.source_get_cmd, #set_cmd = self.source_set_cmd, ) self.add_parameter("trigger_level", label="sets and queries the trigger level", get_cmd=":TRIG:{}:LEV?".format(self.trigger_mode()), set_cmd=":TRIG:{}:LEV {}".format(self.trigger_mode(), "{}"), #get_cmd = self.level_get_cmd, get_parser = float, #set_cmd = self.level_set_cmd, unit = "V" #vals = vals.permissiveInts(-6*#vertical scale, 6*#vertical scale()) ) self.add_parameter("trigger_sweep", label="sets and queries the trigger sweep", get_cmd =":TRIG:{}:SWE?".format(self.trigger_mode()), set_cmd =":TRIG:{}:SWE {}".format(self.trigger_mode(), "{}"), #get_cmd = self.sweep_get_cmd, #set_cmd = self.sweep_set_cmd, vals = vals.Enum("AUTO","NORM","SING") ) self.add_parameter("trigger_coupling", label="sets and queries the coupling", get_cmd = ":TRIG:{}:COUP?".format(self.trigger_mode()), set_cmd = ":TRIG:{}:COUP {}".format(self.trigger_mode(), "{}"), vals = vals.Enum("DC","AC","HF","LF") ) self.add_parameter("trigger_holdoff", label="sets and queries the trigger holdoff", get_cmd = ":TRIG:HOLD?", set_cmd = ":TRIG:HOLD {}", get_parser = float, unit = 's' ) # Waveform self.add_parameter("waveform_points_mode", label="Number of the waveform points", get_cmd=":WAVEFORM:POINTS:MODE?", set_cmd=":WAVEFORM:POINTS:MODE {}", vals = vals.Enum("NORM","MAX","RAW"), get_parser=str, )