def __init__(self, name, address, reset=False, numdacs=16, dac_step=10, dac_delay=.1, dac_max_delay=0.2, safe_version=True, polarity=['BIP', 'BIP', 'BIP', 'BIP'], use_locks=False, **kwargs): ''' Initialzes the IVVI, and communicates with the wrapper Args: name (string) : name of the instrument address (string) : ASRL address reset (bool) : resets to default values, default=false numdacs (int) : number of dacs, multiple of 4, default=16 polarity (string[4]) : list of polarities of each set of 4 dacs choose from 'BIP', 'POS', 'NEG', default=['BIP', 'BIP', 'BIP', 'BIP'] dac_step (float) : max step size for dac parameter dac_delay (float) : delay (in seconds) for dac dac_max_delay (float) : maximum delay before emitting a warning safe_version (bool) : if True then do not send version commands to the IVVI controller use_locks (bool) : if True then locks are used in the `ask` function of the driver. The IVVI driver is not thread safe, this locking mechanism makes it thread safe at the cost of making the call to ask blocking. ''' t0 = time.time() super().__init__(name, address, **kwargs) if use_locks: self.lock = threading.Lock() else: self.lock = None self.safe_version = safe_version if numdacs % 4 == 0 and numdacs > 0: self._numdacs = int(numdacs) else: raise ValueError('numdacs must be a positive multiple of 4, ' 'not {}'.format(numdacs)) # values based on descriptor self.visa_handle.baud_rate = 115200 self.visa_handle.parity = visa.constants.Parity(1) # odd parity self.visa_handle.write_termination = '' self.visa_handle.read_termination = '' self.add_parameter('version', get_cmd=self._get_version) self.add_parameter('check_setpoints', parameter_class=ManualParameter, initial_value=False, label='Check setpoints', vals=Bool(), docstring=('Whether to check if the setpoint is the' ' same as the current DAC value to ' 'prevent an unnecessary set command.')) # Time to wait before sending a set DAC command to the IVVI self.add_parameter('dac_set_sleep', parameter_class=ManualParameter, initial_value=0.05, label='DAC set sleep', unit='s', vals=Numbers(0), docstring=('When check_setpoints is set to True, ' 'this is the waiting time between the' 'command that checks the current DAC ' 'values and the final set DAC command')) # Minimum time to wait before the read buffer contains data self.add_parameter('dac_read_buffer_sleep', parameter_class=ManualParameter, initial_value=0.025, label='DAC read buffer sleep', unit='s', vals=Numbers(0), docstring=('While receiving bytes from the IVVI, ' 'sleeping is done in multiples of this ' 'value. Change to a lower value for ' 'a shorter minimum time to wait.')) self.add_parameter('dac voltages', label='Dac voltages', get_cmd=self._get_dacs) self.add_function('trigger', call_cmd=self._send_trigger) # initialize pol_num, the voltage offset due to the polarity self.pol_num = np.zeros(self._numdacs) for i in range(int(self._numdacs / 4)): self.set_pol_dacrack(polarity[i], np.arange(1 + i * 4, 1 + (i + 1) * 4), get_all=False) for i in range(1, numdacs + 1): self.add_parameter('dac{}'.format(i), label='Dac {}'.format(i), unit='mV', get_cmd=self._gen_ch_get_func(self._get_dac, i), set_cmd=self._gen_ch_set_func(self._set_dac, i), vals=vals.Numbers( self.pol_num[i - 1], self.pol_num[i - 1] + self.Fullrange), step=dac_step, delay=dac_delay, max_delay=dac_max_delay, max_val_age=10) self._update_time = 5 # seconds self._time_last_update = 0 # ensures first call will always update t1 = time.time() # make sure we ignore termination characters # See http://www.ni.com/tutorial/4256/en/#toc2 on Termination Character # Enabled v = self.visa_handle v.set_visa_attribute(visa.constants.VI_ATTR_TERMCHAR_EN, 0) v.set_visa_attribute(visa.constants.VI_ATTR_ASRL_END_IN, 0) v.set_visa_attribute(visa.constants.VI_ATTR_ASRL_END_OUT, 0) v.set_visa_attribute(visa.constants.VI_ATTR_SEND_END_EN, 0) # basic test to confirm we are properly connected try: self.get_all() except Exception as ex: print('IVVI: get_all() failed, maybe connected to wrong port?') print(traceback.format_exc()) print('Initialized IVVI-rack in %.2fs' % (t1 - t0))
def __init__(self, name: str, address: str, terminator: str = '\n', **kwargs: Any): """ Create an instance of the instrument. Args: name: Name of the instrument instance address: Visa-resolvable instrument address. """ super().__init__(name, address, terminator=terminator, **kwargs) idn = self.IDN.get() self.has_firmware_a_02_10_or_above = version.parse( convert_legacy_version_to_supported_version( idn["firmware"])) >= version.parse( convert_legacy_version_to_supported_version("A.02.10")) self.has_option_001 = '001' in self._options() self._dc_bias_v_level_range: Union[Numbers, Enum] if self.has_option_001: self._v_level_range = Numbers(0, 20) self._i_level_range = Numbers(0, 0.1) self._imp_range = Enum(0.1, 1, 10, 100, 300, 1000, 3000, 10000, 30000, 100000) self._dc_bias_v_level_range = Numbers(-40, 40) else: self._v_level_range = Numbers(0, 2) self._i_level_range = Numbers(0, 0.02) self._imp_range = Enum(1, 10, 100, 300, 1000, 3000, 10000, 30000, 100000) self._dc_bias_v_level_range = Enum(0, 1.5, 2) self._measurement_pair = MeasurementPair( "CPD", ("capacitance", "dissipation_factor"), ("F", "")) self.add_parameter( "frequency", get_cmd=":FREQuency?", set_cmd=":FREQuency {}", get_parser=float, unit="Hz", vals=Numbers(20, 2E6), docstring="Gets and sets the frequency for normal measurement.") self.add_parameter( "current_level", get_cmd=self._get_current_level, set_cmd=self._set_current_level, unit="A", vals=self._i_level_range, docstring="Gets and sets the current level for measurement signal." ) self.add_parameter( "voltage_level", get_cmd=self._get_voltage_level, set_cmd=self._set_voltage_level, unit="V", vals=self._v_level_range, docstring="Gets and sets the AC bias voltage level for measurement " "signal.") self.add_parameter("measurement_function", get_cmd=":FUNCtion:IMPedance?", set_cmd=self._set_measurement) self.add_parameter( "range", get_cmd=":FUNCtion:IMPedance:RANGe?", set_cmd=self._set_range, unit='Ohm', vals=self._imp_range, docstring="Selects the impedance measurement range, also turns " "the auto range function OFF.") self.add_parameter( "imp_autorange_enabled", get_cmd=":FUNCtion:IMPedance:RANGe:AUTO?", set_cmd=":FUNCtion:IMPedance:RANGe:AUTO {}", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="Enables the auto-range for impedance measurement.") self.add_parameter( "dc_bias_enabled", get_cmd=":BIAS:STATe?", set_cmd=":BIAS:STATe {}", vals=Bool(), val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="Enables DC bias. DC bias is automatically turned " "off after recalling the state from memory.") self.add_parameter( "dc_bias_voltage_level", get_cmd=":BIAS:VOLTage:LEVel?", set_cmd=":BIAS:VOLTage:LEVel {}", get_parser=float, unit="V", vals=self._dc_bias_v_level_range, docstring="Sets the DC bias voltage. Setting does not " "implicitly turn the DC bias ON.") self.add_parameter("meas_time_mode", val_mapping={ "short": "SHOR", "medium": "MED", "long": "LONG" }, parameter_class=GroupParameter) self.add_parameter("averaging_rate", vals=Ints(1, 256), parameter_class=GroupParameter, get_parser=int, docstring="Averaging rate for the measurement.") self._aperture_group = Group( [self.meas_time_mode, self.averaging_rate], set_cmd=":APERture {meas_time_mode},{averaging_rate}", get_cmd=":APERture?") if self.has_firmware_a_02_10_or_above: self.add_parameter( "dc_bias_autorange_enabled", get_cmd=":BIAS:RANGe:AUTO?", set_cmd=":BIAS:RANGe:AUTO {}", vals=Bool(), val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="Enables DC Bias range AUTO setting. When DC bias " "range is fixed (not AUTO), '#' is displayed in " "the BIAS field of the display.") self.add_parameter( "signal_mode", initial_value=None, vals=Enum("Voltage", "Current", None), parameter_class=ManualParameter, docstring="This parameter tracks the signal mode which is being " "set.") self.add_submodule("_correction", Correction4980A(self, "correction")) self._set_signal_mode_on_driver_initialization() self.connect_message()
def test_valid_bool_values(): val = Bool() for vval in val.valid_values: val.validate(vval)
def __init__(self, name: str, address: str, terminator: str = "\n", **kwargs: Any) -> None: super().__init__(name, address, terminator=terminator, **kwargs) self.add_parameter('output', label='Output State', get_cmd=self.state, set_cmd=lambda x: self.on() if x else self.off(), val_mapping={ 'off': 0, 'on': 1, }) self.add_parameter('source_mode', label='Source Mode', get_cmd=':SOUR:FUNC?', set_cmd=self._set_source_mode, vals=Enum('VOLT', 'CURR')) # We need to get the source_mode value here as we cannot rely on the # default value that may have been changed before we connect to the # instrument (in a previous session or via the frontpanel). self.source_mode() self.add_parameter('voltage_range', label='Voltage Source Range', unit='V', get_cmd=partial(self._get_range, "VOLT"), set_cmd=partial(self._set_range, "VOLT"), vals=Enum(10e-3, 100e-3, 1e0, 10e0, 30e0), snapshot_exclude=self.source_mode() == 'CURR') self.add_parameter('current_range', label='Current Source Range', unit='I', get_cmd=partial(self._get_range, "CURR"), set_cmd=partial(self._set_range, "CURR"), vals=Enum(1e-3, 10e-3, 100e-3, 200e-3), snapshot_exclude=self.source_mode() == "VOLT") self.add_parameter('range', parameter_class=DelegateParameter, source=None) # The instrument does not support auto range. The parameter # auto_range is introduced to add this capability with # setting the initial state at False mode. self.add_parameter('auto_range', label='Auto Range', set_cmd=self._set_auto_range, get_cmd=None, initial_cache_value=False, vals=Bool()) self.add_parameter('voltage', label='Voltage', unit='V', set_cmd=partial(self._get_set_output, "VOLT"), get_cmd=partial(self._get_set_output, "VOLT"), snapshot_exclude=self.source_mode() == "CURR") self.add_parameter('current', label='Current', unit='I', set_cmd=partial(self._get_set_output, "CURR"), get_cmd=partial(self._get_set_output, "CURR"), snapshot_exclude=self.source_mode() == 'VOLT') self.add_parameter('output_level', parameter_class=DelegateParameter, source=None) # We need to pass the source parameter for delegate parameters # (range and output_level) here according to the present # source_mode. if self.source_mode() == 'VOLT': self.range.source = self.voltage_range self.output_level.source = self.voltage else: self.range.source = self.current_range self.output_level.source = self.current self.add_parameter('voltage_limit', label='Voltage Protection Limit', unit='V', vals=Ints(1, 30), get_cmd=":SOUR:PROT:VOLT?", set_cmd=":SOUR:PROT:VOLT {}", get_parser=float_round, set_parser=int) self.add_parameter('current_limit', label='Current Protection Limit', unit='I', vals=Numbers(1e-3, 200e-3), get_cmd=":SOUR:PROT:CURR?", set_cmd=":SOUR:PROT:CURR {:.3f}", get_parser=float, set_parser=float) self.add_parameter('four_wire', label='Four Wire Sensing', get_cmd=':SENS:REM?', set_cmd=':SENS:REM {}', val_mapping={ 'off': 0, 'on': 1, }) # Note: The guard feature can be used to remove common mode noise. # Read the manual to see if you would like to use it self.add_parameter('guard', label='Guard Terminal', get_cmd=':SENS:GUAR?', set_cmd=':SENS:GUAR {}', val_mapping={ 'off': 0, 'on': 1 }) # Return measured line frequency self.add_parameter("line_freq", label='Line Frequency', unit="Hz", get_cmd="SYST:LFR?", get_parser=int) # Check if monitor is present, and if so enable measurement monitor_present = '/MON' in self.ask("*OPT?") measure = GS200_Monitor(self, 'measure', monitor_present) self.add_submodule('measure', measure) # Reset function self.add_function('reset', call_cmd='*RST') self.add_submodule('program', GS200Program(self, 'program')) self.add_parameter("BNC_out", label="BNC trigger out", get_cmd=":ROUT:BNCO?", set_cmd=":ROUT:BNCO {}", vals=Enum("trigger", "output", "ready"), docstring="Sets or queries the output BNC signal") self.add_parameter("BNC_in", label="BNC trigger in", get_cmd=":ROUT:BNCI?", set_cmd=":ROUT:BNCI {}", vals=Enum("trigger", "output"), docstring="Sets or queries the input BNC signal") self.add_parameter( "system_errors", get_cmd=":SYSTem:ERRor?", docstring="returns the oldest unread error message from the event " "log and removes it from the log.") self.connect_message()
def __init__(self, name: str, address: str, **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) min_freq = 300e3 max_freq = 20e9 min_power = -85 max_power = 10 nports = 2 # # #Ports # ports = ChannelList(self, "PNAPorts", PNAPort) # self.add_submodule("ports", ports) # for port_num in range(1, nports+1): # port = PNAPort(self, f"port{port_num}", port_num, # min_power, max_power) # ports.append(port) ## self.add_submodule(f"port{port_num}", port) # ports.lock() # Drive power self.add_parameter('power', label='Power', get_cmd='SOUR:POW?', get_parser=float, set_cmd='SOUR:POW {:.2f}', unit='dBm', vals=Numbers(min_value=min_power, max_value=max_power)) # IF bandwidth self.add_parameter( 'if_bandwidth', label='IF Bandwidth', get_cmd='SENS:BAND?', get_parser=float, set_cmd='SENS:BAND {:.2f}', unit='Hz', #vals=Numbers(min_value=10, max_value=15e6)) vals=Enum(*np.append( [10**6, 15 * 10**5], np.kron([10, 15, 20, 30, 50, 70], 10**np.arange(5))))) # Number of averages (also resets averages) self.add_parameter('averages_enabled', label='Averages Enabled', get_cmd="SENS:AVER?", set_cmd="SENS:AVER {}", val_mapping={ True: '1', False: '0' }) self.add_parameter('averages', label='Averages', get_cmd='SENS:AVER:COUN?', get_parser=int, set_cmd='SENS:AVER:COUN {:d}', unit='', vals=Numbers(min_value=1, max_value=999)) self.add_parameter('average_trigger', label='Average Trigger', get_cmd=':TRIG:AVER?', set_cmd=':TRIG:AVER {}', vals=Enum('on', 'On', 'ON', 'off', 'Off', 'OFF')) # Setting frequency range self.add_parameter('start', label='Start Frequency', get_cmd='SENS:FREQ:STAR?', get_parser=float, set_cmd='SENS:FREQ:STAR {}', unit='Hz', vals=Numbers(min_value=min_freq, max_value=max_freq)) self.add_parameter('stop', label='Stop Frequency', get_cmd='SENS:FREQ:STOP?', get_parser=float, set_cmd='SENS:FREQ:STOP {}', unit='Hz', vals=Numbers(min_value=min_freq, max_value=max_freq)) self.add_parameter('center', label='Center Frequency', get_cmd='SENS:FREQ:CENT?', get_parser=float, set_cmd='SENS:FREQ:CENT {}', unit='Hz', vals=Numbers(min_value=min_freq, max_value=max_freq)) self.add_parameter('span', label='Frequency Span', get_cmd='SENS:FREQ:SPAN?', get_parser=float, set_cmd='SENS:FREQ:SPAN {}', unit='Hz', vals=Numbers(min_value=min_freq, max_value=max_freq)) # Number of points in a sweep self.add_parameter('points', label='Points', get_cmd='SENS:SWE:POIN?', get_parser=int, set_cmd='SENS:SWE:POIN {}', unit='', vals=Numbers(min_value=1, max_value=20001)) # Electrical delay self.add_parameter('electrical_delay', label='Electrical Delay', get_cmd='CALC:CORR:EDEL:TIME?', get_parser=float, set_cmd='CALC:CORR:EDEL:TIME {:.6e}', unit='s', vals=Numbers(min_value=0, max_value=100000)) # Sweep Time self.add_parameter('sweep_time', label='Time', get_cmd='SENS:SWE:TIME?', set_cmd='SENS:SWE:TIME {}', get_parser=float, unit='s', vals=Numbers(0, 1e6)) # Trigger Mode self.add_parameter('continuous_mode', label='Continuous Mode', get_cmd=':INIT:CONT?', set_cmd=':INIT:CONT {}', vals=Enum('on', 'On', 'ON', 1, 'off', 'Off', 'OFF', 0)) # Trigger Source self.add_parameter(name='trigger_source', label='Trigger source', get_cmd=":TRIG:SEQ:SOUR?", set_cmd=':TRIG:SEQ:SOUR {}', get_parser=str, vals=Enum('bus', 'BUS', 'Bus', 'EXT', 'external', 'EXTERNAL', 'External', 'INT', 'internal', 'INTERNAL', 'Internal', 'MAN', 'manual', 'MANUAL', 'Manual')) # Traces self.add_parameter(name='num_traces', label='Number of Traces', get_cmd='CALC:PAR:COUN?', set_cmd='CALC:PAR:Coun {}', get_parser=int, vals=Numbers(min_value=1, max_value=4)) self.add_parameter('active_trace', label='Active Trace', set_cmd="CALC:PAR{}:SEL", vals=Numbers(min_value=1, max_value=4)) #Init the names of the traces on the VNA for n in range(self.num_traces()): self.write("CALC:PAR{}:TNAME:DATA TR{}".format(n + 1, n + 1)) # Initialize the trigger source to "BUS" self.trigger_source('BUS') # Initialize sweep time to auto self.sweep_time(0) # Note: Traces will be accessed through the traces property which # updates the channellist to include only active trace numbers self._traces = ChannelList(self, "PNATraces", PNATrace) self.add_submodule("traces", self._traces) # # Add shortcuts to first trace # trace1 = self.traces[0] # for param in trace1.parameters.values(): # self.parameters[param.name] = param # # And also add a link to run sweep # self.run_sweep = trace1.run_sweep # # Set this trace to be the default (it's possible to end up in a # # situation where no traces are selected, causing parameter snapshots # # to fail) # self.active_trace(trace1.trace_num) # Add markers to instrument self._markers = ChannelList(self, "ENAMarkers", ENAMarker) self.add_submodule("markers", self._markers) # Set auto_sweep parameter # If we want to return multiple traces per setpoint without sweeping # multiple times, we should set this to false self.add_parameter('auto_sweep', label='Auto Sweep', set_cmd=None, get_cmd=None, vals=Bool(), initial_value=True) self.connect_message()
def __init__(self, name: str, address: str, # Set frequency ranges min_freq: Union[int, float], max_freq: Union[int, float], # Set power ranges min_power: Union[int, float], max_power: Union[int, float], nports: int, # Number of ports on the PNA **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) self.min_freq = min_freq self.max_freq = max_freq #Ports ports = ChannelList(self, "PNAPorts", PNAPort) for port_num in range(1, nports+1): port = PNAPort(self, f"port{port_num}", port_num, min_power, max_power) ports.append(port) self.add_submodule(f"port{port_num}", port) ports.lock() self.add_submodule("ports", ports) # Drive power self.add_parameter('power', label='Power', get_cmd='SOUR:POW?', get_parser=float, set_cmd='SOUR:POW {:.2f}', unit='dBm', vals=Numbers(min_value=min_power, max_value=max_power)) # IF bandwidth self.add_parameter('if_bandwidth', label='IF Bandwidth', get_cmd='SENS:BAND?', get_parser=float, set_cmd='SENS:BAND {:.2f}', unit='Hz', vals=Numbers(min_value=1, max_value=15e6)) # Number of averages (also resets averages) self.add_parameter('averages_enabled', label='Averages Enabled', get_cmd="SENS:AVER?", set_cmd="SENS:AVER {}", val_mapping={True: '1', False: '0'}) self.add_parameter('averages', label='Averages', get_cmd='SENS:AVER:COUN?', get_parser=int, set_cmd='SENS:AVER:COUN {:d}', unit='', vals=Numbers(min_value=1, max_value=65536)) # RF OUT -> Turns the VNA ON/OFF self.add_parameter('rf_out', label='RF Out', get_cmd="OUTP:STAT?", set_cmd="OUTP:STAT {}", val_mapping={True: '1', False: '0'}) # Setting frequency range self.add_parameter('start', label='Start Frequency', get_cmd='SENS:FREQ:STAR?', get_parser=float, set_cmd='SENS:FREQ:STAR {}', unit='Hz', vals=Numbers(min_value=min_freq, max_value=max_freq)) self.add_parameter('stop', label='Stop Frequency', get_cmd='SENS:FREQ:STOP?', get_parser=float, set_cmd='SENS:FREQ:STOP {}', unit='Hz', vals=Numbers(min_value=min_freq, max_value=max_freq)) self.add_parameter('center', label='Center Frequency', get_cmd='SENS:FREQ:CENT?', get_parser=float, set_cmd='SENS:FREQ:CENT {}', unit='Hz', vals=Numbers(min_value=min_freq, max_value=max_freq)) self.add_parameter('span', label='Frequency Span', get_cmd='SENS:FREQ:SPAN?', get_parser=float, set_cmd='SENS:FREQ:SPAN {}', unit='Hz', vals=Numbers(min_value=0, max_value=max_freq)) # Number of points in a sweep self.add_parameter('points', label='Points', get_cmd='SENS:SWE:POIN?', get_parser=int, set_cmd='SENS:SWE:POIN {}', unit='', vals=Numbers(min_value=1, max_value=100001)) # Electrical delay self.add_parameter('electrical_delay', label='Electrical Delay', get_cmd='CALC:CORR:EDEL:TIME?', get_parser=float, set_cmd='CALC:CORR:EDEL:TIME {:.6e}', unit='s', vals=Numbers(min_value=0, max_value=100000)) # Sweep Time self.add_parameter('sweep_time', label='Time', get_cmd='SENS:SWE:TIME?', get_parser=float, unit='s', vals=Numbers(0, 1e6)) # Sweep Mode self.add_parameter('sweep_mode', label='Mode', get_cmd='SENS:SWE:MODE?', set_cmd='SENS:SWE:MODE {}', vals=Enum("HOLD", "CONT", "GRO", "SING")) # Group trigger count self.add_parameter('group_trigger_count', get_cmd="SENS:SWE:GRO:COUN?", get_parser=int, set_cmd="SENS:SWE:GRO:COUN {}", vals=Ints(1, 2000000)) # Trigger Source self.add_parameter('trigger_source', get_cmd="TRIG:SOUR?", set_cmd="TRIG:SOUR {}", vals=Enum("EXT", "IMM", "MAN")) # Traces self.add_parameter('active_trace', label='Active Trace', get_cmd="CALC:PAR:MNUM?", get_parser=int, set_cmd="CALC:PAR:MNUM {}", vals=Numbers(min_value=1, max_value=24)) # Note: Traces will be accessed through the traces property which # updates the channellist to include only active trace numbers self._traces = ChannelList(self, "PNATraces", PNATrace) self.add_submodule("traces", self._traces) # Add shortcuts to first trace trace1 = self.traces[0] params = trace1.parameters if not isinstance(params, dict): raise RuntimeError(f"Expected trace.parameters to be a dict got " f"{type(params)}") for param in params.values(): self.parameters[param.name] = param # And also add a link to run sweep self.run_sweep = trace1.run_sweep # Set this trace to be the default (it's possible to end up in a # situation where no traces are selected, causing parameter snapshots # to fail) self.active_trace(trace1.trace_num) # Set auto_sweep parameter # If we want to return multiple traces per setpoint without sweeping # multiple times, we should set this to false self.add_parameter('auto_sweep', label='Auto Sweep', set_cmd=None, get_cmd=None, vals=Bool(), initial_value=True) # A default output format on initialisation self.write('FORM REAL,32') self.write('FORM:BORD NORM') self.connect_message()
def __init__(self, name, address, reset_device=False, **kwargs): """ Driver for the Keithley 6500 multimeter. Based on the Keithley 2000 driver, commands have been adapted for the Keithley 6500. This driver does not contain all commands available, but only the ones most commonly used. Status: beta-version. Args: name (str): The name used internally by QCoDeS in the DataSet. address (str): The VISA device address. reset_device (bool): Reset the device on startup if true. """ super().__init__(name, address, terminator='\n', **kwargs) command_set = self.ask('*LANG?') if command_set != 'SCPI': error_msg = "This driver only compatible with the 'SCPI' command " \ "set, not '{}' set".format(command_set) raise CommandSetError(error_msg) self._trigger_sent = False self._mode_map = { 'ac current': 'CURR:AC', 'dc current': 'CURR:DC', 'ac voltage': 'VOLT:AC', 'dc voltage': 'VOLT:DC', '2w resistance': 'RES', '4w resistance': 'FRES', 'temperature': 'TEMP', 'frequency': 'FREQ' } self.add_parameter('mode', get_cmd='SENS:FUNC?', set_cmd="SENS:FUNC {}", val_mapping=self._mode_map) self.add_parameter('nplc', get_cmd=partial(self._get_mode_param, 'NPLC', float), set_cmd=partial(self._set_mode_param, 'NPLC'), vals=Numbers(min_value=0.01, max_value=10)) # TODO: validator, this one is more difficult since different modes # require different validation ranges. self.add_parameter('range', get_cmd=partial(self._get_mode_param, 'RANG', float), set_cmd=partial(self._set_mode_param, 'RANG'), vals=Numbers()) self.add_parameter('auto_range_enabled', get_cmd=partial(self._get_mode_param, 'RANG:AUTO', _parse_output_bool), set_cmd=partial(self._set_mode_param, 'RANG:AUTO'), vals=Bool()) self.add_parameter('digits', get_cmd='DISP:VOLT:DC:DIG?', get_parser=int, set_cmd='DISP:VOLT:DC:DIG? {}', vals=Ints(min_value=4, max_value=7)) self.add_parameter('averaging_type', get_cmd=partial(self._get_mode_param, 'AVER:TCON', _parse_output_string), set_cmd=partial(self._set_mode_param, 'AVER:TCON'), vals=Enum('moving', 'repeat')) self.add_parameter('averaging_count', get_cmd=partial(self._get_mode_param, 'AVER:COUN', int), set_cmd=partial(self._set_mode_param, 'AVER:COUN'), vals=Ints(min_value=1, max_value=100)) self.add_parameter('averaging_enabled', get_cmd=partial(self._get_mode_param, 'AVER:STAT', _parse_output_bool), set_cmd=partial(self._set_mode_param, 'AVER:STAT'), vals=Bool()) # Global parameters self.add_parameter('display_backlight', docstring='Control the brightness of the display ' 'backligt. Off turns the display off and' 'Blackout also turns off indicators and ' 'key lights on the device.', get_cmd='DISP:LIGH:STAT?', set_cmd='DISP:LIGH:STAT {}', val_mapping={ 'On 100': 'ON100', 'On 75': 'ON75', 'On 50': 'ON50', 'On 25': 'ON25', 'Off': 'OFF', 'Blackout': 'BLACkout' }) self.add_parameter('trigger_count', get_parser=int, get_cmd='ROUT:SCAN:COUN:SCAN?', set_cmd='ROUT:SCAN:COUN:SCAN {}', vals=MultiType( Ints(min_value=1, max_value=9999), Enum('inf', 'default', 'minimum', 'maximum'))) for trigger in range(1, 5): self.add_parameter('trigger%i_delay' % trigger, docstring='Set and read trigger delay for ' 'timer %i.' % trigger, get_parser=float, get_cmd='TRIG:TIM%i:DEL?' % trigger, set_cmd='TRIG:TIM%i:DEL {}' % trigger, unit='s', vals=Numbers(min_value=0, max_value=999999.999)) self.add_parameter('trigger%i_source' % trigger, docstring='Set the trigger source for ' 'timer %i.' % trigger, get_cmd='TRIG:TIM%i:STAR:STIM?' % trigger, set_cmd='TRIG:TIM%i:STAR:STIM {}' % trigger, val_mapping={ 'immediate': 'NONE', 'timer1': 'TIM1', 'timer2': 'TIM2', 'timer3': 'TIM3', 'timer4': 'TIM4', 'notify1': 'NOT1', 'notify2': 'NOT2', 'notify3': 'NOT3', 'front-panel': 'DISP', 'bus': 'COMM', 'external': 'EXT' }) # Control interval between scans; the default value from the instrument is 0, # hence 0 is included in the validator's range of this parameter. self.add_parameter('trigger_timer', get_parser=float, get_cmd='ROUT:SCAN:INT?', set_cmd='ROUT:SCAN:INT {}', unit='s', vals=Numbers(min_value=0, max_value=999999.999)) self.add_parameter('amplitude', get_cmd=self._read_next_value, set_cmd=False, unit='a.u.') if reset_device: self.reset() self.write('FORM:DATA ASCII') self.connect_message()
def test_valid_values(self): val = Bool() for vval in val.valid_values: val.validate(vval)
def __init__(self, name, instrument_x, instrument_y, instrument_z, field_limit: Union[numbers.Real, Iterable[CartesianFieldLimitFunction]], **kwargs): super().__init__(name, **kwargs) if not isinstance(name, str): raise ValueError("Name should be a string") instruments = [instrument_x, instrument_y, instrument_z] if not all( [isinstance(instrument, AMI430) for instrument in instruments]): raise ValueError("Instruments need to be instances " "of the class AMI430") self._instrument_x = instrument_x self._instrument_y = instrument_y self._instrument_z = instrument_z self._field_limit: Union[float, Iterable[CartesianFieldLimitFunction]] if isinstance(field_limit, collections.abc.Iterable): self._field_limit = field_limit elif isinstance(field_limit, numbers.Real): # Convertion to float makes related driver logic simpler self._field_limit = float(field_limit) else: raise ValueError("field limit should either be a number or " "an iterable of callable field limit functions.") self._set_point = FieldVector(x=self._instrument_x.field(), y=self._instrument_y.field(), z=self._instrument_z.field()) # Get-only parameters that return a measured value self.add_parameter('cartesian_measured', get_cmd=partial(self._get_measured, 'x', 'y', 'z'), unit='T') self.add_parameter('x_measured', get_cmd=partial(self._get_measured, 'x'), unit='T') self.add_parameter('y_measured', get_cmd=partial(self._get_measured, 'y'), unit='T') self.add_parameter('z_measured', get_cmd=partial(self._get_measured, 'z'), unit='T') self.add_parameter('spherical_measured', get_cmd=partial(self._get_measured, 'r', 'theta', 'phi'), unit='T') self.add_parameter('phi_measured', get_cmd=partial(self._get_measured, 'phi'), unit='deg') self.add_parameter('theta_measured', get_cmd=partial(self._get_measured, 'theta'), unit='deg') self.add_parameter('field_measured', get_cmd=partial(self._get_measured, 'r'), unit='T') self.add_parameter('cylindrical_measured', get_cmd=partial(self._get_measured, 'rho', 'phi', 'z'), unit='T') self.add_parameter('rho_measured', get_cmd=partial(self._get_measured, 'rho'), unit='T') # Get and set parameters for the set points of the coordinates self.add_parameter('cartesian', get_cmd=partial(self._get_setpoints, ('x', 'y', 'z')), set_cmd=partial(self._set_setpoints, ('x', 'y', 'z')), unit='T', vals=Anything()) self.add_parameter('x', get_cmd=partial(self._get_setpoints, ('x', )), set_cmd=partial(self._set_setpoints, ('x', )), unit='T', vals=Numbers()) self.add_parameter('y', get_cmd=partial(self._get_setpoints, ('y', )), set_cmd=partial(self._set_setpoints, ('y', )), unit='T', vals=Numbers()) self.add_parameter('z', get_cmd=partial(self._get_setpoints, ('z', )), set_cmd=partial(self._set_setpoints, ('z', )), unit='T', vals=Numbers()) self.add_parameter('spherical', get_cmd=partial(self._get_setpoints, ('r', 'theta', 'phi')), set_cmd=partial(self._set_setpoints, ('r', 'theta', 'phi')), unit='tuple?', vals=Anything()) self.add_parameter('phi', get_cmd=partial(self._get_setpoints, ('phi', )), set_cmd=partial(self._set_setpoints, ('phi', )), unit='deg', vals=Numbers()) self.add_parameter('theta', get_cmd=partial(self._get_setpoints, ('theta', )), set_cmd=partial(self._set_setpoints, ('theta', )), unit='deg', vals=Numbers()) self.add_parameter('field', get_cmd=partial(self._get_setpoints, ('r', )), set_cmd=partial(self._set_setpoints, ('r', )), unit='T', vals=Numbers()) self.add_parameter('cylindrical', get_cmd=partial(self._get_setpoints, ('rho', 'phi', 'z')), set_cmd=partial(self._set_setpoints, ('rho', 'phi', 'z')), unit='tuple?', vals=Anything()) self.add_parameter('rho', get_cmd=partial(self._get_setpoints, ('rho', )), set_cmd=partial(self._set_setpoints, ('rho', )), unit='T', vals=Numbers()) self.add_parameter('block_during_ramp', set_cmd=None, initial_value=True, unit='', vals=Bool())
def __init__(self, name, address, timeout=20, **kwargs): """ Initialises the DS4000. Args: name (str): Name of the instrument used by QCoDeS address (string): Instrument address as used by VISA timeout (float): 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() # 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, "ch{}".format(channel_number), channel_number) channels.append(channel) channels.lock() self.add_submodule('channels', channels)
def __init__(self, name, address=None, port=7020, axes=None, **kwargs): super().__init__(name, address=address, port=port, terminator='\n', **kwargs) self.axes = axes self._ATOB = [] self._latest_response = '' # for some reason the first call is always invalid?! # need some kind of init? self.ask('*IDN?') if axes is None: self._determine_magnet_axes() self._determine_current_to_field() self.add_parameter( 'hold_after_set', get_cmd=None, set_cmd=None, vals=Bool(), initial_value=True, docstring= 'Should the driver block while waiting for the Magnet power supply ' 'to go into hold mode.') self.add_parameter('setpoint', names=tuple('B' + ax.lower() + '_setpoint' for ax in self.axes), units=tuple('T' for ax in self.axes), get_cmd=partial(self._get_fld, self.axes, 'FSET'), set_cmd=partial(self._ramp_to_setpoint, self.axes, 'FSET'), parameter_class=MercuryiPSArray) self.add_parameter('rate', names=tuple('rate_B' + ax.lower() for ax in self.axes), units=tuple('T/m' for ax in self.axes), get_cmd=partial(self._get_fld, self.axes, 'RFST'), set_cmd=partial(self._ramp_to_setpoint, self.axes, 'RFST'), parameter_class=MercuryiPSArray) self.add_parameter('fld', names=tuple('B' + ax.lower() for ax in self.axes), units=tuple('T' for ax in self.axes), get_cmd=partial(self._get_fld, self.axes, 'FLD'), set_cmd=partial(self._ramp_to_setpoint, self.axes, 'FSET'), parameter_class=MercuryiPSArray) self.add_parameter('fldC', names=['B' + ax.lower() for ax in self.axes], units=['T' for ax in self.axes], get_cmd=partial(self._get_fld, self.axes, 'CURR'), set_cmd=partial(self._ramp_to_setpoint, self.axes, 'CSET'), parameter_class=MercuryiPSArray) self.add_parameter('rtp', names=['radius', 'theta', 'phi'], units=['|B|', 'rad', 'rad'], get_cmd=partial(self._get_rtp, self.axes, 'FLD'), set_cmd=partial(self._set_rtp, self.axes, 'FSET'), parameter_class=MercuryiPSArray) self.add_parameter('rtpC', names=['radius', 'theta', 'phi'], units=['|B|', 'rad', 'rad'], get_cmd=partial(self._get_rtp, self.axes, 'CURR'), set_cmd=partial(self._set_rtp, self.axes, 'CSET'), parameter_class=MercuryiPSArray) # so we have radius, theta and phi in buffer self.rtp.get() self.add_parameter('radius', get_cmd=self._get_r, set_cmd=self._set_r, unit='|B|') self.add_parameter('theta', get_cmd=self._get_theta, set_cmd=self._set_theta, unit='rad') self.add_parameter('phi', get_cmd=self._get_phi, set_cmd=self._set_phi, unit='rad') for ax in self.axes: self.add_parameter(ax.lower() + '_fld', get_cmd=partial(self._get_fld, ax, 'FLD'), set_cmd=partial(self._ramp_to_setpoint, ax, 'FSET'), label='B' + ax.lower(), unit='T') self.add_parameter(ax.lower() + '_fldC', get_cmd=partial(self._get_fld, ax, 'CURR'), set_cmd=partial(self._ramp_to_setpoint, ax, 'CSET'), label='B' + ax.lower(), unit='T') self.add_parameter(ax.lower() + '_fld_wait', get_cmd=partial(self._get_fld, ax, 'CURR'), set_cmd=partial(self._ramp_to_setpoint_and_wait, ax, 'CSET'), label='B' + ax.lower(), unit='T') self.add_parameter(ax.lower() + '_ACTN', get_cmd=partial( self._get_cmd, 'READ:DEV:GRP' + ax + ':PSU:ACTN?'), set_cmd='SET:DEV:GRP' + ax + ':PSU:ACTN:{}', vals=Enum('HOLD', 'RTOS', 'RTOZ', 'CLMP')) self.add_parameter(ax.lower() + '_setpoint', get_cmd=partial(self._get_fld, ax, 'FSET'), set_cmd=partial(self._set_fld, ax, 'FSET'), unit='T') self.add_parameter(ax.lower() + '_setpointC', get_cmd=partial(self._get_fld, ax, 'CSET'), set_cmd=partial(self._set_fld, ax, 'CSET'), unit='T') self.add_parameter(ax.lower() + '_rate', get_cmd=partial(self._get_fld, ax, 'RFST'), set_cmd=partial(self._set_fld, ax, 'RFST'), unit='T/m') self.add_parameter(ax.lower() + '_rateC', get_cmd=partial(self._get_fld, ax, 'RCST'), set_cmd=partial(self._set_fld, ax, 'RCST'), unit='T/m') self.connect_message()
def test_valid_values(self): val = Bool() val.validate(val.valid_values[0])
def __init__( self, name: str, address: str, min_freq: Union[int, float], max_freq: Union[int, float], # Set frequency ranges min_power: Union[int, float], max_power: Union[int, float], # Set power ranges nports: int, # Number of ports on the PNA **kwargs: Any) -> None: super().__init__(name, address, terminator='\n', **kwargs) #Ports ports = ChannelList(self, "PNAPorts", PNAPort) for port_num in range(1, nports + 1): port = PNAPort(self, f"port{port_num}", port_num, min_power, max_power) ports.append(port) self.add_submodule(f"port{port_num}", port) ports.lock() self.add_submodule("ports", ports) # Drive power self.add_parameter('power', label='Power', get_cmd='SOUR:POW?', get_parser=float, set_cmd='SOUR:POW {:.2f}', unit='dBm', vals=Numbers(min_value=min_power, max_value=max_power)) # IF bandwidth self.add_parameter('if_bandwidth', label='IF Bandwidth', get_cmd='SENS:BAND?', get_parser=float, set_cmd='SENS:BAND {:.2f}', unit='Hz', vals=Numbers(min_value=1, max_value=15e6)) # Number of averages (also resets averages) self.add_parameter('averages_enabled', label='Averages Enabled', get_cmd="SENS:AVER?", set_cmd="SENS:AVER {}", val_mapping={ True: '1', False: '0' }) self.add_parameter('averages', label='Averages', get_cmd='SENS:AVER:COUN?', get_parser=int, set_cmd='SENS:AVER:COUN {:d}', unit='', vals=Numbers(min_value=1, max_value=65536)) # Setting frequency range self.add_parameter('start', label='Start Frequency', get_cmd='SENS:FREQ:STAR?', get_parser=float, set_cmd='SENS:FREQ:STAR {}', unit='', vals=Numbers(min_value=min_freq, max_value=max_freq)) self.add_parameter('stop', label='Stop Frequency', get_cmd='SENS:FREQ:STOP?', get_parser=float, set_cmd='SENS:FREQ:STOP {}', unit='', vals=Numbers(min_value=min_freq, max_value=max_freq)) # Number of points in a sweep self.add_parameter('points', label='Points', get_cmd='SENS:SWE:POIN?', get_parser=int, set_cmd='SENS:SWE:POIN {}', unit='', vals=Numbers(min_value=1, max_value=100001)) # Electrical delay self.add_parameter('electrical_delay', label='Electrical Delay', get_cmd='CALC:CORR:EDEL:TIME?', get_parser=float, set_cmd='CALC:CORR:EDEL:TIME {:.6e}', unit='s', vals=Numbers(min_value=0, max_value=100000)) # Sweep Time self.add_parameter('sweep_time', label='Time', get_cmd='SENS:SWE:TIME?', get_parser=float, unit='s', vals=Numbers(0, 1e6)) # Sweep Mode self.add_parameter('sweep_mode', label='Mode', get_cmd='SENS:SWE:MODE?', set_cmd='SENS:SWE:MODE {}', vals=Enum("HOLD", "CONT", "GRO", "SING")) # Traces self.add_parameter('active_trace', label='Active Trace', get_cmd="CALC:PAR:MNUM?", get_parser=int, set_cmd="CALC:PAR:MNUM {}", vals=Numbers(min_value=1, max_value=24)) # Note: Traces will be accessed through the traces property which updates # the channellist to include only active trace numbers self._traces = ChannelList(self, "PNATraces", PNATrace) self.add_submodule("traces", self._traces) # Add shortcuts to trace 1 trace1 = PNATrace(self, "tr1", 1) for param in trace1.parameters.values(): self.parameters[param.name] = param # Set this trace to be the default (it's possible to end up in a situation where # no traces are selected, causing parameter snapshots to fail) self.active_trace(1) # Set auto_sweep parameter # If we want to return multiple traces per setpoint without sweeping # multiple times, we should set this to false self.add_parameter('auto_sweep', label='Auto Sweep', set_cmd=None, get_cmd=None, vals=Bool(), initial_value=True) # A default output format on initialisation self.write('FORM REAL,32') self.write('FORM:BORD NORM') self.connect_message()
def __init__( self, name: str, instrument_x: Union[AMI430, str], instrument_y: Union[AMI430, str], instrument_z: Union[AMI430, str], field_limit: Union[numbers.Real, Iterable[CartesianFieldLimitFunction]], **kwargs: Any, ): """ Driver for controlling three American Magnetics Model 430 magnet power supplies simultaneously for setting magnetic field vectors. The individual magnet power supplies can be passed in as either instances of AMI430 driver or as names of existing AMI430 instances. In the latter case, the instances will be found via the passed names. Args: name: a name for the instrument instrument_x: AMI430 instance or a names of existing AMI430 instance for controlling the X axis of magnetic field instrument_y: AMI430 instance or a names of existing AMI430 instance for controlling the Y axis of magnetic field instrument_z: AMI430 instance or a names of existing AMI430 instance for controlling the Z axis of magnetic field field_limit: a number for maximum allows magnetic field or an iterable of callable field limit functions that define region(s) of allowed values in 3D magnetic field space """ super().__init__(name, **kwargs) if not isinstance(name, str): raise ValueError("Name should be a string") for instrument, arg_name in zip( (instrument_x, instrument_y, instrument_z), ("instrument_x", "instrument_y", "instrument_z"), ): if not isinstance(instrument, (AMI430, str)): raise ValueError( f"Instruments need to be instances of the class AMI430 " f"or be valid names of already instantiated instances " f"of AMI430 class; {arg_name} argument is " f"neither of those") def find_ami430_with_name(ami430_name: str) -> AMI430: found_ami430 = AMI430.find_instrument(name=ami430_name, instrument_class=AMI430) return found_ami430 self._instrument_x = (instrument_x if isinstance(instrument_x, AMI430) else find_ami430_with_name(instrument_x)) self._instrument_y = (instrument_y if isinstance(instrument_y, AMI430) else find_ami430_with_name(instrument_y)) self._instrument_z = (instrument_z if isinstance(instrument_z, AMI430) else find_ami430_with_name(instrument_z)) self._field_limit: Union[float, Iterable[CartesianFieldLimitFunction]] if isinstance(field_limit, collections.abc.Iterable): self._field_limit = field_limit elif isinstance(field_limit, numbers.Real): # Conversion to float makes related driver logic simpler self._field_limit = float(field_limit) else: raise ValueError("field limit should either be a number or " "an iterable of callable field limit functions.") self._set_point = FieldVector( x=self._instrument_x.field(), y=self._instrument_y.field(), z=self._instrument_z.field(), ) # Get-only parameters that return a measured value self.add_parameter( "cartesian_measured", get_cmd=partial(self._get_measured, "x", "y", "z"), unit="T", ) self.add_parameter("x_measured", get_cmd=partial(self._get_measured, "x"), unit="T") self.add_parameter("y_measured", get_cmd=partial(self._get_measured, "y"), unit="T") self.add_parameter("z_measured", get_cmd=partial(self._get_measured, "z"), unit="T") self.add_parameter( "spherical_measured", get_cmd=partial(self._get_measured, "r", "theta", "phi"), unit="T", ) self.add_parameter("phi_measured", get_cmd=partial(self._get_measured, "phi"), unit="deg") self.add_parameter("theta_measured", get_cmd=partial(self._get_measured, "theta"), unit="deg") self.add_parameter("field_measured", get_cmd=partial(self._get_measured, "r"), unit="T") self.add_parameter( "cylindrical_measured", get_cmd=partial(self._get_measured, "rho", "phi", "z"), unit="T", ) self.add_parameter("rho_measured", get_cmd=partial(self._get_measured, "rho"), unit="T") # Get and set parameters for the set points of the coordinates self.add_parameter( "cartesian", get_cmd=partial(self._get_setpoints, ("x", "y", "z")), set_cmd=partial(self._set_setpoints, ("x", "y", "z")), unit="T", vals=Anything(), ) self.add_parameter( "x", get_cmd=partial(self._get_setpoints, ("x", )), set_cmd=partial(self._set_setpoints, ("x", )), unit="T", vals=Numbers(), ) self.add_parameter( "y", get_cmd=partial(self._get_setpoints, ("y", )), set_cmd=partial(self._set_setpoints, ("y", )), unit="T", vals=Numbers(), ) self.add_parameter( "z", get_cmd=partial(self._get_setpoints, ("z", )), set_cmd=partial(self._set_setpoints, ("z", )), unit="T", vals=Numbers(), ) self.add_parameter( "spherical", get_cmd=partial(self._get_setpoints, ("r", "theta", "phi")), set_cmd=partial(self._set_setpoints, ("r", "theta", "phi")), unit="tuple?", vals=Anything(), ) self.add_parameter( "phi", get_cmd=partial(self._get_setpoints, ("phi", )), set_cmd=partial(self._set_setpoints, ("phi", )), unit="deg", vals=Numbers(), ) self.add_parameter( "theta", get_cmd=partial(self._get_setpoints, ("theta", )), set_cmd=partial(self._set_setpoints, ("theta", )), unit="deg", vals=Numbers(), ) self.add_parameter( "field", get_cmd=partial(self._get_setpoints, ("r", )), set_cmd=partial(self._set_setpoints, ("r", )), unit="T", vals=Numbers(), ) self.add_parameter( "cylindrical", get_cmd=partial(self._get_setpoints, ("rho", "phi", "z")), set_cmd=partial(self._set_setpoints, ("rho", "phi", "z")), unit="tuple?", vals=Anything(), ) self.add_parameter( "rho", get_cmd=partial(self._get_setpoints, ("rho", )), set_cmd=partial(self._set_setpoints, ("rho", )), unit="T", vals=Numbers(), ) self.add_parameter("block_during_ramp", set_cmd=None, initial_value=True, unit="", vals=Bool()) self.ramp_mode = Parameter( name="ramp_mode", instrument=self, get_cmd=None, set_cmd=None, vals=Enum("default", "simultaneous"), initial_value="default", ) self.ramping_state_check_interval = Parameter( name="ramping_state_check_interval", instrument=self, initial_value=0.05, unit="s", vals=Numbers(0, 10), set_cmd=None, get_cmd=None, ) self.vector_ramp_rate = Parameter( name="vector_ramp_rate", instrument=self, unit="T/s", vals=Numbers(min_value=0.0), set_cmd=None, get_cmd=None, set_parser=self._set_vector_ramp_rate_units, docstring="Ramp rate along a line (vector) in 3D space. Only active" " if `ramp_mode='simultaneous'`.", ) """Ramp rate along a line (vector) in 3D field space"""
def __init__(self, name, address, reset=False, **kwargs): super().__init__(name, address, terminator='\n', **kwargs) self._trigger_sent = False # Unfortunately the strings have to contain quotation marks and a # newline character, as this is how the instrument returns it. self._mode_map = { 'ac current': 'CURR:AC', 'dc current': 'CURR:DC', 'ac voltage': 'VOLT:AC', 'dc voltage': 'VOLT:DC', '2w resistance': 'RES', '4w resistance': 'FRES', 'temperature': 'TEMP', 'frequency': 'FREQ', 'none': 'NONE', # instrument is in digitizer mode } self.add_parameter('mode', get_cmd='SENS:FUNC?', set_cmd="SENS:FUNC \"{}\"", val_mapping=self._mode_map) # Mode specific parameters self.add_parameter('nplc', get_cmd=partial(self._get_mode_param, 'NPLC', float), set_cmd=partial(self._set_mode_param, 'NPLC'), vals=Numbers(min_value=0.01, max_value=10)) # TODO: validator, this one is more difficult since different modes # require different validation ranges self.add_parameter('range', get_cmd=partial(self._get_mode_param, 'RANG', float), set_cmd=partial(self._set_mode_param, 'RANG'), vals=Numbers()) self.add_parameter('auto_range_enabled', get_cmd=partial(self._get_mode_param, 'RANG:AUTO', parse_output_bool), set_cmd=partial(self._set_mode_param, 'RANG:AUTO'), vals=Bool()) self.add_parameter('digits', get_cmd=partial(self._get_mode_param, 'DIG', int), set_cmd=partial(self._set_mode_param, 'DIG'), vals=Ints(min_value=4, max_value=7)) self.add_parameter('averaging_type', get_cmd=partial(self._get_mode_param, 'AVER:TCON', parse_output_string), set_cmd=partial(self._set_mode_param, 'AVER:TCON'), vals=Enum('moving', 'repeat')) self.add_parameter('averaging_count', get_cmd=partial(self._get_mode_param, 'AVER:COUN', int), set_cmd=partial(self._set_mode_param, 'AVER:COUN'), vals=Ints(min_value=1, max_value=100)) self.add_parameter('averaging_enabled', get_cmd=partial(self._get_mode_param, 'AVER:STAT', parse_output_bool), set_cmd=partial(self._set_mode_param, 'AVER:STAT'), vals=Bool()) # Global parameters self.add_parameter('display_enabled', get_cmd='DISP:ENAB?', get_parser=parse_output_bool, set_cmd='DISP:ENAB {}', set_parser=int, vals=Bool()) self.add_parameter('trigger_count', get_cmd='TRIG:COUN?', get_parser=int, set_cmd='TRIG:COUN {}', vals=MultiType( Ints(min_value=1, max_value=9999), Enum('inf', 'default', 'minimum', 'maximum'))) self.add_parameter('trigger_delay', get_cmd='TRIG:DEL?', get_parser=float, set_cmd='TRIG:DEL {}', unit='s', vals=Numbers(min_value=0, max_value=999999.999)) self.add_parameter('trigger_source', get_cmd='TRIG:SOUR?', set_cmd='TRIG:SOUR {}', val_mapping={ 'immediate': 'IMM', 'timer': 'TIM', 'manual': 'MAN', 'bus': 'BUS', 'external': 'EXT', }) self.add_parameter('trigger_timer', get_cmd='TRIG:TIM?', get_parser=float, set_cmd='TRIG:TIM {}', unit='s', vals=Numbers(min_value=0.001, max_value=999999.999)) # self.add_parameter('amplitude', # unit='arb.unit', # get_cmd=self._read_next_value) self.add_parameter('data_format', get_cmd=':FORMat:DATA?') # Trace subystem, also called reading buffers self.add_parameter( 'buffer_readings', label='number of readings in buffer', get_cmd=':TRACe:ACTual?', get_parser=int, ) self.add_function('clear_buffer', call_cmd=':TRACe:CLEar') self.add_function('reset', call_cmd='*RST') if reset: self.reset() # Set the data format to have only ascii data without units and channels # self.write('FORM:DATA ASCII') # self.write('FORM:ELEM READ') self.connect_message()
def __init__(self, name: str, instrument_x: Union[AMI430, str], instrument_y: Union[AMI430, str], instrument_z: Union[AMI430, str], field_limit: Union[numbers.Real, Iterable[CartesianFieldLimitFunction]], **kwargs: Any): """ Driver for controlling three American Magnetics Model 430 magnet power supplies simultaneously for setting magnetic field vectors. The individual magnet power supplies can be passed in as either instances of AMI430 driver or as names of existing AMI430 instances. In the latter case, the instances will be found via the passed names. Args: name: a name for the instrument instrument_x: AMI430 instance or a names of existing AMI430 instance for controlling the X axis of magnetic field instrument_y: AMI430 instance or a names of existing AMI430 instance for controlling the Y axis of magnetic field instrument_z: AMI430 instance or a names of existing AMI430 instance for controlling the Z axis of magnetic field field_limit: a number for maximum allows magnetic field or an iterable of callable field limit functions that define region(s) of allowed values in 3D magnetic field space """ super().__init__(name, **kwargs) if not isinstance(name, str): raise ValueError("Name should be a string") for instrument, arg_name in zip( (instrument_x, instrument_y, instrument_z), ("instrument_x", "instrument_y", "instrument_z"), ): if not isinstance(instrument, (AMI430, str)): raise ValueError( f"Instruments need to be instances of the class AMI430 " f"or be valid names of already instantiated instances " f"of AMI430 class; {arg_name} argument is " f"neither of those") def find_ami430_with_name(ami430_name: str) -> AMI430: found_ami430 = AMI430.find_instrument(name=ami430_name, instrument_class=AMI430) return cast(AMI430, found_ami430) self._instrument_x = (instrument_x if isinstance(instrument_x, AMI430) else find_ami430_with_name(instrument_x)) self._instrument_y = (instrument_y if isinstance(instrument_y, AMI430) else find_ami430_with_name(instrument_y)) self._instrument_z = (instrument_z if isinstance(instrument_z, AMI430) else find_ami430_with_name(instrument_z)) self._field_limit: Union[float, Iterable[CartesianFieldLimitFunction]] if isinstance(field_limit, collections.abc.Iterable): self._field_limit = field_limit elif isinstance(field_limit, numbers.Real): # Conversion to float makes related driver logic simpler self._field_limit = float(field_limit) else: raise ValueError("field limit should either be a number or " "an iterable of callable field limit functions.") self._set_point = FieldVector(x=self._instrument_x.field(), y=self._instrument_y.field(), z=self._instrument_z.field()) # Get-only parameters that return a measured value self.add_parameter('cartesian_measured', get_cmd=partial(self._get_measured, 'x', 'y', 'z'), unit='T') self.add_parameter('x_measured', get_cmd=partial(self._get_measured, 'x'), unit='T') self.add_parameter('y_measured', get_cmd=partial(self._get_measured, 'y'), unit='T') self.add_parameter('z_measured', get_cmd=partial(self._get_measured, 'z'), unit='T') self.add_parameter('spherical_measured', get_cmd=partial(self._get_measured, 'r', 'theta', 'phi'), unit='T') self.add_parameter('phi_measured', get_cmd=partial(self._get_measured, 'phi'), unit='deg') self.add_parameter('theta_measured', get_cmd=partial(self._get_measured, 'theta'), unit='deg') self.add_parameter('field_measured', get_cmd=partial(self._get_measured, 'r'), unit='T') self.add_parameter('cylindrical_measured', get_cmd=partial(self._get_measured, 'rho', 'phi', 'z'), unit='T') self.add_parameter('rho_measured', get_cmd=partial(self._get_measured, 'rho'), unit='T') # Get and set parameters for the set points of the coordinates self.add_parameter('cartesian', get_cmd=partial(self._get_setpoints, ('x', 'y', 'z')), set_cmd=partial(self._set_setpoints, ('x', 'y', 'z')), unit='T', vals=Anything()) self.add_parameter('x', get_cmd=partial(self._get_setpoints, ('x', )), set_cmd=partial(self._set_setpoints, ('x', )), unit='T', vals=Numbers()) self.add_parameter('y', get_cmd=partial(self._get_setpoints, ('y', )), set_cmd=partial(self._set_setpoints, ('y', )), unit='T', vals=Numbers()) self.add_parameter('z', get_cmd=partial(self._get_setpoints, ('z', )), set_cmd=partial(self._set_setpoints, ('z', )), unit='T', vals=Numbers()) self.add_parameter('spherical', get_cmd=partial(self._get_setpoints, ('r', 'theta', 'phi')), set_cmd=partial(self._set_setpoints, ('r', 'theta', 'phi')), unit='tuple?', vals=Anything()) self.add_parameter('phi', get_cmd=partial(self._get_setpoints, ('phi', )), set_cmd=partial(self._set_setpoints, ('phi', )), unit='deg', vals=Numbers()) self.add_parameter('theta', get_cmd=partial(self._get_setpoints, ('theta', )), set_cmd=partial(self._set_setpoints, ('theta', )), unit='deg', vals=Numbers()) self.add_parameter('field', get_cmd=partial(self._get_setpoints, ('r', )), set_cmd=partial(self._set_setpoints, ('r', )), unit='T', vals=Numbers()) self.add_parameter('cylindrical', get_cmd=partial(self._get_setpoints, ('rho', 'phi', 'z')), set_cmd=partial(self._set_setpoints, ('rho', 'phi', 'z')), unit='tuple?', vals=Anything()) self.add_parameter('rho', get_cmd=partial(self._get_setpoints, ('rho', )), set_cmd=partial(self._set_setpoints, ('rho', )), unit='T', vals=Numbers()) self.add_parameter('block_during_ramp', set_cmd=None, initial_value=True, unit='', vals=Bool())
def __init__(self, name: str, address: str, terminator: str="\n", **kwargs): super().__init__(name, address, terminator=terminator, *kwargs) self.add_parameter('output', label='Output State', get_cmd=self.state, set_cmd=lambda x: self.on() if x else self.off(), val_mapping={ 'off': 0, 'on': 1, }) self.add_parameter('source_mode', label='Source Mode', get_cmd=':SOUR:FUNC?', set_cmd=self._set_source_mode, vals=Enum('VOLT', 'CURR')) # When getting the mode internally in the driver, look up the mode as recorded by the _cached_mode property, # instead of calling source_mode(). This will prevent frequent VISA calls to the instrument. Calling # _set_source_mode will change the chased value. self._cached_mode = "VOLT" # We want to cache the range value so communication with the instrument only happens when the set the # range. Getting the range always returns the cached value. This value is adjusted when calling # self._set_range self._cached_range_value = None self.add_parameter('voltage_range', label='Voltage Source Range', unit='V', get_cmd=partial(self._get_range, "VOLT"), set_cmd=partial(self._set_range, "VOLT"), vals=Enum(10e-3, 100e-3, 1e0, 10e0, 30e0)) self.add_parameter('current_range', label='Current Source Range', unit='I', get_cmd=partial(self._get_range, "CURR"), set_cmd=partial(self._set_range, "CURR"), vals=Enum(1e-3, 10e-3, 100e-3, 200e-3) ) # This is changed through the source_mode interface self.range = self.voltage_range self._auto_range = False self.add_parameter('auto_range', label='Auto Range', set_cmd=self._set_auto_range, get_cmd=lambda: self._auto_range, vals=Bool()) self.add_parameter('voltage', label='Voltage', unit='V', set_cmd=partial(self._get_set_output, "VOLT"), get_cmd=partial(self._get_set_output, "VOLT") ) self.add_parameter('current', label='Current', unit='I', set_cmd=partial(self._get_set_output, "CURR"), get_cmd=partial(self._get_set_output, "CURR") ) # This is changed through the source_mode interface self.output_level = self.voltage self.add_parameter('voltage_limit', label='Voltage Protection Limit', unit='V', vals=Ints(1, 30), get_cmd=":SOUR:PROT:VOLT?", set_cmd=":SOUR:PROT:VOLT {}", get_parser=float_round, set_parser=int) self.add_parameter('current_limit', label='Current Protection Limit', unit='I', vals=Numbers(1e-3, 200e-3), get_cmd=":SOUR:PROT:CURR?", set_cmd=":SOUR:PROT:CURR {:.3f}", get_parser=float, set_parser=float) self.add_parameter('four_wire', label='Four Wire Sensing', get_cmd=':SENS:REM?', set_cmd=':SENS:REM {}', val_mapping={ 'off': 0, 'on': 1, }) # Note: The guard feature can be used to remove common mode noise. # Read the manual to see if you would like to use it self.add_parameter('guard', label='Guard Terminal', get_cmd=':SENS:GUAR?', set_cmd=':SENS:GUAR {}', val_mapping={ 'off': 0, 'on': 1, }) # Return measured line frequency self.add_parameter("line_freq", label='Line Frequency', unit="Hz", get_cmd="SYST:LFR?", get_parser=int) # Check if monitor is present, and if so enable measurement monitor_present = '/MON' in self.ask("*OPT?") measure = GS200_Monitor(self, 'measure', monitor_present) self.add_submodule('measure', measure) # Reset function self.add_function('reset', call_cmd='*RST') self.connect_message() self.output("off") self.source_mode("VOLT") self.auto_range(False)
def __init__(self, name: str, address: str, terminator="\n", reset: bool = False, **kwargs): super().__init__(name, address, terminator=terminator, **kwargs) self.add_parameter('source_current_compliance', unit='A', get_parser=float, set_cmd='SENS:CURR:PROT {}', get_cmd='SENS:CURR:PROT?', vals=Numbers(1e-9, 105e-3)) self.add_parameter('source_voltage_compliance', unit='V', get_parser=float, set_cmd='SENS:VOLT:PROT {}', get_cmd='SENS:VOLT:PROT?', vals=Numbers(1e-12, 210)) self.add_parameter( 'source_current_compliance_tripped', get_cmd='SENS:CURR:PROT:TRIP?', val_mapping=on_off_vals, docstring='True if current has reached specified ' 'compliance.', ) self.add_parameter( 'source_voltage_compliance_tripped', get_cmd='SENS:VOLT:PROT:TRIP?', val_mapping=on_off_vals, docstring='True if voltage has reached specified ' 'compliance.', ) self.add_parameter( 'source_current', unit='A', get_parser=float, label='Source current', set_cmd='SOUR:CURR:LEV {}', get_cmd='SOUR:CURR:LEV?', vals=Numbers(-105e-3, 105e-3), docstring='When in current sourcing mode, tries to ' 'set current to this level.', ) self.add_parameter( 'sense_current', unit='A', get_parser=float, label='Measured current', get_cmd=partial(self._read_value, 'CURR:DC'), snapshot_get=False, docstring='Value of measured current, when in ' 'current sensing mode.', ) self.add_parameter( 'sense_voltage', unit='V', get_parser=float, label='Measured voltage', get_cmd=partial(self._read_value, 'VOLT:DC'), snapshot_get=False, docstring='Value of measured voltage, when in ' 'voltage sensing mode.', ) self.add_parameter( 'sense_resistance', unit='Ohm', get_parser=float, label='Measured resistance', get_cmd=partial(self._read_value, 'RES'), snapshot_get=False, docstring='Value of measured resistance, when in ' 'resistance sensing mode.', ) self.add_parameter('source_current_range', unit='A', get_parser=float, set_cmd='SOUR:CURR:RANG {}', get_cmd='SOUR:CURR:RANG?', vals=Numbers(1e-12, 105e-3)) self.add_parameter( 'source_voltage', unit='V', get_parser=float, label='Source voltage', set_cmd='SOUR:VOLT:LEV {}', get_cmd='SOUR:VOLT:LEV?', vals=Numbers(-210, 210), docstring='When in voltage sourcing mode, tries to ' 'set voltage to this level.', ) self.add_parameter('source_voltage_range', unit='V', get_parser=float, set_cmd='SOUR:VOLT:RANG {}', get_cmd='SOUR:VOLT:RANG?', vals=Numbers(200e-3, 200)) self.add_parameter( 'source_delay_auto', set_cmd=':SOUR:DEL:AUTO {}', get_cmd=':SOUR:DEL:AUTO?', val_mapping=on_off_vals, docstring="Automatically set a delay period that " "is appropriate for the present " "source/measure setup configuration.", ) self.add_parameter( 'source_delay', unit='s', get_parser=float, set_cmd=':SOUR:DEL {}', get_cmd=':SOUR:DEL?', vals=Numbers(0, 9999.998), docstring="Settling time after setting source " "value.", ) self.add_parameter( 'output_enabled', set_cmd='OUTP {}', get_cmd='OUTP?', val_mapping=on_off_vals, docstring='Turns the source on or off.', ) self.add_parameter( 'output_auto_off_enabled', set_cmd=':SOUR:CLE:AUTO {}', get_cmd='OUTP?', val_mapping=on_off_vals, ) self.add_parameter( 'source_mode', set_cmd='SOUR:FUNC {}', get_cmd=self._get_source_mode, vals=Enum('VOLT', 'CURR'), docstring="Either 'VOLT' to source voltage, " "or 'CURR' for current.", ) self.add_parameter( 'sense_mode', set_cmd=self._set_sense_mode, get_cmd=self._get_sense_mode, vals=Strings(), docstring="Sensing mode." "Set to 'VOLT:DC', " "'CURR:DC', or 'RES', or a combination " "thereof by using comma.", ) self.add_parameter( 'sense_autorange', set_cmd=self._set_sense_autorange, get_cmd=self._get_sense_autorange, vals=Bool(), docstring="If True, all ranges in all modes are" " chosen automatically", ) self.add_parameter( 'sense_current_range', unit='A', get_parser=float, set_cmd=':SENS:CURR:RANG {}', get_cmd=':SENS:CURR:RANG?', vals=Numbers(1e-12, 1e-1), ) self.add_parameter( 'sense_voltage_range', unit='V', get_parser=float, set_cmd=':SENS:VOLT:RANG {}', get_cmd=':SENS:VOLT:RANG?', vals=Enum(200, 20, 2, 0.2), ) self.add_parameter( 'sense_resistance_range', unit='Ohm', get_parser=float, set_cmd=':SENS:RES:RANG {}', get_cmd=':SENS:RES:RANG?', vals=Numbers(2, 2e13), ) self.add_parameter( 'sense_resistance_offset_comp_enabled', set_cmd=':SENS:RES:OCOM {}', get_cmd=':SENS:RES:OCOM?', val_mapping=on_off_vals, docstring="Set offset compensation on/off for " "resistance measurements.", ) self.add_parameter( 'trigger_source', set_cmd=':TRIG:SOUR {}', get_cmd=':TRIG:SOUR?', vals=Enum('IMM', 'TLIN'), docstring="Specify trigger control source." "IMMediate or TLINk.", ) self.add_parameter( 'arm_source', set_cmd=':ARM:SOUR {}', get_cmd=':ARM:SOUR?', vals=Enum('IMM', 'TLIN', "TIM", "MAN", "BUS", "NST", "PST", "BST"), docstring="Specify arm control source." "IMMediate, or TLINk, TIMer, MANual," " BUS, NSTest, PSTest, or BSTest.", ) self.add_parameter( 'trigger_count', set_cmd=':TRIG:COUN {}', get_cmd=':TRIG:COUN?', vals=Ints(), docstring="How many times to trigger.", ) self.add_parameter( 'arm_count', set_cmd=':ARM:COUN {}', get_cmd=':ARM:COUN?', vals=Ints(), docstring="How many times to arm.", ) self.add_parameter( 'nplc', get_parser=float, set_cmd=':NPLC {}', get_cmd=':NPLC?', vals=Numbers(0.01, 10), docstring="Set integration time to the specified" "value in Number of Powerline Cycles.", ) self.add_parameter( 'digits', get_parser=int, set_cmd='DISP:DIG {}', get_cmd='DISP:DIG?', vals=Ints(4, 7), docstring="Display resolution.", ) self.add_parameter( 'autozero', set_cmd='SYST:AZER:STAT {}', get_cmd='SYST:AZER:STAT?', val_mapping=on_off_vals, docstring="Enable autozero." "Enabling maximizes accuracy, " "disabling increases speed.", ) self.add_parameter( 'filter_auto', set_cmd='AVER:AUTO {}', get_cmd='AVER:AUTO?', val_mapping=on_off_vals, docstring="Automatically choose filtering.", ) self.add_parameter( 'filter_repeat_enabled', set_cmd=':AVER:REP:STAT {}', get_cmd='AVER:AUTO?', val_mapping=on_off_vals, docstring="Enable repeat filter.", ) self.add_parameter( 'filter_median_enabled', set_cmd=':MED:STAT {}', get_cmd=':MED:STAT?', val_mapping=on_off_vals, docstring="Enable median filter.", ) self.add_parameter( 'filter_moving_enabled', set_cmd=':AVER:STAT {}', get_cmd=':AVER:STAT?', val_mapping=on_off_vals, docstring="Enable moving average.", ) self.add_parameter( 'filter_repeat', get_parser=int, set_cmd=':AVER:REP:COUN {}', get_cmd=':AVER:REP:COUN?', vals=Ints(), docstring="Number of readings that are acquired" "and stored in the filter buffer.", ) self.add_parameter( 'filter_median', get_parser=int, set_cmd=':MED:RANK {}', get_cmd=':MED:RANK?', vals=Ints(), docstring="Number of reading samples" " for the median filter process.", ) self.add_parameter( 'filter_moving', get_parser=int, set_cmd=':AVER:COUN {}', get_cmd=':AVER:COUN?', vals=Ints(), docstring="Number of reading samples" " in the moving average filter.", ) self.connect_message() if reset: self.reset()