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) 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=":CURRent:LEVel?", set_cmd=":CURRent:LEVel {}", get_parser=float, unit="A", vals=Numbers(0, 0.1), docstring="Gets and sets the current level for measurement signal." ) self.add_parameter( "voltage_level", get_cmd=":VOLTage:LEVel?", set_cmd=":VOLTage:LEVel {}", get_parser=float, unit="V", vals=Numbers(0, 20), docstring="Gets and sets the 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=":FUNCtion:IMPedance:RANGe {}", unit='Ohm', vals=Enum(0.1, 1, 10, 100, 300, 1000, 3000, 10000, 30000, 100000), docstring="Selects the impedance measurement range, also turns " "the auto range function OFF.") self.add_submodule("_correction", Correction4980A(self, "correction")) self.connect_message()
def __init__(self, parent: 'GS200', name: str, present: bool) -> None: super().__init__(parent, name) self.present = present # Start off with all disabled self._enabled = False self._output = False # Set up monitoring parameters if present: self.add_parameter('enabled', label='Measurement Enabled', get_cmd=self.state, set_cmd=lambda x: self.on() if x else self.off(), val_mapping={ 'off': 0, 'on': 1, }) # Note: Measurement will only run if source and measurement is enabled. self.add_parameter('measure', label='<unset>', unit='V/I', get_cmd=self._get_measurement) self.add_parameter('NPLC', label='NPLC', unit='1/LineFreq', vals=Ints(1, 25), set_cmd=':SENS:NPLC {}', set_parser=int, get_cmd=':SENS:NPLC?', get_parser=float_round) self.add_parameter('delay', label='Measurement Delay', unit='ms', vals=Ints(0, 999999), set_cmd=':SENS:DEL {}', set_parser=int, get_cmd=':SENS:DEL?', get_parser=float_round) self.add_parameter('trigger', label='Trigger Source', set_cmd=':SENS:TRIG {}', get_cmd=':SENS:TRIG?', val_mapping={ 'READY': 'READ', 'READ': 'READ', 'TIMER': 'TIM', 'TIM': 'TIM', 'COMMUNICATE': 'COMM', 'IMMEDIATE': 'IMM', 'IMM': 'IMM' }) self.add_parameter('interval', label='Measurement Interal', unit='s', vals=Numbers(0.1, 3600), set_cmd=':SENS:INT {}', set_parser=float, get_cmd=':SENS:INT?', get_parser=float)
def __init__(self, name, alazar_name, **kwargs): self.number_of_channels = 2 self.awgx = None self.awgy = None # make a call to the parent class and by extension, # create the parameter structure of this class super().__init__(name, alazar_name, **kwargs) self.add_parameter(name='acquisition', parameter_class=IQMagPhase) self.add_parameter('x_start', parameter_class=ManualParameter, label='Sweep x start voltage', unit='V', vals=Numbers(-10, 10)) self.add_parameter('x_end', parameter_class=ManualParameter, label='Sweep x end voltage', unit='V', vals=Numbers(-10, 10)) self.add_parameter( 'x_npts', parameter_class=ManualParameter, label='Number of point x (needs to be multiple of 128)', unit=None, vals=Numbers(1, 10000)) self.add_parameter('y_start', parameter_class=ManualParameter, label='Sweep x end voltage', unit='V', vals=Numbers(-10, 10)) self.add_parameter('y_end', parameter_class=ManualParameter, label='Sweep x end voltage', unit='V', vals=Numbers(-10, 10)) self.add_parameter('y_npts', parameter_class=ManualParameter, label='Number of point x', unit=None, vals=Numbers(1, 10000)) self.add_parameter('sample_rate', parameter_class=ManualParameter, initial_value=500000., label='Sample rate', unit=None, vals=Numbers(1, 250e6)) self.add_parameter('input_coupling', parameter_class=ManualParameter, initial_value='DC', label='Coupling', unit=None, vals=Enum('DC', 'AC')) self.add_parameter('input_range', parameter_class=ManualParameter, initial_value=2., label='Range', unit=None, vals=Enum(0.2, 0.4, 0.8, 2., 4., 8, 16)) self.add_parameter('impedance', parameter_class=ManualParameter, initial_value=50, label='Impedance', unit=None, vals=Enum(50, 1000000))
def __init__(self, name, address, **kwargs): super().__init__(name, address, **kwargs) # Reference and phase self.add_parameter('phase', label='Phase', get_cmd='PHAS?', get_parser=float, set_cmd='PHAS {:.2f}', unit='deg', vals=Numbers(min_value=-360, max_value=729.99)) self.add_parameter('reference_source', label='Reference source', get_cmd='FMOD?', set_cmd='FMOD {}', val_mapping={ 'external': 0, 'internal': 1, }) self.add_parameter('frequency', label='Frequency', get_cmd='FREQ?', get_parser=float, set_cmd='FREQ {:.4f}', unit='Hz', vals=Numbers(min_value=1e-3, max_value=102e3)) self.add_parameter('ext_trigger', label='External trigger', get_cmd='RSLP?', set_cmd='RSLP {}', val_mapping={ 'sine': 0, 'TTL rising': 1, 'TTL falling': 2, }) self.add_parameter('harmonic', label='Harmonic', get_cmd='HARM?', get_parser=int, set_cmd='HARM {:d}', vals=Ints(min_value=1, max_value=19999)) self.add_parameter('amplitude', label='Amplitude', get_cmd='SLVL?', get_parser=float, set_cmd='SLVL {:.3f}', unit='V', vals=Numbers(min_value=0.004, max_value=5.000)) # Input and filter self.add_parameter('input_config', label='Input configuration', get_cmd='ISRC?', get_parser=self._get_input_config, set_cmd='ISRC {}', set_parser=self._set_input_config, vals=Enum(*self._INPUT_CONFIG_TO_N.keys())) self.add_parameter('input_shield', label='Input shield', get_cmd='IGND?', set_cmd='IGND {}', val_mapping={ 'float': 0, 'ground': 1, }) self.add_parameter('input_coupling', label='Input coupling', get_cmd='ICPL?', set_cmd='ICPL {}', val_mapping={ 'AC': 0, 'DC': 1, }) self.add_parameter('notch_filter', label='Notch filter', get_cmd='ILIN?', set_cmd='ILIN {}', val_mapping={ 'off': 0, 'line in': 1, '2x line in': 2, 'both': 3, }) # Gain and time constant self.add_parameter(name='sensitivity', label='Sensitivity', get_cmd='SENS?', set_cmd='SENS {:d}', get_parser=self._get_sensitivity, set_parser=self._set_sensitivity) self.add_parameter('reserve', label='Reserve', get_cmd='RMOD?', set_cmd='RMOD {}', val_mapping={ 'high': 0, 'normal': 1, 'low noise': 2, }) self.add_parameter('time_constant', label='Time constant', get_cmd='OFLT?', set_cmd='OFLT {}', unit='s', val_mapping={ 10e-6: 0, 30e-6: 1, 100e-6: 2, 300e-6: 3, 1e-3: 4, 3e-3: 5, 10e-3: 6, 30e-3: 7, 100e-3: 8, 300e-3: 9, 1: 10, 3: 11, 10: 12, 30: 13, 100: 14, 300: 15, 1e3: 16, 3e3: 17, 10e3: 18, 30e3: 19, }) self.add_parameter('filter_slope', label='Filter slope', get_cmd='OFSL?', set_cmd='OFSL {}', unit='dB/oct', val_mapping={ 6: 0, 12: 1, 18: 2, 24: 3, }) self.add_parameter('sync_filter', label='Sync filter', get_cmd='SYNC?', set_cmd='SYNC {}', val_mapping={ 'off': 0, 'on': 1, }) def parse_offset_get(s): parts = s.split(',') return float(parts[0]), int(parts[1]) # TODO: Parameters that can be set with multiple arguments # For the OEXP command for example two arguments are needed self.add_parameter('X_offset', get_cmd='OEXP? 1', get_parser=parse_offset_get) self.add_parameter('Y_offset', get_cmd='OEXP? 2', get_parser=parse_offset_get) self.add_parameter('R_offset', get_cmd='OEXP? 3', get_parser=parse_offset_get) # Aux input/output for i in [1, 2, 3, 4]: self.add_parameter('aux_in{}'.format(i), label='Aux input {}'.format(i), get_cmd='OAUX? {}'.format(i), get_parser=float, unit='V') self.add_parameter('aux_out{}'.format(i), label='Aux output {}'.format(i), get_cmd='AUXV? {}'.format(i), get_parser=float, set_cmd='AUXV {0}, {{}}'.format(i), unit='V') # Setup self.add_parameter('output_interface', label='Output interface', get_cmd='OUTX?', set_cmd='OUTX {}', val_mapping={ 'RS232': '0\n', 'GPIB': '1\n', }) # Auto functions self.add_function('auto_gain', call_cmd='AGAN') self.add_function('auto_reserve', call_cmd='ARSV') self.add_function('auto_phase', call_cmd='APHS') self.add_function('auto_offset', call_cmd='AOFF {0}', args=[Enum(1, 2, 3)]) # Data transfer self.add_parameter('X', get_cmd='OUTP? 1', get_parser=float, unit='V') self.add_parameter('Y', get_cmd='OUTP? 2', get_parser=float, unit='V') self.add_parameter('R', get_cmd='OUTP? 3', get_parser=float, unit='V') self.add_parameter('P', get_cmd='OUTP? 4', get_parser=float, unit='deg') # 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') # Initialize the proper units of the outputs and sensitivities self.input_config() 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=False, **kwargs): super().__init__(name, address, terminator='\n', **kwargs) model = self.get_idn()['model'] models = ['DG4202', 'DG4162', 'DG4102', 'DG4062'] if model in models: i = models.index(model) sine_freq = [200e6, 160e6, 100e6, 60e6][i] square_freq = [60e6, 50e6, 40e6, 25e6][i] ramp_freq = [5e6, 4e6, 3e6, 1e6][i] pulse_freq = [50e6, 40e6, 25e6, 15e6][i] harmonic_freq = [100e6, 80e6, 50e6, 30e6][i] arb_freq = [50e6, 40e6, 25e6, 15e6][i] else: raise KeyError('Model code ' + model + ' is not recognized') on_off_map = {True: 'ON', False: 'OFF'} # Counter self.add_parameter('counter_attenuation', get_cmd='COUN:ATT?', set_cmd='COUN:ATT {}', val_mapping={1: '1X', 10: '10X'}) self.add_function('auto_counter', call_cmd='COUN:AUTO') self.add_parameter('counter_coupling', get_cmd='COUN:COUP?', set_cmd='COUN:COUP {}', vals=Enum('AC', 'DC')) self.add_parameter('counter_gate_time', get_cmd='COUN:GATE?', set_cmd='COUN:GATE {}', units='s', val_mapping={ 'auto': 'AUTO', 0.001: 'USER1', 0.01: 'USER2', 0.1: 'USER3', 1: 'USER4', 10: 'USER5', '>10': 'USER6', }) self.add_parameter('counter_hf_reject_enabled', get_cmd='COUN:HF?', set_cmd='COUN:HF {}', val_mapping=on_off_map) self.add_parameter('counter_impedance', get_cmd='COUN:IMP?', set_cmd='COUN:IMP {}', units='Ohm', val_mapping={50: '50', 1e6: '1M'}) self.add_parameter('counter_trigger_level', get_cmd='COUN:LEVE?', get_parser=float, set_cmd='COUN:LEVE {}', units='V', vals=Numbers(min_value=-2.5, max_value=2.5)) self.add_parameter('counter_enabled', get_cmd='COUN:STAT?', set_cmd='COUN:STAT {}', val_mapping=on_off_map) measure_params = ['frequency', 'period', 'duty_cycle', 'positive_width', 'negative_width'] # TODO: Check units of outputs for i, param in enumerate(measure_params): self.add_parameter('counter_{}'.format(param), get_cmd='COUN:MEAS?', get_parser=partial(parse_single_output, i)) self.add_parameter('counter_trigger_sensitivity', get_cmd='COUN:SENS?', get_parser=float, set_cmd='COUN:SENS {}', units='%', vals=Numbers(min_value=0, max_value=100)) # Output and Source parameters for both channel 1 and 2 for i in [1, 2]: ch = 'ch{}_'.format(i) output = 'OUTP{}:'.format(i) source = 'SOUR{}:'.format(i) self.add_parameter(ch + 'output_impedance', get_cmd=output + 'IMP?', get_parser=parse_string_output, set_cmd=output + 'IMP {}', units='Ohm', vals=MultiType(Numbers(min_value=1, max_value=10e3), Enum('infinity', 'minimum', 'maximum'))) self.add_parameter(ch + 'add_noise_scale', get_cmd=output + 'NOIS:SCAL?', get_parser=float, set_cmd=output + 'NOIS:SCAL', units='%', vals=Numbers(min_value=0, max_value=50)) self.add_parameter(ch + 'add_noise_enabled', get_cmd=output + 'NOIS?', set_cmd=output + 'NOIS {}', val_mapping=on_off_map) self.add_parameter(ch + 'output_polarity', get_cmd=output + 'POL?', set_cmd=output + 'POL {}', val_mapping={'normal': 'NORM', 'inverted': 'INV'}) self.add_parameter(ch + 'output_enabled', get_cmd=output + 'STAT?', set_cmd=output + 'STAT {}', val_mapping=on_off_map) self.add_parameter(ch + 'sync_polarity', get_cmd=output + 'SYNC:POL?', set_cmd=output + 'SYNC:POL {}', val_mapping={'positive': 'POS', 'negative': 'NEG'}) self.add_parameter(ch + 'sync_enabled', get_cmd=output + 'SYNC?', set_cmd=output + 'SYNC {}', val_mapping=on_off_map) # Source Apply # TODO: Various parameters are limited by # impedance/freq/period/amplitude settings, this might be very hard # to implement in here self.add_function(ch + 'custom', call_cmd=source + 'APPL:CUST ' '{:.6e},{:.6e},{:.6e},{:.6e}', args=[Numbers(1e-6, arb_freq), Numbers(), Numbers(), Numbers(0, 360)]) self.add_function(ch + 'harmonic', call_cmd=source + 'APPL:HARM ' '{:.6e},{:.6e},{:.6e},{:.6e}', args=[Numbers(1e-6, harmonic_freq), Numbers(), Numbers(), Numbers(0, 360)]) self.add_function(ch + 'noise', call_cmd=source + 'APPL:NOIS {:.6e},{:.6e}', args=[Numbers(0, 10), Numbers()]) self.add_function(ch + 'pulse', call_cmd=source + 'APPL:PULS ' '{:.6e},{:.6e},{:.6e},{:.6e}', args=[Numbers(1e-6, pulse_freq), Numbers(), Numbers(), Numbers(0)]) self.add_function(ch + 'ramp', call_cmd=source + 'APPL:RAMP ' '{:.6e},{:.6e},{:.6e},{:.6e}', args=[Numbers(1e-6, ramp_freq), Numbers(), Numbers(), Numbers(0, 360)]) self.add_function(ch + 'sinusoid', call_cmd=source + 'APPL:SIN ' '{:.6e},{:.6e},{:.6e},{:.6e}', args=[Numbers(1e-6, sine_freq), Numbers(), Numbers(), Numbers(0, 360)]) self.add_function(ch + 'square', call_cmd=source + 'APPL:SQU ' '{:.6e},{:.6e},{:.6e},{:.6e}', args=[Numbers(1e-6, square_freq), Numbers(), Numbers(), Numbers(0, 360)]) self.add_function(ch + 'user', call_cmd=source + 'APPL:USER ' '{:.6e},{:.6e},{:.6e},{:.6e}', args=[Numbers(1e-6, arb_freq), Numbers(), Numbers(), Numbers(0, 360)]) self.add_parameter(ch + 'configuration', get_cmd=source + 'APPL?', get_parser=parse_multiple_outputs) # Source Burst self.add_parameter(ch + 'burst_mode', get_cmd=source + 'BURS:MODE?', set_cmd=source + 'BURS:MODE {}', val_mapping={'triggered': 'TRIG', 'gated': 'GAT', 'infinity': 'INF'}) self.add_parameter(ch + 'burst_cycles', get_cmd=source + 'BURS:NCYC?', get_parser=float, set_cmd=source + 'BURS:NCYC {}', vals=Ints(1, 1000000)) self.add_parameter(ch + 'burst_period', get_cmd=source + 'BURS:INT:PER?', get_parser=float, set_cmd=source + 'BURS:INT:PER {}', units='s', vals=Numbers(1e-6)) self.add_parameter(ch + 'burst_phase', get_cmd=source + 'BURS:PHAS?', get_parser=float, set_cmd=source + 'BURS:PHAS {}', units='deg', vals=Numbers(0, 360)) self.add_parameter(ch + 'burst_trigger_edge', get_cmd=source + 'BURS:TRIG:SLOP?', set_cmd=source + 'BURS:TRIG:SLOP {}', val_mapping={'positive': 'POS', 'negative': 'NEG'}) self.add_parameter(ch + 'burst_trigger_source', get_cmd=source + 'BURS:TRIG:SOUR?', set_cmd=source + 'BURS:TRIG:SOUR {}', val_mapping={'internal': 'INT', 'external': 'EXT', 'manual': 'MAN'}) self.add_parameter(ch + 'burst_trigger_out', get_cmd=source + 'BURS:TRIG:TRIGO?', set_cmd=source + 'BURS:TRIG:TRIGO {}', val_mapping={'off': 'OFF', 'positive': 'POS', 'negative': 'NEG'}) # Source Frequency # TODO: The upper bounds of these parameters also depend on the # current waveform self.add_parameter(ch + 'frequency_center', get_cmd=source + 'FREQ:CENT?', get_parser=float, set_cmd=source + 'FREQ:CENT {}', units='Hz', vals=Numbers(1e-6)) self.add_parameter(ch + 'frequency', get_cmd=source + 'FREQ?', get_parser=float, set_cmd=source + 'FREQ {}', units='Hz', vals=Numbers(1e-6)) self.add_parameter(ch + 'frequency_start', get_cmd=source + 'FREQ:STAR?', get_parser=float, set_cmd=source + 'FREQ:STAR {}', units='Hz', vals=Numbers(1e-6)) self.add_parameter(ch + 'frequency_stop', get_cmd=source + 'FREQ:STOP?', get_parser=float, set_cmd=source + 'FREQ:STOP {}', units='Hz', vals=Numbers(1e-6)) # Source Function self.add_parameter(ch + 'ramp_symmetry', get_cmd=source + 'FUNC:RAMP:SYMM?', get_parser=float, set_cmd=source + 'FUNC:RAMP:SYMM {}', units='%', vals=Numbers(0, 100)) self.add_parameter(ch + 'square_duty_cycle', get_cmd=source + 'FUNC:SQU:DCYC?', get_parser=float, set_cmd=source + 'FUNC:SQU:DCYC {}', units='%', vals=Numbers(20, 80)) # Source Harmonic self.add_function(ch + 'set_harmonic_amplitude', call_cmd=source + 'HARM:AMPL {},{:.6e}', args=[Ints(2, 16), Numbers(0)]) self.add_function(ch + 'get_harmonic_amplitude', call_cmd=source + 'HARM:AMPL? {}', args=[Ints(2, 16)], return_parser=float) self.add_parameter(ch + 'harmonic_order', get_cmd=source + 'HARM:ORDE?', get_parser=int, set_cmd=source + 'HARM:ORDE {}', vals=Ints(2, 16)) self.add_function(ch + 'set_harmonic_phase', call_cmd=source + 'HARM:PHAS {},{:.6e}', args=[Ints(2, 16), Numbers(0, 360)]) self.add_function(ch + 'get_harmonic_phase', call_cmd=source + 'HARM:PHAS? {}', args=[Ints(2, 16)], return_parser=float) self.add_parameter(ch + 'harmonic_type', get_cmd=source + 'HARM:TYP?', get_parser=str.lower, set_cmd=source + 'HARM:TYP {}', vals=Enum('even', 'odd', 'all', 'user')) # Source Marker self.add_parameter(ch + 'marker_frequency', get_cmd=source + 'MARK:FREQ?', get_parser=float, set_cmd=source + 'HMARK:FREQ {}', units='Hz', vals=Numbers(1e-6)) self.add_parameter(ch + 'marker_enabled', get_cmd=source + 'MARK?', set_cmd=source + 'MARK {}', val_mapping=on_off_map) # Source Modulation (not implemented yet) # Source Period (not implemented yet) # Source Phase self.add_parameter(ch + 'phase', get_cmd=source + 'PHAS?', get_parser=float, set_cmd=source + 'PHAS {}', units='deg', vals=Numbers(0, 360)) self.add_function(ch + 'align_phase', call_cmd=source + 'PHAS:INIT') # Source Pulse self.add_parameter(ch + 'pulse_duty_cycle', get_cmd=source + 'PULS:DCYC?', get_parser=float, set_cmd=source + 'PULS:DCYC {}', units='%', vals=Numbers(0, 100)) self.add_parameter(ch + 'pulse_delay', get_cmd=source + 'PULS:DEL?', get_parser=float, set_cmd=source + 'PULS:DEL {}', units='s', vals=Numbers(0)) self.add_parameter(ch + 'pulse_hold', get_cmd=source + 'PULS:HOLD?', set_cmd=source + 'PULS:HOLD {}', units='s', val_mapping={'width': 'WIDT', 'duty': 'DUTY'}) self.add_parameter(ch + 'pulse_leading_edge', get_cmd=source + 'PULS:TRAN:LEAD?', get_parser=float, set_cmd=source + 'PULS:TRAN:LEAD {}', units='s', vals=Numbers(0)) self.add_parameter(ch + 'pulse_trailing_edge', get_cmd=source + 'PULS:TRAN:TRA?', get_parser=float, set_cmd=source + 'PULS:TRAN:TRA {}', units='s', vals=Numbers(0)) self.add_parameter(ch + 'pulse_width', get_cmd=source + 'PULS:WIDT?', get_parser=float, set_cmd=source + 'PULS:WIDT {}', units='s', vals=Numbers(0)) # Source Sweep self.add_parameter(ch + 'sweep_hold_start', get_cmd=source + 'SWE:HTIM:STAR?', get_parser=float, set_cmd=source + 'SWE:HTIM:STAR {}', units='s', vals=Numbers(0, 300)) self.add_parameter(ch + 'sweep_hold_stop', get_cmd=source + 'SWE:HTIM:STOP?', get_parser=float, set_cmd=source + 'SWE:HTIM:STOP {}', units='s', vals=Numbers(0, 300)) self.add_parameter(ch + 'sweep_return_time', get_cmd=source + 'SWE:RTIM?', get_parser=float, set_cmd=source + 'SWE:RTIM {}', units='s', vals=Numbers(0, 300)) self.add_parameter(ch + 'sweep_spacing', get_cmd=source + 'SWE:SPAC?', set_cmd=source + 'SWE:SPAC {}', val_mapping={'linear': 'LIN', 'logarithmic': 'LOG', 'step': 'STE'}) self.add_parameter(ch + 'sweep_enabled', get_cmd=source + 'SWE:STAT?', set_cmd=source + 'SWE:STAT {}', val_mapping=on_off_map) self.add_parameter(ch + 'sweep_step', get_cmd=source + 'SWE:STEP?', get_parser=int, set_cmd=source + 'SWE:STEP {}', vals=Ints(2, 2048)) self.add_parameter(ch + 'sweep_time', get_cmd=source + 'SWE:TIME?', get_parser=float, set_cmd=source + 'SWE:TIME {}', units='s', vals=Numbers(1e-3, 300)) # Source Voltage self.add_parameter(ch + 'amplitude', get_cmd=source + 'VOLT?', get_parser=float, set_cmd=source + 'VOLT {}', units='V', vals=Numbers()) self.add_parameter(ch + 'offset', get_cmd=source + 'VOLT:OFFS?', get_parser=float, set_cmd=source + 'VOLT:OFFS {}', units='V', vals=Numbers()) self.add_parameter(ch + 'unit', get_cmd=source + 'VOLT:UNIT?', get_parser=str.lower, set_cmd=source + 'VOLT:UNIT {}', vals=Enum('vpp', 'vrms', 'dbm')) # System self.add_function('beep', call_cmd='SYST:BEEP') self.add_parameter('beeper_enabled', get_cmd='SYST:BEEP:STAT?', set_cmd='SYST:BEEP:STAT {}', val_mapping=on_off_map) self.add_function('copy_config_to_ch1', call_cmd='SYST:CSC CH2,CH1') self.add_function('copy_config_to_ch2', call_cmd='SYST:CSC CH1,CH2') self.add_function('copy_waveform_to_ch1', call_cmd='SYST:CWC CH2,CH1') self.add_function('copy_waveform_to_ch2', call_cmd='SYST:CWC CH1,CH2') self.add_function('get_error', call_cmd='SYST:ERR?', return_parser=str) self.add_parameter('keyboard_locked', get_cmd='SYST:KLOCK?', set_cmd='SYST:KLOCK {}', val_mapping=on_off_map) self.add_parameter('startup_mode', get_cmd='SYST:POWS?', get_parser=str.lower, set_cmd='SYST:POWS {}', vals=Enum('user', 'auto')) system_states = Enum('default', 'user1', 'user2', 'user3', 'user4', 'user5', 'user6', 'user7', 'user8', 'user9', 'user10') self.add_function('preset', call_cmd='SYST:PRES {}', args=[system_states]) self.add_function('restart', call_cmd='SYST:RESTART') self.add_parameter('reference_clock_source', get_cmd='SYST:ROSC:SOUR?', set_cmd='SYST:ROSC:SOUR {}', val_mapping={'internal': 'INT', 'external': 'EXT'}) self.add_function('shutdown', call_cmd='SYST:SHUTDOWN') self.add_parameter('scpi_version', get_cmd='SYST:VERS?') # Trace self.add_function('upload_data', call_cmd=self._upload_data, args=[Anything()]) self.add_function('reset', call_cmd='*RST') if reset: self.reset() self.connect_message()
def __init__(self, name: str, address: str, reset_device: bool = False, **kwargs: Any): """ 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 __init__(self, parent:'NGE100Base', channel:int): """ Arguments: parent: Parent instrument. channel: Channel number. """ if channel < 1 or channel > parent.nb_channels: raise ValueError(f"Invalid channel '{channel}', channels can " f"only be between 1 and {parent.NB_CHANNELS}.") super().__init__(parent, name=f"ch{channel}") self.channel = channel # Store dummy values for virtual instrument if self.parent.virtual: self._virtual_parameters = {} self.add_parameter( name="voltage", get_cmd=partial(self._get_channel_parameter, "VOLTage?"), set_cmd=partial(self._set_channel_parameter, "VOLTage {}"), get_parser=float, unit="V", vals=Numbers(0, 32.0), docstring="The voltage is adjustable in 10 mV steps. If the set " "value is not on the 10 mV grid, it will be rounded to " "the closest possible value " "(e.g.`` 0.014 -> 0.01``; ``0.015 -> 0.02``)." ) self.add_parameter( name="current_limit", get_cmd=partial(self._get_channel_parameter, "CURRent?"), set_cmd=partial(self._set_channel_parameter, "CURRent {}"), get_parser=float, unit="A", vals=Numbers(0, 3.0), docstring="Note that the actual limit may be lower as the maximum " "output power is 33.6 W per channel." ) self.add_parameter( name="output", get_cmd=partial(self._get_channel_parameter, "OUTPut?"), set_cmd=partial(self._set_channel_parameter, "OUTPut {}"), get_parser=int, vals=Enum(0, 1, "OFF", "ON"), docstring="Enable or disable the channel output." ) self.add_parameter( name="measured_voltage", get_cmd=partial(self._get_channel_parameter, "MEASure:VOLTage?"), get_parser=float, unit="V", docstring="(readonly)" ) self.add_parameter( name="measured_current", get_cmd=partial(self._get_channel_parameter, "MEASure:CURRent?"), get_parser=float, unit="A", docstring="(readonly)" ) self.add_parameter( name="measured_power", get_cmd=partial(self._get_channel_parameter, "MEASure:POWer?"), get_parser=float, unit="W", docstring="(readonly)" ) if self.parent.virtual: self.add_parameter( name="mock_output_current", parameter_class=ManualParameter, unit="A", initial_value=0.0, vals=Numbers(0, 3.0), snapshot_exclude=True, docstring="This value will be forwarded to ``measured_current``. " "Only available for virtual instruments." )
class Sense2450(InstrumentChannel): """ The sense module of the Keithley 2450 SMU. Args: parent name proper_function: This can be one of either "current", "voltage" or "resistance". All parameters and methods in this submodule should only be accessible to the user if self.parent.sense_function.get() == self._proper_function. We ensure this through the 'sense' property on the main driver class which returns the proper submodule for any given function mode """ function_modes = { "current": { "name": '"CURR:DC"', "unit": "A", "range_vals": Numbers(10E-9, 1) }, "resistance": { "name": '"RES"', "unit": "Ohm", "range_vals": Numbers(20, 200E6) }, "voltage": { "name": '"VOLT:DC"', "unit": "V", "range_vals": Numbers(0.02, 200) } } def __init__(self, parent: 'Keithley2450', name: str, proper_function: str) -> None: super().__init__(parent, name) self._proper_function = proper_function range_vals = self.function_modes[self._proper_function]["range_vals"] unit = self.function_modes[self._proper_function]["unit"] self.function = self.parent.sense_function self.add_parameter( "four_wire_measurement", set_cmd=f":SENSe:{self._proper_function}:RSENse {{}}", get_cmd=f":SENSe:{self._proper_function}:RSENse?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0")) self.add_parameter( "range", set_cmd=f":SENSe:{self._proper_function}:RANGe {{}}", get_cmd=f":SENSe:{self._proper_function}:RANGe?", vals=range_vals, get_parser=float, unit=unit) self.add_parameter( "auto_range", set_cmd=f":SENSe:{self._proper_function}:RANGe:AUTO {{}}", get_cmd=f":SENSe:{self._proper_function}:RANGe:AUTO?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0")) self.add_parameter(self._proper_function, get_cmd=self._measure, get_parser=float, unit=unit, snapshot_value=False) self.add_parameter("sweep", label=self._proper_function, get_cmd=self._measure_sweep, unit=unit, vals=Arrays(shape=(self.parent.npts, )), parameter_class=ParameterWithSetpointsCustomized) self.add_parameter( "nplc", get_cmd=f":SENSe:{self._proper_function}:NPLCycles?", set_cmd=f":SENSe:{self._proper_function}:NPLCycles {{}}", vals=Numbers(0.001, 10)) self.add_parameter('user_number', get_cmd=None, set_cmd=None, initial_value=1, vals=Ints(1, 5)) self.add_parameter("user_delay", get_cmd=self._get_user_delay, set_cmd=self._set_user_delay, vals=Numbers(0, 1e4)) self.add_parameter( 'auto_zero_enabled', get_cmd=f":SENSe:{self._proper_function}:AZERo?", set_cmd=f":SENSe:{self._proper_function}:AZERo {{}}", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="This command enables or disables automatic updates to" "the internal reference measurements (autozero) of the" "instrument.") self.add_parameter( 'count', get_cmd=":SENSe:COUNt?", set_cmd=":SENSe:COUNt {}", docstring="The number of measurements to make when a measurement " "is requested.") def _measure(self) -> Union[float, str]: if not self.parent.output_enabled(): raise RuntimeError("Output needs to be on for a measurement") buffer_name = self.parent.buffer_name() return float(self.ask(f":MEASure? '{buffer_name}'")) def _measure_sweep(self) -> np.ndarray: source = cast(Source2450, self.parent.source) source.sweep_start() buffer_name = self.parent.buffer_name() buffer = cast(Buffer2450, self.parent.submodules[f"_buffer_{buffer_name}"]) end_idx = self.parent.npts() raw_data = buffer.get_data(1, end_idx, readings_only=True) raw_data_with_extra = buffer.get_data(1, end_idx) self.parent.sense.sweep._user_selected_data = raw_data_with_extra # Clear the trace so we can be assured that a subsequent measurement # will not be contaminated with data from this run. buffer.clear_buffer() return np.array([float(i) for i in raw_data]) def auto_zero_once(self) -> None: """ This command causes the instrument to refresh the reference and zero measurements once. """ self.write(":SENSe:AZERo:ONCE") def clear_trace(self, buffer_name: str = "defbuffer1") -> None: """ Clear the data buffer """ self.write(f":TRACe:CLEar '{buffer_name}'") def _get_user_delay(self) -> str: get_cmd = f":SENSe:{self._proper_function}:DELay:USER" \ f"{self.user_number()}?" return self.ask(get_cmd) def _set_user_delay(self, value) -> None: set_cmd = f":SENSe:{self._proper_function}:DELay:USER" \ f"{self.user_number()} {value}" self.write(set_cmd)
class Sense2450(InstrumentChannel): """ The sense module of the Keithley 2450 SMU. Args: parent name proper_function: This can be one of either "current", "voltage" or "resistance". All parameters and methods in this submodule should only be accessible to the user if self.parent.sense_function.get() == self._proper_function. We ensure this through the 'sense' property on the main driver class which returns the proper submodule for any given function mode """ function_modes = { "current": { "name": '"CURR:DC"', "unit": "A", "range_vals": Numbers(10E-9, 1) }, "resistance": { "name": '"RES"', "unit": "Ohm", "range_vals": Numbers(20, 200E6) }, "voltage": { "name": '"VOLT:DC"', "unit": "V", "range_vals": Numbers(0.02, 200) } } def __init__(self, parent: 'Keithley2450', name: str, proper_function: str) -> None: super().__init__(parent, name) self._proper_function = proper_function range_vals = self.function_modes[self._proper_function]["range_vals"] unit = self.function_modes[self._proper_function]["unit"] self.function = self.parent.sense_function self.add_parameter( "four_wire_measurement", set_cmd=f":SENSe:{self._proper_function}:RSENse {{}}", get_cmd=f":SENSe:{self._proper_function}:RSENse?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0") ) self.add_parameter( "range", set_cmd=f":SENSe:{self._proper_function}:RANGe {{}}", get_cmd=f":SENSe:{self._proper_function}:RANGe?", vals=range_vals, get_parser=float, unit=unit ) self.add_parameter( "auto_range", set_cmd=f":SENSe:{self._proper_function}:RANGe:AUTO {{}}", get_cmd=f":SENSe:{self._proper_function}:RANGe:AUTO?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0") ) self.add_parameter( self._proper_function, get_cmd=self._measure, get_parser=float, unit=unit, snapshot_value=False ) self.add_parameter( "sweep", label=self._proper_function, get_cmd=self._measure_sweep, unit=unit, vals=Arrays(shape=(self.parent.npts,)), parameter_class=ParameterWithSetpoints ) def _measure(self) -> str: if not self.parent.output_enabled(): raise RuntimeError("Output needs to be on for a measurement") return self.ask(":MEASure?") def _measure_sweep(self) -> np.ndarray: source = cast(Source2450, self.parent.source) source.sweep_start() raw_data = self.ask(f":TRACe:DATA? 1, {self.parent.npts()}") # Clear the trace so we can be assured that a subsequent measurement # will not be contaminated with data from this run. self.clear_trace() return np.array([float(i) for i in raw_data.split(",")]) def clear_trace(self) -> None: """ Clear the data buffer """ self.write(":TRACe:CLEar")
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"""
class Source2450(InstrumentChannel): """ The source module of the Keithley 2450 SMU. Args: parent name proper_function: This can be one of either "current" or "voltage" All parameters and methods in this submodule should only be accessible to the user if self.parent.source_function.get() == self._proper_function. We ensure this through the 'source' property on the main driver class which returns the proper submodule for any given function mode """ function_modes = { "current": { "name": "CURR", "unit": "A", "range_vals": Numbers(-1, 1) }, "voltage": { "name": "VOLT", "unit": "V", "range_vals": Numbers(-200, 200) } } def __init__(self, parent: 'Keithley2450', name: str, proper_function: str) -> None: super().__init__(parent, name) self._proper_function = proper_function range_vals = self.function_modes[self._proper_function]["range_vals"] unit = self.function_modes[self._proper_function]["unit"] self.function = self.parent.source_function self._sweep_arguments: Dict[str, Union[float, int, str]] = {} self.add_parameter( "range", set_cmd=f":SOUR:{self._proper_function}:RANGe {{}}", get_cmd=f":SOUR:{self._proper_function}:RANGe?", vals=range_vals, get_parser=float, unit=unit ) self.add_parameter( "auto_range", set_cmd=f":SOURce:{self._proper_function}:RANGe:AUTO {{}}", get_cmd=f":SOURce:{self._proper_function}:RANGe:AUTO?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0") ) limit_cmd = {"current": "VLIM", "voltage": "ILIM"}[self._proper_function] self.add_parameter( "limit", set_cmd=f"SOUR:{self._proper_function}:{limit_cmd} {{}}", get_cmd=f"SOUR:{self._proper_function}:{limit_cmd}?", get_parser=float, unit=unit ) self.add_parameter( self._proper_function, set_cmd=f"SOUR:{self._proper_function} {{}}", get_cmd=f"SOUR:{self._proper_function}?", get_parser=float, unit=unit, snapshot_value=False ) self.add_parameter( "sweep_axis", label=self._proper_function, get_cmd=self.get_sweep_axis, vals=Arrays(shape=(self.parent.npts,)), unit=unit ) def get_sweep_axis(self) -> np.ndarray: if self._sweep_arguments == {}: raise ValueError( "Please setup the sweep before getting values of this parameter" ) return np.linspace( start=self._sweep_arguments["start"], stop=self._sweep_arguments["stop"], num=self._sweep_arguments["step_count"] ) def sweep_setup( self, start: float, stop: float, step_count: int, delay: float = 0, sweep_count: int = 1, range_mode: str = "AUTO" ) -> None: self._sweep_arguments = dict( start=start, stop=stop, step_count=step_count, delay=delay, sweep_count=sweep_count, range_mode=range_mode ) def sweep_start(self) -> None: """ Start a sweep and return when the sweep has finished. Note: This call is blocking """ cmd_args = dict(self._sweep_arguments) cmd_args["function"] = self._proper_function cmd = ":SOURce:SWEep:{function}:LINear {start},{stop}," \ "{step_count},{delay},{sweep_count},{range_mode}".format(**cmd_args) self.write(cmd) self.write(":INITiate") self.write("*WAI") def sweep_reset(self) -> None: self._sweep_arguments = {}
def __init__(self, name, address, reset=False, **kwargs): super().__init__(name, address, terminator='\n', **kwargs) on_off_map = {True: 'ON', False: 'OFF'} for channel in ['A', 'B', 'C', 'D']: c = 'ch{}_'.format(channel) self.add_parameter(c + 'temperature', get_cmd='input? {}'.format(channel), get_parser=float) self.add_parameter(c + 'units', get_cmd='input {}:units?'.format(channel), get_parser=str.upper, set_cmd='input {}:units {{}}'.format(channel), vals=Enum('K', 'C', 'F', 'S')) self.add_parameter(c + 'sensor', get_cmd='input {}:sensor?'.format(channel), get_parser=int, set_cmd='input {}:sensor {{}}'.format(channel), vals=Ints(0)) self.add_parameter(c + 'sensor_power', get_cmd='input {}:sensp {{}}'.format(channel), get_parser=float) self.add_parameter(c + 'min', get_cmd='input? {}:min'.format(channel), get_parser=float) self.add_parameter(c + 'max', get_cmd='input? {}:max'.format(channel), get_parser=float) self.add_parameter(c + 'variance', get_cmd='input? {}:variance'.format(channel), get_parser=float) self.add_parameter(c + 'slope', get_cmd='input? {}:slope'.format(channel), get_parser=float) self.add_parameter(c + 'offset', get_cmd='input? {}:offset'.format(channel), get_parser=float) self.add_function('stop_control_loops', call_cmd='stop') self.add_function('start_control_loops', call_cmd='control') # TODO: check case of returned strings self.add_parameter('control_enabled', get_cmd='control?', val_mapping=on_off_map) for loop in [1, 2, 3, 4]: l = 'loop{}_'.format(loop) self.add_parameter(l + 'source', get_cmd='loop {}:source?'.format(loop), get_parser=str.upper, set_cmd='loop {}:source {{}}'.format(loop), vals=Enum('A', 'B', 'C', 'D')) self.add_parameter(l + 'setpoint', get_cmd='loop {}:setpt?'.format(loop), get_parser=float, set_cmd='loop {}:setpt {{}}'.format(loop), vals=Numbers()) self.add_parameter(l + 'type', get_cmd='loop {}:type?', set_cmd='loop {}:type {{}}'.format(loop), val_mapping={ 'off': 'OFF', 'manual': 'MAN', 'PID': 'PID', 'table': 'TABLE', 'ramp': 'RAMPP' }) mapping = {'high': 'HI', 'medium': 'MID', 'low': 'LOW'} if loop == 1: mapping['100W'] = '100W' self.add_parameter(l + 'range', get_cmd='loop {}:range?', set_cmd='loop {}:range {{}}'.format(loop), val_mapping=mapping) self.add_parameter(l + 'is_ramping', get_cmd='loop {}:ramp?', val_mapping=on_off_map) self.add_parameter(l + 'ramp_rate', get_cmd='loop {}:rate?'.format(loop), get_parser=float, set_cmd='loop {}:rate {{}}'.format(loop), vals=Numbers(0, 100), unit='units/min') self.add_parameter(l + 'P', get_cmd='loop {}:pgain?'.format(loop), get_parser=float, set_cmd='loop {}:pgain {{}}'.format(loop), vals=Numbers(0, 1000), unit='-') self.add_parameter(l + 'I', get_cmd='loop {}:igain?'.format(loop), get_parser=float, set_cmd='loop {}:igain {{}}'.format(loop), vals=Numbers(0, 1000), unit='s') self.add_parameter(l + 'D', get_cmd='loop {}:dgain?'.format(loop), get_parser=float, set_cmd='loop {}:dgain {{}}'.format(loop), vals=Numbers(0, 1000), unit='1/s') self.add_parameter(l + 'manual_power', get_cmd='loop {}:pman?', get_parser=float, set_cmd='loop {}:pman {{}}'.format(loop), vals=Numbers(0, 100), unit='%') self.add_parameter(l + 'output_power', get_cmd='loop {}:outp?', get_parser=float, unit='%') self.add_parameter(l + 'read_heater', get_cmd='loop {}:htrread?', get_parser=float, unit='%') self.add_parameter(l + 'max_power', get_cmd='loop {}:maxp?', get_parser=float, set_cmd='loop {}:maxp {{}}'.format(loop), vals=Numbers(0, 100), unit='%')
def __init__(self, name, address, coil_constant=0.113375, current_rating=105.84, current_ramp_limit=0.0506, reset=False, timeout=5, **kwargs): log.debug('Initializing instrument') if 'terminator' in kwargs.keys(): kwargs.pop('terminator') log.warning('Passing terminator to CryogenicSMS is no longer supported and has no effect') super().__init__(name, address, terminator='\r\n', **kwargs) self.visa_handle.baud_rate = 9600 self.visa_handle.parity = visa.constants.Parity.none self.visa_handle.stop_bits = visa.constants.StopBits.one self.visa_handle.data_bits = 8 self.visa_handle.flow_control = 0 self.visa_handle.flush(vi_const.VI_READ_BUF_DISCARD | vi_const.VI_WRITE_BUF_DISCARD) # keep for debugging idn = self.IDN.get() print(idn) self._persistentField = 0 # temp code stub self._coil_constant = coil_constant self._current_rating = current_rating self._current_ramp_limit = current_ramp_limit self._field_rating = coil_constant * \ current_rating # corresponding max field based self._field_ramp_limit = coil_constant * current_ramp_limit self.add_parameter(name='unit', get_cmd=self._get_unit, set_cmd=self._set_unit, val_mapping={'AMPS': 0, 'TESLA': 1}) self.add_parameter('rampStatus', get_cmd=self._get_rampStatus, val_mapping={'HOLDING': 0, 'RAMPING': 1, 'QUENCH DETECTED': 2, 'EXTERNAL TRIP': 3, 'FAULT': 4, }) self.add_parameter('polarity', get_cmd=self._get_polarity, set_cmd=self._set_polarity, val_mapping={'POSITIVE': '+', 'NEGATIVE': '-'}) self.add_parameter(name='switchHeater', get_cmd=self._get_switchHeater, set_cmd=self._set_switchHeater, val_mapping={False: 0, True: 1}) self.add_parameter('persistentMode', get_cmd=self._get_persistentMode, set_cmd=self._set_persistentMode, val_mapping={False: 0, True: 1}) self.add_parameter(name='persistentField', get_cmd=self._get_persistentField, vals=Numbers(self._persistentField)) self.add_parameter(name='field', get_cmd=self._get_field, set_cmd=self._set_field, vals=Numbers(-self._field_rating, # i.e. ~12T, calculated self._field_rating)) self.add_parameter(name='maxField', get_cmd=self._get_maxField, set_cmd=self._set_maxField, vals=Numbers(0, # i.e. ~12T, calculated self._field_rating)) self.add_parameter(name='rampRate', get_cmd=self._get_rampRate, set_cmd=self._set_rampRate, vals=Numbers(0, self._current_ramp_limit)) self.add_parameter('pauseRamp', set_cmd=self._set_pauseRamp, get_cmd=self._get_pauseRamp, val_mapping={False: 0, True: 1})
def __init__(self, name, address=None, port=None, reset=False, terminator='\r\n', current_ramp_limit=None, has_current_rating=False, **kwargs): super().__init__(name, address, port, terminator=terminator, write_confirmation=False, **kwargs) self._parent_instrument = None self.has_current_rating = has_current_rating # Add reset function self.add_function('reset', call_cmd='*RST') if reset: self.reset() # Add parameters setting instrument units self.add_parameter("ramp_rate_units", get_cmd='RAMP:RATE:UNITS?', set_cmd=(lambda units: self._update_units(ramp_rate_units=units)), val_mapping={'seconds': 0, 'minutes': 1}) self.add_parameter('field_units', get_cmd='FIELD:UNITS?', set_cmd=(lambda units: self._update_units(field_units=units)), val_mapping={'kilogauss': 0, 'tesla': 1}) # Set programatic safety limits self.add_parameter('current_ramp_limit', get_cmd=lambda: self._current_ramp_limit, set_cmd=self._update_ramp_rate_limit, unit="A/s") self.add_parameter('field_ramp_limit', get_cmd=lambda: self.current_ramp_limit(), set_cmd=lambda x: self.current_ramp_limit(x), scale=1/float(self.ask("COIL?")), unit="T/s") if current_ramp_limit is None: self._update_ramp_rate_limit(AMI430._DEFAULT_CURRENT_RAMP_LIMIT, update=False) else: self._update_ramp_rate_limit(current_ramp_limit, update=False) # Add solenoid parameters self.add_parameter('coil_constant', get_cmd=self._update_coil_constant, set_cmd=self._update_coil_constant, vals=Numbers(0.001, 999.99999)) # TODO: Not all AMI430s expose this setting. Currently, we # don't know why, but this most likely a firmware version issue, # so eventually the following condition will be smth like # if firmware_version > XX if has_current_rating: self.add_parameter('current_rating', get_cmd="CURR:RATING?", get_parser=float, set_cmd="CONF:CURR:RATING {}", unit="A", vals=Numbers(0.001, 9999.9999)) self.add_parameter('field_rating', get_cmd=lambda: self.current_rating(), set_cmd=lambda x: self.current_rating(x), scale=1/float(self.ask("COIL?"))) self.add_parameter('current_limit', unit="A", set_cmd="CONF:CURR:LIMIT {}", get_cmd='CURR:LIMIT?', get_parser=float, vals=Numbers(0, 80)) # what are good numbers here? self.add_parameter('field_limit', set_cmd=self.current_limit.set, get_cmd=self.current_limit.get, scale=1/float(self.ask("COIL?"))) # Add current solenoid parameters # Note that field is validated in set_field self.add_parameter('field', get_cmd='FIELD:MAG?', get_parser=float, set_cmd=self.set_field) self.add_parameter('ramp_rate', get_cmd=self._get_ramp_rate, set_cmd=self._set_ramp_rate) self.add_parameter('setpoint', get_cmd='FIELD:TARG?', get_parser=float) self.add_parameter('is_quenched', get_cmd='QU?', val_mapping={True: 1, False: 0}) self.add_function('reset_quench', call_cmd='QU 0') self.add_function('set_quenched', call_cmd='QU 1') self.add_parameter('ramping_state', get_cmd='STATE?', get_parser=int, val_mapping={ 'ramping': 1, 'holding': 2, 'paused': 3, 'manual up': 4, 'manual down': 5, 'zeroing current': 6, 'quench detected': 7, 'at zero current': 8, 'heating switch': 9, 'cooling switch': 10, }) # Add persistent switch switch_heater = AMI430SwitchHeater(self) self.add_submodule("switch_heater", switch_heater) # Add interaction functions self.add_function('get_error', call_cmd='SYST:ERR?') self.add_function('ramp', call_cmd='RAMP') self.add_function('pause', call_cmd='PAUSE') self.add_function('zero', call_cmd='ZERO') # Correctly assign all units self._update_units() self.connect_message()
def __init__(self, parent: 'Keithley2450', name: str, proper_function: str) -> None: super().__init__(parent, name) self._proper_function = proper_function range_vals = self.function_modes[self._proper_function]["range_vals"] unit = self.function_modes[self._proper_function]["unit"] self.function = self.parent.sense_function self.add_parameter( "four_wire_measurement", set_cmd=f":SENSe:{self._proper_function}:RSENse {{}}", get_cmd=f":SENSe:{self._proper_function}:RSENse?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0")) self.add_parameter( "range", set_cmd=f":SENSe:{self._proper_function}:RANGe {{}}", get_cmd=f":SENSe:{self._proper_function}:RANGe?", vals=range_vals, get_parser=float, unit=unit) self.add_parameter( "auto_range", set_cmd=f":SENSe:{self._proper_function}:RANGe:AUTO {{}}", get_cmd=f":SENSe:{self._proper_function}:RANGe:AUTO?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0")) self.add_parameter(self._proper_function, get_cmd=self._measure, get_parser=float, unit=unit, snapshot_value=False) self.add_parameter("sweep", label=self._proper_function, get_cmd=self._measure_sweep, unit=unit, vals=Arrays(shape=(self.parent.npts, )), parameter_class=ParameterWithSetpointsCustomized) self.add_parameter( "nplc", get_cmd=f":SENSe:{self._proper_function}:NPLCycles?", set_cmd=f":SENSe:{self._proper_function}:NPLCycles {{}}", vals=Numbers(0.001, 10)) self.add_parameter('user_number', get_cmd=None, set_cmd=None, initial_value=1, vals=Ints(1, 5)) self.add_parameter("user_delay", get_cmd=self._get_user_delay, set_cmd=self._set_user_delay, vals=Numbers(0, 1e4)) self.add_parameter( 'auto_zero_enabled', get_cmd=f":SENSe:{self._proper_function}:AZERo?", set_cmd=f":SENSe:{self._proper_function}:AZERo {{}}", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="This command enables or disables automatic updates to" "the internal reference measurements (autozero) of the" "instrument.") self.add_parameter( 'count', get_cmd=":SENSe:COUNt?", set_cmd=":SENSe:COUNt {}", docstring="The number of measurements to make when a measurement " "is requested.")
def __init__(self, name, instrument_x, instrument_y, instrument_z, field_limit, **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 if repr(field_limit).isnumeric() or isinstance(field_limit, collections.Iterable): self._field_limit = field_limit else: raise ValueError("field limit should either be" " a number or an iterable") 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() )
class Source2450(InstrumentChannel): """ The source module of the Keithley 2450 SMU. Args: parent name proper_function: This can be one of either "current" or "voltage" All parameters and methods in this submodule should only be accessible to the user if self.parent.source_function.get() == self._proper_function. We ensure this through the 'source' property on the main driver class which returns the proper submodule for any given function mode """ function_modes = { "current": { "name": "CURR", "unit": "A", "range_vals": Numbers(-1, 1) }, "voltage": { "name": "VOLT", "unit": "V", "range_vals": Numbers(-200, 200) } } def __init__(self, parent: 'Keithley2450', name: str, proper_function: str) -> None: super().__init__(parent, name) self._proper_function = proper_function range_vals = self.function_modes[self._proper_function]["range_vals"] unit = self.function_modes[self._proper_function]["unit"] self.function = self.parent.source_function self._sweep_arguments: Dict[str, Union[float, int, str]] = {} self.add_parameter("range", set_cmd=f":SOUR:{self._proper_function}:RANGe {{}}", get_cmd=f":SOUR:{self._proper_function}:RANGe?", vals=range_vals, get_parser=float, unit=unit) self.add_parameter( "auto_range", set_cmd=f":SOURce:{self._proper_function}:RANGe:AUTO {{}}", get_cmd=f":SOURce:{self._proper_function}:RANGe:AUTO?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0")) limit_cmd = { "current": "VLIM", "voltage": "ILIM" }[self._proper_function] self.add_parameter( "limit", set_cmd=f"SOUR:{self._proper_function}:{limit_cmd} {{}}", get_cmd=f"SOUR:{self._proper_function}:{limit_cmd}?", get_parser=float, unit=unit) self.add_parameter( "limit_tripped", get_cmd=f":SOUR:{self._proper_function}:{limit_cmd}:TRIPped?", val_mapping={ True: 1, False: 0 }) self.add_parameter(self._proper_function, set_cmd=f"SOUR:{self._proper_function} {{}}", get_cmd=f"SOUR:{self._proper_function}?", get_parser=float, unit=unit, snapshot_value=False) self.add_parameter("sweep_axis", label=self._proper_function, get_cmd=self.get_sweep_axis, vals=Arrays(shape=(self.parent.npts, )), unit=unit) self.add_parameter( "delay", get_cmd=f":SOURce:{self._proper_function}:DELay?", set_cmd=f":SOURce:{self._proper_function}:DELay {{}}", vals=Numbers(0, 1e4)) self.add_parameter('user_number', get_cmd=None, set_cmd=None, initial_value=1, vals=Ints(1, 5)) self.add_parameter("user_delay", get_cmd=self._get_user_delay, set_cmd=self._set_user_delay, vals=Numbers(0, 1e4)) self.add_parameter( "auto_delay", get_cmd=f":SOURce:{self._proper_function}:DELay:AUTO?", set_cmd=f":SOURce:{self._proper_function}:DELay:AUTO {{}}", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0")) self.add_parameter( "read_back_enabled", get_cmd=f":SOURce:{self._proper_function}:READ:BACK?", set_cmd=f":SOURce:{self._proper_function}:READ:BACK {{}}", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="This command determines if the instrument records the " "measured source value or the configured source value " "when making a measurement.") def get_sweep_axis(self) -> np.ndarray: if self._sweep_arguments == {}: raise ValueError( "Please setup the sweep before getting values of this parameter" ) return np.linspace(start=self._sweep_arguments["start"], stop=self._sweep_arguments["stop"], num=self._sweep_arguments["step_count"]) def sweep_setup(self, start: float, stop: float, step_count: int, delay: float = 0, sweep_count: int = 1, range_mode: str = "AUTO", fail_abort: str = "ON", dual: str = "OFF", buffer_name: str = "defbuffer1") -> None: self._sweep_arguments = dict(start=start, stop=stop, step_count=step_count, delay=delay, sweep_count=sweep_count, range_mode=range_mode, fail_abort=fail_abort, dual=dual, buffer_name=buffer_name) def sweep_start(self) -> None: """ Start a sweep and return when the sweep has finished. Note: This call is blocking """ cmd_args = dict(self._sweep_arguments) cmd_args["function"] = self._proper_function cmd = ":SOURce:SWEep:{function}:LINear {start},{stop}," \ "{step_count},{delay},{sweep_count},{range_mode}," \ "{fail_abort},{dual},'{buffer_name}'".format(**cmd_args) self.write(cmd) self.write(":INITiate") self.write("*WAI") def sweep_reset(self) -> None: self._sweep_arguments = {} def _get_user_delay(self) -> str: get_cmd = f":SOURce:{self._proper_function}:DELay:USER" \ f"{self.user_number()}?" return self.ask(get_cmd) def _set_user_delay(self, value) -> None: set_cmd = f":SOURce:{self._proper_function}:DELay:USER" \ f"{self.user_number()} {value}" self.write(set_cmd)
def initialize(self, reverse_clearance: int = 0) -> None: """ Prepares motor for operation Configure the motor settings and perform mechanical limit seeking Digital input 6 is configured as the external limit switch with inverted polarity (such that the controller is triggered when the limit switch turns off). The external limit switch is configured for free run backwards mode during an external reference run and stop mode during normal runs. """ self.command_response('Enabled') self.firmware_version() self.phase_current(20) self.phase_current_standstill(0) self.limit_switch_behavior(int(0b0010010000100010)) self.io_polarity(int(0b110000000000011111)) self.digital_input_6_function('ExternalLimitSwitch') self.error_correction('Off') self.reverse_clearance(reverse_clearance) self.acceleration_mode('Jerk-free') self.acceleration_jerk(1) self.braking_jerk(100000000) self.step_mode(4) # Escape the limit if we are currently at one previous_direction = self.direction() if self.limit_switch_on(): for i in range(1, 3): if previous_direction == 'Left': direction = 'Right' else: direction = 'Left' self._escape_limit(direction, steps=30 * i) previous_direction = self.direction() # Find the limits by driving to the upper limit in external reference # run mode and then driving to lower limit in relative mode until # the limit switch is reached and the motor stops print(f'Finding {self.name} motor travel limits') self._find_limits() # Travel away from the limit self._travel_away_from_limit() self.limit_switch_behavior(0b10010000100010) self._is_initialized = True self.add_parameter( 'setting_normalized', label='Normalized Setting', unit='', get_cmd=(lambda: self.position()), set_cmd=(lambda x: self.drive_motor(x)), get_parser=(lambda x: float(x) / self._upper_bound), set_parser=(lambda x: round(self._upper_bound * x)), vals=Numbers(min_value=0.0, max_value=1.0), docstring=('Normalized setting (position)' 'Min value: 0' 'Max value: 1' 'Automatically mapped between lower and upper' 'bound of limit switch'))
def __init__(self, parent: 'Keithley2450', name: str, proper_function: str) -> None: super().__init__(parent, name) self._proper_function = proper_function range_vals = self.function_modes[self._proper_function]["range_vals"] unit = self.function_modes[self._proper_function]["unit"] self.function = self.parent.source_function self._sweep_arguments: Dict[str, Union[float, int, str]] = {} self.add_parameter("range", set_cmd=f":SOUR:{self._proper_function}:RANGe {{}}", get_cmd=f":SOUR:{self._proper_function}:RANGe?", vals=range_vals, get_parser=float, unit=unit) self.add_parameter( "auto_range", set_cmd=f":SOURce:{self._proper_function}:RANGe:AUTO {{}}", get_cmd=f":SOURce:{self._proper_function}:RANGe:AUTO?", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0")) limit_cmd = { "current": "VLIM", "voltage": "ILIM" }[self._proper_function] self.add_parameter( "limit", set_cmd=f"SOUR:{self._proper_function}:{limit_cmd} {{}}", get_cmd=f"SOUR:{self._proper_function}:{limit_cmd}?", get_parser=float, unit=unit) self.add_parameter( "limit_tripped", get_cmd=f":SOUR:{self._proper_function}:{limit_cmd}:TRIPped?", val_mapping={ True: 1, False: 0 }) self.add_parameter(self._proper_function, set_cmd=f"SOUR:{self._proper_function} {{}}", get_cmd=f"SOUR:{self._proper_function}?", get_parser=float, unit=unit, snapshot_value=False) self.add_parameter("sweep_axis", label=self._proper_function, get_cmd=self.get_sweep_axis, vals=Arrays(shape=(self.parent.npts, )), unit=unit) self.add_parameter( "delay", get_cmd=f":SOURce:{self._proper_function}:DELay?", set_cmd=f":SOURce:{self._proper_function}:DELay {{}}", vals=Numbers(0, 1e4)) self.add_parameter('user_number', get_cmd=None, set_cmd=None, initial_value=1, vals=Ints(1, 5)) self.add_parameter("user_delay", get_cmd=self._get_user_delay, set_cmd=self._set_user_delay, vals=Numbers(0, 1e4)) self.add_parameter( "auto_delay", get_cmd=f":SOURce:{self._proper_function}:DELay:AUTO?", set_cmd=f":SOURce:{self._proper_function}:DELay:AUTO {{}}", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0")) self.add_parameter( "read_back_enabled", get_cmd=f":SOURce:{self._proper_function}:READ:BACK?", set_cmd=f":SOURce:{self._proper_function}:READ:BACK {{}}", val_mapping=create_on_off_val_mapping(on_val="1", off_val="0"), docstring="This command determines if the instrument records the " "measured source value or the configured source value " "when making a measurement.")
def __init__(self, name, address, **kwargs): super().__init__(name, address, **kwargs) # Reference and phase self.add_parameter('phase', label='Phase', get_cmd='PHAS?', get_parser=float, set_cmd='PHAS {:.2f}', unit='deg', vals=Numbers(min_value=-360, max_value=729.99)) self.add_parameter('reference_source', label='Reference source', get_cmd='FMOD?', set_cmd='FMOD {}', val_mapping={ 'external': 0, 'internal': 1, }, vals=Enum('external', 'internal')) self.add_parameter('frequency', label='Frequency', get_cmd='FREQ?', get_parser=float, set_cmd='FREQ {:.4f}', unit='Hz', vals=Numbers(min_value=1e-3, max_value=102e3)) self.add_parameter('ext_trigger', label='External trigger', get_cmd='RSLP?', set_cmd='RSLP {}', val_mapping={ 'sine': 0, 'TTL rising': 1, 'TTL falling': 2, }) self.add_parameter('harmonic', label='Harmonic', get_cmd='HARM?', get_parser=int, set_cmd='HARM {:d}', vals=Ints(min_value=1, max_value=19999)) self.add_parameter('amplitude', label='Amplitude', get_cmd='SLVL?', get_parser=float, set_cmd='SLVL {:.3f}', unit='V', vals=Numbers(min_value=0.004, max_value=5.000)) # Input and filter self.add_parameter('input_config', label='Input configuration', get_cmd='ISRC?', get_parser=self._get_input_config, set_cmd='ISRC {}', set_parser=self._set_input_config, vals=Enum(*self._INPUT_CONFIG_TO_N.keys())) self.add_parameter('input_shield', label='Input shield', get_cmd='IGND?', set_cmd='IGND {}', val_mapping={ 'float': 0, 'ground': 1, }) self.add_parameter('input_coupling', label='Input coupling', get_cmd='ICPL?', set_cmd='ICPL {}', val_mapping={ 'AC': 0, 'DC': 1, }) self.add_parameter('notch_filter', label='Notch filter', get_cmd='ILIN?', set_cmd='ILIN {}', val_mapping={ 'off': 0, 'line in': 1, '2x line in': 2, 'both': 3, }) # Gain and time constant self.add_parameter(name='sensitivity', label='Sensitivity', get_cmd='SENS?', set_cmd='SENS {:d}', get_parser=self._get_sensitivity, set_parser=self._set_sensitivity) self.add_parameter('reserve', label='Reserve', get_cmd='RMOD?', set_cmd='RMOD {}', val_mapping={ 'high': 0, 'normal': 1, 'low noise': 2, }) self.add_parameter('time_constant', label='Time constant', get_cmd='OFLT?', set_cmd='OFLT {}', unit='s', val_mapping={ 10e-6: 0, 30e-6: 1, 100e-6: 2, 300e-6: 3, 1e-3: 4, 3e-3: 5, 10e-3: 6, 30e-3: 7, 100e-3: 8, 300e-3: 9, 1: 10, 3: 11, 10: 12, 30: 13, 100: 14, 300: 15, 1e3: 16, 3e3: 17, 10e3: 18, 30e3: 19, }) self.add_parameter('filter_slope', label='Filter slope', get_cmd='OFSL?', set_cmd='OFSL {}', unit='dB/oct', val_mapping={ 6: 0, 12: 1, 18: 2, 24: 3, }) self.add_parameter('sync_filter', label='Sync filter', get_cmd='SYNC?', set_cmd='SYNC {}', val_mapping={ 'off': 0, 'on': 1, }) def parse_offset_get(s): parts = s.split(',') return float(parts[0]), int(parts[1]) # TODO: Parameters that can be set with multiple arguments # For the OEXP command for example two arguments are needed self.add_parameter('X_offset', get_cmd='OEXP? 1', get_parser=parse_offset_get) self.add_parameter('Y_offset', get_cmd='OEXP? 2', get_parser=parse_offset_get) self.add_parameter('R_offset', get_cmd='OEXP? 3', get_parser=parse_offset_get) # Aux input/output for i in [1, 2, 3, 4]: self.add_parameter('aux_in{}'.format(i), label='Aux input {}'.format(i), get_cmd='OAUX? {}'.format(i), get_parser=float, unit='V') self.add_parameter('aux_out{}'.format(i), label='Aux output {}'.format(i), get_cmd='AUXV? {}'.format(i), get_parser=float, set_cmd='AUXV {0}, {{}}'.format(i), unit='V') # Setup self.add_parameter('output_interface', label='Output interface', get_cmd='OUTX?', set_cmd='OUTX {}', val_mapping={ 'RS232': '0\n', 'GPIB': '1\n', }) # Channel setup for ch in range(1, 3): # detailed validation and mapping performed in set/get functions self.add_parameter('ch{}_ratio'.format(ch), label='Channel {} ratio'.format(ch), get_cmd=partial(self._get_ch_ratio, ch), set_cmd=partial(self._set_ch_ratio, ch), vals=Strings()) self.add_parameter('ch{}_display'.format(ch), label='Channel {} display'.format(ch), get_cmd=partial(self._get_ch_display, ch), set_cmd=partial(self._set_ch_display, ch), vals=Strings()) self.add_parameter('ch{}_databuffer'.format(ch), channel=ch, parameter_class=ChannelBuffer) # Data transfer self.add_parameter('X', get_cmd='OUTP? 1', get_parser=float, unit='V') self.add_parameter('Y', get_cmd='OUTP? 2', get_parser=float, unit='V') self.add_parameter('R', get_cmd='OUTP? 3', get_parser=float, unit='V') self.add_parameter('P', get_cmd='OUTP? 4', get_parser=float, unit='deg') # Data buffer settings self.add_parameter('buffer_SR', label='Buffer sample rate', get_cmd='SRAT ?', set_cmd=self._set_buffer_SR, unit='Hz', val_mapping={ 62.5e-3: 0, 0.125: 1, 0.250: 2, 0.5: 3, 1: 4, 2: 5, 4: 6, 8: 7, 16: 8, 32: 9, 64: 10, 128: 11, 256: 12, 512: 13, 'Trigger': 14 }, get_parser=int) self.add_parameter('buffer_acq_mode', label='Buffer acquistion mode', get_cmd='SEND ?', set_cmd='SEND {}', val_mapping={ 'single shot': 0, 'loop': 1 }, get_parser=int) self.add_parameter('buffer_trig_mode', label='Buffer trigger start mode', get_cmd='TSTR ?', set_cmd='TSTR {}', val_mapping={ 'ON': 1, 'OFF': 0 }, get_parser=int) self.add_parameter('buffer_npts', label='Buffer number of stored points', get_cmd='SPTS ?', get_parser=int) # Auto functions self.add_function('auto_gain', call_cmd='AGAN') self.add_function('auto_reserve', call_cmd='ARSV') self.add_function('auto_phase', call_cmd='APHS') self.add_function('auto_offset', call_cmd='AOFF {0}', args=[Enum(1, 2, 3)]) # 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') self.add_function('send_trigger', call_cmd='TRIG', docstring=("Send a software trigger. " "This command has the same effect as a " "trigger at the rear panel trigger" " input.")) self.add_function('buffer_start', call_cmd='STRT', docstring=("The buffer_start command starts or " "resumes data storage. buffer_start" " is ignored if storage is already in" " progress.")) self.add_function('buffer_pause', call_cmd='PAUS', docstring=("The buffer_pause command pauses data " "storage. If storage is already paused " "or reset then this command is ignored.")) self.add_function('buffer_reset', call_cmd='REST', docstring=("The buffer_reset command resets the data" " buffers. The buffer_reset command can " "be sent at any time - any storage in " "progress, paused or not, will be reset." " This command will erase the data " "buffer.")) # Initialize the proper units of the outputs and sensitivities self.input_config() # start keeping track of buffer setpoints self._buffer1_ready = False self._buffer2_ready = False self.connect_message()
def __init__(self, name: str, address: str, terminator: str = "\n", **kwargs) -> 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.connect_message()
def __init__(self, name: str, address: str, **kwargs) -> None: super().__init__(name, address, terminator='\n', **kwargs) # general commands self.add_parameter(name='frequency', label='Frequency', unit='Hz', get_cmd='FREQ?', set_cmd='FREQ {}', get_parser=float, vals=Numbers(min_value=100e3, max_value=40e9)) self.add_parameter(name='freq_offset', label='Frequency offset', unit='Hz', get_cmd='FREQ:OFFS?', set_cmd='FREQ:OFFS {}', get_parser=float, vals=Numbers(min_value=-200e9, max_value=200e9)) self.add_parameter('freq_mode', label='Frequency mode', set_cmd='FREQ:MODE {}', get_cmd='FREQ:MODE?', vals=vals.Enum('FIX', 'CW', 'SWE', 'LIST')) self.add_parameter('pulse_width', label='Pulse width', unit='ns', set_cmd='PULM:INT:PWID {}', get_cmd='PULM:INT:PWID?', vals=Numbers(min_value=10e-9, max_value=20e-9)) self.add_parameter(name='phase', label='Phase', unit='deg', get_cmd='PHAS?', set_cmd='PHAS {}', get_parser=self.rad_to_deg, set_parser=self.deg_to_rad, vals=Numbers(min_value=-180, max_value=179)) self.add_parameter(name='power', label='Power', unit='dBm', get_cmd='POW?', set_cmd='POW {}', get_parser=float, vals=Numbers(min_value=-135, max_value=25)) self.add_parameter(name='power_offset', label='Power offset', unit='dBm', get_cmd='POW:OFFS?', set_cmd='POW:OFFS {}', get_parser=float, vals=Numbers(min_value=-200, max_value=200)) self.add_parameter(name='output_rf', get_cmd='OUTP?', set_cmd='OUTP {}', val_mapping={'OFF': 0, 'ON': 1}) self.add_parameter(name='modulation_rf', get_cmd='OUTP:MOD?', set_cmd='OUTP:MOD {}', val_mapping={'OFF': 0, 'ON': 1}) # reset values after each reconnect self.power(0) self.power_offset(0) self.connect_message() self.add_function('reset', call_cmd='*RST')
def __init__(self, name, cardid='spcm0', **kwargs): """Driver for the Spectrum M4i.44xx-x8 cards. For more information see: http://spectrum-instrumentation.com/en/m4i-platform-overview Example: Example usage for acquisition with channel 2 using an external trigger that triggers multiple times with trigger mode HIGH:: m4 = M4i(name='M4i', server_name=None) m4.enable_channels(pyspcm.CHANNEL2) m4.set_channel_settings(2,mV_range, input_path, termination, coupling, compensation) m4.set_ext0_OR_trigger_settings(pyspcm.SPC_TM_HIGH,termination,coupling,level0) calc = m4.multiple_trigger_acquisition(mV_range,memsize,seg_size,posttrigger_size) Todo: Whenever an error occurs (including validation errors) the python console needs to be restarted """ super().__init__(name, **kwargs) self.hCard = pyspcm.spcm_hOpen(cardid) if self.hCard is None: logging.warning("M4i: no card found\n") # add parameters for getting self.add_parameter('card_id', label='card id', get_cmd=None, set_cmd=None, initial_value=cardid, vals=Anything(), docstring='The card ID') self.add_parameter('max_sample_rate', label='max sample rate', unit='Hz', get_cmd=self.get_max_sample_rate, docstring='The maximumum sample rate') self.add_parameter('memory', label='memory', unit='bytes', get_cmd=self.get_card_memory, docstring='Amount of memory on card') self.add_parameter('resolution', label='resolution', unit='bits', get_cmd=partial(self._param32bit, pyspcm.SPC_MIINST_BITSPERSAMPLE), docstring='Resolution of the card') self.add_parameter('pcidate', label='pcidate', get_cmd=partial(self._param32bit, pyspcm.SPC_PCIDATE), docstring='The PCI date') self.add_parameter('serial_number', label='serial number', get_cmd=partial(self._param32bit, pyspcm.SPC_PCISERIALNO), docstring='The serial number of the board') self.add_parameter('channel_count', label='channel count', get_cmd=partial(self._param32bit, pyspcm.SPC_CHCOUNT), docstring='Return number of enabled channels') self.add_parameter('input_path_count', label='input path count', get_cmd=partial(self._param32bit, pyspcm.SPC_READAIPATHCOUNT), docstring='Return number of analog input paths') self.add_parameter( 'input_ranges_count', label='input ranges count', get_cmd=partial(self._param32bit, pyspcm.SPC_READIRCOUNT), docstring='Return number of input ranges for the current input path' ) self.add_parameter( 'input_path_features', label='input path features', get_cmd=partial(self._param32bit, pyspcm.SPC_READAIFEATURES), docstring='Return a bitmap of features for current input path') self.add_parameter('available_card_modes', label='available card modes', get_cmd=partial(self._param32bit, pyspcm.SPC_AVAILCARDMODES), docstring='Return a bitmap of available card modes') self.add_parameter( 'card_status', label='card status', get_cmd=partial(self._param32bit, pyspcm.SPC_M2STATUS), docstring='Return a bitmap for the status information') self.add_parameter( 'read_range_min_0', label='read range min 0', unit='mV', get_cmd=partial(self._param32bit, pyspcm.SPC_READRANGEMIN0), docstring='Return the lower border of input range 0') # buffer handling self.add_parameter( 'user_available_length', label='user available length', get_cmd=partial(self._param32bit, pyspcm.SPC_DATA_AVAIL_USER_LEN), docstring= 'returns the number of currently to the user available bytes inside a sample data transfer' ) self.add_parameter( 'user_available_position', label='user available position', get_cmd=partial(self._param32bit, pyspcm.SPC_DATA_AVAIL_USER_POS), docstring= 'returns the position as byte index where the currently available data samles start' ) self.add_parameter( 'buffer_fill_size', label='buffer fill size', get_cmd=partial(self._param32bit, pyspcm.SPC_FILLSIZEPROMILLE), docstring= 'returns the current fill size of the on-board memory (FIFO buffer) in promille (1/1000)' ) # triggering self.add_parameter( 'available_trigger_or_mask', label='available trigger or mask', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_AVAILORMASK), docstring= 'bitmask, in which all bits of sources for the OR mask are set, if available' ) self.add_parameter( 'available_channel_or_mask', label='available channel or mask', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_CH_AVAILORMASK0), docstring= 'bitmask, in which all bits of sources/channels (0-31) for the OR mask are set, if available' ) self.add_parameter( 'available_trigger_and_mask', label='available trigger and mask', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_AVAILANDMASK), docstring= 'bitmask, in which all bits of sources for the AND mask are set, if available' ) self.add_parameter( 'available_channel_and_mask', label='available channel and mask', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_CH_AVAILANDMASK0), docstring= 'bitmask, in which all bits of sources/channels (0-31) for the AND mask are set, if available' ) self.add_parameter( 'available_trigger_delay', label='available trigger delay', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_AVAILDELAY), docstring= 'contains the maximum available delay as decimal integer value') self.add_parameter( 'available_external_trigger_modes', label='available external trigger modes', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_EXT0_AVAILMODES), docstring= 'bitmask showing all available trigger modes for external 0 (main analog trigger input)' ) self.add_parameter('external_trigger_min_level', label='external trigger min level', unit='mV', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_EXT_AVAIL0_MIN), docstring='returns the minimum trigger level') self.add_parameter('external_trigger_max_level', label='external trigger max level', unit='mV', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_EXT_AVAIL0_MAX), docstring='returns the maximum trigger level') self.add_parameter( 'external_trigger_level_step_size', label='external trigger level step size', unit='mV', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_EXT_AVAIL0_STEP), docstring='returns the step size of the trigger level') self.add_parameter( 'available_channel_trigger_modes', label='available channel trigger modes', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_CH_AVAILMODES), docstring= 'bitmask, in which all bits of the modes for the channel trigger are set' ) self.add_parameter( 'trigger_counter', label='trigger counter', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIGGERCOUNTER), docstring= 'returns the number of triger events since acquisition start') # data per sample self.add_parameter('bytes_per_sample', label='bytes per sample', get_cmd=partial(self._param32bit, pyspcm.SPC_MIINST_BYTESPERSAMPLE), docstring='returns the number of bytes per sample') self.add_parameter('bits_per_sample', label='bits per sample', get_cmd=partial(self._param32bit, pyspcm.SPC_MIINST_BITSPERSAMPLE), docstring='returns the number of bits per sample') # available clock modes self.add_parameter( 'available_clock_modes', label='available clock modes', get_cmd=partial(self._param32bit, pyspcm.SPC_AVAILCLOCKMODES), docstring= 'returns a bitmask in which the bits of the clock modes are set, if available' ) # converting ADC samples to voltage values self.add_parameter( 'ADC_to_voltage', label='ADC to voltage', get_cmd=partial(self._param32bit, pyspcm.SPC_MIINST_MAXADCVALUE), docstring= 'contains the decimal code (in LSB) of the ADC full scale value') self.add_parameter('oversampling_factor', label='oversampling factor', get_cmd=partial(self._param32bit, pyspcm.SPC_OVERSAMPLINGFACTOR), docstring='Reads the oversampling factor') # add parameters for setting and getting (read/write direction # registers) self.add_parameter('enable_channels', label='Channels enabled', get_cmd=partial(self._param32bit, pyspcm.SPC_CHENABLE), set_cmd=partial(self._set_param32bit, pyspcm.SPC_CHENABLE), vals=Enum(1, 2, 4, 8, 3, 5, 9, 6, 10, 12, 15), docstring='Set and get enabled channels') # analog input path functions # TODO: change Enum validator to set_parser for the numbered functions # if we want string inputs self.add_parameter( 'read_input_path', label='read input path', get_cmd=partial(self._param32bit, pyspcm.SPC_READAIPATH), set_cmd=partial(self._set_param32bit, pyspcm.SPC_READAIPATH), vals=Enum(0, 1, 2, 3), docstring= 'Select the input path which is used to read out the features') for i in [0, 1, 2, 3]: self.add_parameter( 'input_path_{}'.format(i), label='input path {}'.format(i), get_cmd=partial(self._param32bit, getattr(pyspcm, 'SPC_PATH{}'.format(i))), set_cmd=partial(self._set_param32bit, getattr(pyspcm, 'SPC_PATH{}'.format(i))), vals=Enum(0, 1), docstring='Set and get analog input path for channel {}'. format(i)) # channel range functions # TODO: check the input path to set the right validator (either by # directly calling input_path_x() or by storing a variable) self.add_parameter( 'range_channel_{}'.format(i), label='range channel {}'.format(i), get_cmd=partial(self._param32bit, getattr(pyspcm, 'SPC_AMP{}'.format(i))), set_cmd=partial(self._set_param32bit, getattr(pyspcm, 'SPC_AMP{}'.format(i))), vals=Enum(200, 500, 1000, 2000, 2500, 5000, 10000), docstring='Set and get input range of channel {}'.format(i)) # input termination functions self.add_parameter( 'termination_{}'.format(i), label='termination {}'.format(i), get_cmd=partial(self._param32bit, getattr(pyspcm, 'SPC_50OHM{}'.format(i))), set_cmd=partial(self._set_param32bit, getattr(pyspcm, 'SPC_50OHM{}'.format(i))), vals=Enum(0, 1), docstring= 'if 1 sets termination to 50 Ohm, otherwise 1 MOhm for channel {}' .format(i)) # input coupling self.add_parameter( 'ACDC_coupling_{}'.format(i), label='ACDC coupling {}'.format(i), get_cmd=partial(self._param32bit, getattr(pyspcm, 'SPC_ACDC{}'.format(i))), set_cmd=partial(self._set_param32bit, getattr(pyspcm, 'SPC_ACDC{}'.format(i))), vals=Enum(0, 1), docstring= 'if 1 sets the AC coupling, otherwise sets the DC coupling for channel {}' .format(i)) # AC/DC offset compensation self.add_parameter( 'ACDC_offs_compensation_{}'.format(i), label='ACDC offs compensation {}'.format(i), get_cmd=partial(self._get_compensation, i), set_cmd=partial(self._set_compensation, i), vals=Enum(0, 1), docstring= 'if 1 enables compensation, if 0 disables compensation for channel {}' .format(i)) # anti aliasing filter (Bandwidth limit) self.add_parameter( 'anti_aliasing_filter_{}'.format(i), label='anti aliasing filter {}'.format(i), get_cmd=partial(self._param32bit, getattr(pyspcm, 'SPC_FILTER{}'.format(i))), set_cmd=partial(self._set_param32bit, getattr(pyspcm, 'SPC_FILTER{}'.format(i))), vals=Enum(0, 1), docstring= 'if 1 selects bandwidth limit, if 0 sets to full bandwidth for channel {}' .format(i)) self.add_parameter('channel_{}'.format(i), label='channel {}'.format(i), unit='a.u.', get_cmd=partial(self._read_channel, i)) # acquisition modes # TODO: If required, the other acquisition modes can be added to the # validator self.add_parameter( 'card_mode', label='card mode', get_cmd=partial(self._param32bit, pyspcm.SPC_CARDMODE), set_cmd=partial(self._set_param32bit, pyspcm.SPC_CARDMODE), vals=Enum(pyspcm.SPC_REC_STD_SINGLE, pyspcm.SPC_REC_STD_MULTI, pyspcm.SPC_REC_STD_GATE, pyspcm.SPC_REC_STD_ABA, pyspcm.SPC_REC_FIFO_SINGLE, pyspcm.SPC_REC_FIFO_MULTI, pyspcm.SPC_REC_FIFO_GATE, pyspcm.SPC_REC_FIFO_ABA, pyspcm.SPC_REC_STD_AVERAGE), docstring='defines the used operating mode') # wait command self.add_parameter('timeout', label='timeout', get_cmd=partial(self._param32bit, pyspcm.SPC_TIMEOUT), unit='ms', set_cmd=partial(self._set_param32bit, pyspcm.SPC_TIMEOUT), docstring='defines the timeout for wait commands') # Single acquisition mode memory, pre- and posttrigger (pretrigger = memory size - posttrigger) # TODO: improve the validators to make them take into account the # current state of the instrument self.add_parameter( 'data_memory_size', label='data memory size', get_cmd=partial(self._param32bit, pyspcm.SPC_MEMSIZE), set_cmd=partial(self._set_param32bit, pyspcm.SPC_MEMSIZE), vals=Numbers(min_value=16), docstring='sets the memory size in samples per channel') self.add_parameter( 'posttrigger_memory_size', label='posttrigger memory size', get_cmd=partial(self._param32bit, pyspcm.SPC_POSTTRIGGER), set_cmd=partial(self._set_param32bit, pyspcm.SPC_POSTTRIGGER), docstring= 'sets the number of samples to be recorded after trigger event') # FIFO single acquisition length and pretrigger self.add_parameter( 'pretrigger_memory_size', label='pretrigger memory size', get_cmd=partial(self._param32bit, pyspcm.SPC_PRETRIGGER), set_cmd=partial(self._set_param32bit, pyspcm.SPC_PRETRIGGER), docstring= 'sets the number of samples to be recorded before trigger event') self.add_parameter('segment_size', label='segment size', get_cmd=partial(self._param32bit, pyspcm.SPC_SEGMENTSIZE), set_cmd=partial(self._set_param32bit, pyspcm.SPC_SEGMENTSIZE), docstring='length of segments to acquire') self.add_parameter( 'total_segments', label='total segments', get_cmd=partial(self._param32bit, pyspcm.SPC_LOOPS), set_cmd=partial(self._set_param32bit, pyspcm.SPC_LOOPS), docstring= 'number of segments to acquire in total. Setting 0 makes it run until stopped by user' ) # clock generation self.add_parameter( 'clock_mode', label='clock mode', get_cmd=partial(self._param32bit, pyspcm.SPC_CLOCKMODE), set_cmd=partial(self._set_param32bit, pyspcm.SPC_CLOCKMODE), vals=Enum(pyspcm.SPC_CM_INTPLL, pyspcm.SPC_CM_QUARTZ2, pyspcm.SPC_CM_EXTREFCLOCK, pyspcm.SPC_CM_PXIREFCLOCK), docstring= 'defines the used clock mode or reads out the actual selected one') self.add_parameter( 'sample_rate', label='sample rate', get_cmd=partial(self._param32bit, pyspcm.SPC_SAMPLERATE), unit='Hz', set_cmd=partial(self._set_param32bit, pyspcm.SPC_SAMPLERATE), docstring= 'write the sample rate for internal sample generation or read rate nearest to desired' ) self.add_parameter( 'special_clock', label='special clock', get_cmd=partial(self._param32bit, pyspcm.SPC_SPECIALCLOCK), unit='Hz', set_cmd=partial(self._set_param32bit, pyspcm.SPC_SPECIALCLOCK), docstring= 'Activate/Deactivate the special clock mode (lower and more sampling clock rates)' ) # triggering self.add_parameter( 'trigger_or_mask', label='trigger or mask', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_ORMASK), set_cmd=partial(self._set_param32bit, pyspcm.SPC_TRIG_ORMASK), vals=Enum(pyspcm.SPC_TMASK_NONE, pyspcm.SPC_TMASK_SOFTWARE, pyspcm.SPC_TMASK_EXT0, pyspcm.SPC_TMASK_EXT1), docstring= 'defines the events included within the trigger OR mask card') self.add_parameter( 'channel_or_mask', label='channel or mask', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_CH_ORMASK0), set_cmd=partial(self._set_param32bit, pyspcm.SPC_TRIG_CH_ORMASK0), docstring= 'includes the channels (0-31) within the channel trigger OR mask of the card' ) self.add_parameter( 'trigger_and_mask', label='trigger and mask', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_ANDMASK), set_cmd=partial(self._set_param32bit, pyspcm.SPC_TRIG_ANDMASK), vals=Enum(pyspcm.SPC_TMASK_NONE, pyspcm.SPC_TMASK_EXT0, pyspcm.SPC_TMASK_EXT1), docstring= 'defines the events included within the trigger AND mask card') self.add_parameter( 'channel_and_mask', label='channel and mask', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_CH_ANDMASK0), set_cmd=partial(self._set_param32bit, pyspcm.SPC_TRIG_CH_ANDMASK0), docstring= 'includes the channels (0-31) within the channel trigger AND mask of the card' ) self.add_parameter( 'trigger_delay', label='trigger delay', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_DELAY), set_cmd=partial(self._set_param32bit, pyspcm.SPC_TRIG_DELAY), docstring='defines the delay for the detected trigger events') self.add_parameter( 'external_trigger_mode', label='external trigger mode', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_EXT0_MODE), set_cmd=partial(self._set_param32bit, pyspcm.SPC_TRIG_EXT0_MODE), docstring= 'defines the external trigger mode for the external SMA connector trigger input' ) self.add_parameter( 'external_trigger_termination', label='external trigger termination', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_TERM), set_cmd=partial(self._set_param32bit, pyspcm.SPC_TRIG_TERM), vals=Enum(0, 1), docstring= 'A 1 sets the 50 Ohm termination, a 0 sets high impedance termination' ) self.add_parameter( 'external_trigger_input_coupling', label='external trigger input coupling', get_cmd=partial(self._param32bit, pyspcm.SPC_TRIG_EXT0_ACDC), set_cmd=partial(self._set_param32bit, pyspcm.SPC_TRIG_EXT0_ACDC), vals=Enum(0, 1), docstring= 'A 1 sets the AC coupling for the external trigger, a 0 sets DC') for l in [0, 1]: self.add_parameter( 'external_trigger_level_{}'.format(l), label='external trigger level {}'.format(l), get_cmd=partial( self._param32bit, getattr(pyspcm, 'SPC_TRIG_EXT0_LEVEL{}'.format(l))), set_cmd=partial( self._set_param32bit, getattr(pyspcm, 'SPC_TRIG_EXT0_LEVEL{}'.format(l))), docstring='trigger level {} for external trigger'.format(l)) for i in [0, 1, 2, 3]: self.add_parameter( 'trigger_mode_channel_{}'.format(i), label='trigger mode channel {}'.format(i), get_cmd=partial( self._param32bit, getattr(pyspcm, 'SPC_TRIG_CH{}_MODE'.format(i))), set_cmd=partial( self._set_param32bit, getattr(pyspcm, 'SPC_TRIG_CH{}_MODE'.format(i))), docstring='sets the trigger mode for channel {}'.format(i)) for l in [0, 1]: self.add_parameter( 'trigger_channel_{}_level_{}'.format(i, l), label='trigger channel {} level {}'.format(i, l), get_cmd=partial( self._param32bit, getattr(pyspcm, 'SPC_TRIG_CH{}_LEVEL{}'.format(i, l))), set_cmd=partial( self._set_param32bit, getattr(pyspcm, 'SPC_TRIG_CH{}_LEVEL{}'.format(i, l))), docstring='trigger level {} channel {}'.format(l, i)) # add parameters for setting (write only registers) # Buffer handling self.add_parameter( 'card_available_length', label='card available length', set_cmd=partial(self._set_param32bit, pyspcm.SPC_DATA_AVAIL_CARD_LEN), docstring= 'writes the number of bytes that the card can now use for sample data transfer again' ) # General self.add_parameter( 'general_command', label='general command', set_cmd=partial(self._set_param32bit, pyspcm.SPC_M2CMD), docstring='executes a command for the card or data transfer') # memsize used for simple channel read-out self._channel_memsize = 2**12
def __init__(self, name: str, address: str, terminator: str="\n", **kwargs) -> 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')) # 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 # type: Optional[Union[float,int]] 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)) # The driver is initialized in the volt mode. In this mode we cannot # get 'current_range'. Hence the snapshot is excluded. 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=True ) # 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") ) # Again, at init we are in "VOLT" mode. Hence, exclude the snapshot of # 'current' as instrument does not support this parameter in # "VOLT" mode. 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=True ) # 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()
def test_max(self): for max_val in [-1e20, -1, -0.1, 0, 0.1, 10]: n = Numbers(max_value=max_val) n.validate(n.valid_values[0]) for v in self.numbers: if v <= max_val: n.validate(v) else: with self.assertRaises(ValueError): n.validate(v) for v in self.not_numbers: with self.assertRaises(TypeError): n.validate(v) with self.assertRaises(ValueError): n.validate(float('nan'))
def __init__(self, name: str, address: str, timeout: float = 20, **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. """ super().__init__(name, address, timeout=timeout, terminator='\n', **kwargs) self.connect_message() # Scope trace boolean self.trace_ready = False # switch the response header off, # else none of our parameters will work self.write(':SYSTem:HEADer OFF') # functions # general parameters # the parameters are in the same order as the front panel. # Beware, he list of implemented parameters is not complete. Refer to # the manual (Infiniium prog guide) for an equally infiniium list. # time base # timebase_scale is commented out for same reason as channel scale # use range instead # self.add_parameter('timebase_scale', # label='Scale of the one time devision', # unit='s/Div', # get_cmd=':TIMebase:SCALe?', # set_cmd=':TIMebase:SCALe {}', # vals=Numbers(), # get_parser=float, # ) self.add_parameter('timebase_range', label='Range of the time axis', unit='s', get_cmd=':TIMebase:RANGe?', set_cmd=':TIMebase:RANGe {}', vals=Numbers(5e-12, 20), get_parser=float, ) self.add_parameter('timebase_position', label='Offset of the time axis', unit='s', get_cmd=':TIMebase:POSition?', set_cmd=':TIMebase:POSition {}', vals=Numbers(), get_parser=float, ) self.add_parameter('timebase_roll_enabled', label='Is rolling mode enabled', get_cmd=':TIMebase:ROLL:ENABLE?', set_cmd=':TIMebase:ROLL:ENABLE {}', val_mapping={True: 1, False: 0} ) # trigger self.add_parameter('trigger_enabled', label='Is trigger enabled', get_cmd=':TRIGger:AND:ENABLe?', set_cmd=':TRIGger:AND:ENABLe {}', val_mapping={True: 1, False: 0} ) self.add_parameter('trigger_edge_source', label='Source channel for the edge trigger', get_cmd=':TRIGger:EDGE:SOURce?', set_cmd=':TRIGger:EDGE:SOURce {}', vals=Enum(*( [f'CHANnel{i}' for i in range(1, 4 + 1)] + [f'CHAN{i}' for i in range(1, 4 + 1)] + [f'DIGital{i}' for i in range(16 + 1)] + [f'DIG{i}' for i in range(16 + 1)] + ['AUX', 'LINE'])) ) # add enum for case insesitivity self.add_parameter('trigger_edge_slope', label='slope of the edge trigger', get_cmd=':TRIGger:EDGE:SLOPe?', set_cmd=':TRIGger:EDGE:SLOPe {}', vals=Enum('positive', 'negative', 'neither') ) self.add_parameter('trigger_level_aux', label='Tirgger level AUX', unit='V', get_cmd=':TRIGger:LEVel? AUX', set_cmd=':TRIGger:LEVel AUX,{}', get_parser=float, 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.add_parameter('acquire_points', label='sample points', get_cmd='ACQ:POIN?', get_parser=int, set_cmd=self._cmd_and_invalidate('ACQ:POIN {}'), unit='pts', vals=vals.Numbers(min_value=1, max_value=100e6) ) self.add_parameter('acquire_sample_rate', label='sample rate', get_cmd='ACQ:SRAT?', set_cmd=self._cmd_and_invalidate('ACQ:SRAT {}'), unit='Sa/s', get_parser=float ) # this parameter gets used internally for data aquisition. For now it # should not be used manually self.add_parameter( "data_source", label="Waveform Data source", get_cmd=":WAVeform:SOURce?", set_cmd=":WAVeform:SOURce {}", vals=Enum( *( [f"CHANnel{i}" for i in range(1, 4 + 1)] + [f"CHAN{i}" for i in range(1, 4 + 1)] + [f"DIFF{i}" for i in range(1, 2 + 1)] + [f"COMMonmode{i}" for i in range(3, 4 + 1)] + [f"COMM{i}" for i in range(3, 4 + 1)] + [f"FUNCtion{i}" for i in range(1, 16 + 1)] + [f"FUNC{i}" for i in range(1, 16 + 1)] + [f"WMEMory{i}" for i in range(1, 4 + 1)] + [f"WMEM{i}" for i in range(1, 4 + 1)] + [f"BUS{i}" for i in range(1, 4 + 1)] + ["HISTogram", "HIST", "CLOCK"] + ["MTRend", "MTR"] ) ), ) # TODO: implement as array parameter to allow for setting the other filter # ratios self.add_parameter('acquire_interpolate', get_cmd=':ACQuire:INTerpolate?', set_cmd=self._cmd_and_invalidate(':ACQuire:INTerpolate {}'), val_mapping={True: 1, False: 0} ) self.add_parameter('acquire_mode', label='Acquisition mode', get_cmd= 'ACQuire:MODE?', set_cmd='ACQuire:MODE {}', vals=Enum('ETIMe', 'RTIMe', 'PDETect', 'HRESolution', 'SEGMented', 'SEGPdetect', 'SEGHres') ) self.add_parameter('acquire_timespan', get_cmd=(lambda: self.acquire_points.get_latest() \ /self.acquire_sample_rate.get_latest()), unit='s', get_parser=float ) # time of the first point self.add_parameter('waveform_xorigin', get_cmd='WAVeform:XORigin?', unit='s', get_parser=float ) self.add_parameter('data_format', set_cmd='SAV:WAV:FORM {}', val_mapping={'csv': 'CSV', 'binary': 'BIN', 'asciixy': 'ASC'}, docstring=("Set the format for saving " "files using save_data function") ) # Channels channels = ChannelList(self, "Channels", InfiniiumChannel, snapshotable=False) for i in range(1,5): 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()) # Submodules meassubsys = MeasurementSubsystem(self, 'measure') self.add_submodule('measure', meassubsys)
def __init__(self, name: str, ats, sgen1, sgen2, aeroflex, **kwargs): super().__init__(name, **kwargs) self.sgen1 = sgen1 self.sgen2 = sgen2 self.aeroflex = aeroflex self.aeroflex_CH_map = {'In': 'CH1', 'Out': 'CH2'} self.ats = ats self.delta_frequency = ats.delta_frequency self.rangeCHA = self.ats.channel_range1.get() self.rangeCHB = self.ats.channel_range2.get() self.add_parameter(name='IQ', iqmixer=self, parameter_class=IQPair) self.add_parameter(name='AmPh', iqmixer=self, parameter_class=AmPhPair) self.add_parameter( name='frequency', label='fprobe', unit='Hz', get_cmd=self.sgen1.frequency.get, # get freq in MHz set_cmd=self.set_IQfrequency, #set freq in range F1 in GHz vals=Numbers(min_value=1e3, max_value=40e9)) self.add_parameter( name='S21', label='S21', # unit='Hz', get_cmd=self.get_S21, set_cmd=None) self.add_parameter(name='S21_ampl', label='Amplitude', unit='V', get_cmd=self.get_S21ampl, set_cmd=None) self.add_parameter(name='S21_phase', label='Phase', unit='rad', get_cmd=self.get_S21phase, set_cmd=None) self.add_parameter(name='S21_I', label='I', unit='V', get_cmd=self.get_S21I, set_cmd=None) self.add_parameter(name='S21_Q', label='Q', unit='V', get_cmd=self.get_S21Q, set_cmd=None) self.add_parameter(name='P_dBm', label='ADC power', unit='dBm', get_cmd=self.get_PdBm, set_cmd=None) self.add_parameter(name='S21_dB', label='S21', unit='dB', get_cmd=self.get_S21dB, set_cmd=None) for io, ch in self.aeroflex_CH_map.items(): self.add_parameter( 'att{}'.format(io), label='att {}'.format(io), unit='dB', get_cmd=getattr(self.aeroflex, 'attenuation{}'.format(ch)).get, # set_cmd=getattr(self.aeroflex, 'attenuation{}'.format(ch)).set, # vals=vals.Enum(*np.arange(0, 100.1, 2).tolist()))
def __init__( self, name: str, address: str, port: Optional[int] = None, reset: bool = False, terminator: str = '\r\n', current_ramp_limit: Optional[float] = None, **kwargs: Any): if "has_current_rating" in kwargs.keys(): warnings.warn( "'has_current_rating' kwarg to AMI430 " "is deprecated and has no effect", category=QCoDeSDeprecationWarning) kwargs.pop("has_current_rating") super().__init__(name, address, port, terminator=terminator, write_confirmation=False, **kwargs) self._parent_instrument = None # Add reset function self.add_function('reset', call_cmd='*RST') if reset: self.reset() # Add parameters setting instrument units self.add_parameter("ramp_rate_units", get_cmd='RAMP:RATE:UNITS?', set_cmd=(lambda units: self._update_units(ramp_rate_units=units)), val_mapping={'seconds': 0, 'minutes': 1}) self.add_parameter('field_units', get_cmd='FIELD:UNITS?', set_cmd=(lambda units: self._update_units(field_units=units)), val_mapping={'kilogauss': 0, 'tesla': 1}) # Set programmatic safety limits self.add_parameter('current_ramp_limit', get_cmd=lambda: self._current_ramp_limit, set_cmd=self._update_ramp_rate_limit, unit="A/s") self.add_parameter('field_ramp_limit', get_cmd=lambda: self.current_ramp_limit(), set_cmd=lambda x: self.current_ramp_limit(x), scale=1/float(self.ask("COIL?")), unit="T/s") if current_ramp_limit is None: self._update_ramp_rate_limit(AMI430._DEFAULT_CURRENT_RAMP_LIMIT, update=False) else: self._update_ramp_rate_limit(current_ramp_limit, update=False) # Add solenoid parameters self.add_parameter('coil_constant', get_cmd=self._update_coil_constant, set_cmd=self._update_coil_constant, vals=Numbers(0.001, 999.99999)) self.add_parameter('current_limit', unit="A", set_cmd="CONF:CURR:LIMIT {}", get_cmd='CURR:LIMIT?', get_parser=float, vals=Numbers(0, 80)) # what are good numbers here? self.add_parameter('field_limit', set_cmd=self.current_limit.set, get_cmd=self.current_limit.get, scale=1/float(self.ask("COIL?"))) # Add current solenoid parameters # Note that field is validated in set_field self.add_parameter('field', get_cmd='FIELD:MAG?', get_parser=float, set_cmd=self.set_field) self.add_parameter('ramp_rate', get_cmd=self._get_ramp_rate, set_cmd=self._set_ramp_rate) self.add_parameter('setpoint', get_cmd='FIELD:TARG?', get_parser=float) self.add_parameter('is_quenched', get_cmd='QU?', val_mapping={True: 1, False: 0}) self.add_function('reset_quench', call_cmd='QU 0') self.add_function('set_quenched', call_cmd='QU 1') self.add_parameter('ramping_state', get_cmd='STATE?', get_parser=int, val_mapping={ 'ramping': 1, 'holding': 2, 'paused': 3, 'manual up': 4, 'manual down': 5, 'zeroing current': 6, 'quench detected': 7, 'at zero current': 8, 'heating switch': 9, 'cooling switch': 10, }) # Add persistent switch switch_heater = AMI430SwitchHeater(self) self.add_submodule("switch_heater", switch_heater) # Add interaction functions self.add_function('get_error', call_cmd='SYST:ERR?') self.add_function('ramp', call_cmd='RAMP') self.add_function('pause', call_cmd='PAUSE') self.add_function('zero', call_cmd='ZERO') # Correctly assign all units self._update_units() self.connect_message()
def __init__(self, name: str, serial_number: str, dll=None, debug=False, **kwargs: Any): super().__init__(name, **kwargs) logging.info( __name__ + f' : Initializing instrument SignalCore generator {serial_number}') if dll is not None: self._dll = dll else: self._dll = ctypes.CDLL(DLL.__path__._path[0] + '//sc5511a.dll') if debug: print(self._dll) self._dll.sc5511a_open_device.restype = ctypes.c_uint64 self._handle = ctypes.c_void_p( self._dll.sc5511a_open_device( ctypes.c_char_p(bytes(serial_number, 'utf-8')))) self._serial_number = ctypes.c_char_p(bytes(serial_number, 'utf-8')) self._rf_params = Device_rf_params_t(0, 0, 0, 0, 0, 0, 0, 0, 0) self._status = Operate_status_t(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) self._open = False self._temperature = Device_temperature_t(0) self._pll_status = Pll_status_t() self._list_mode = List_mode_t() self._device_status = Device_status_t(self._list_mode, self._status, self._pll_status) if debug: print(serial_number, self._handle) self._dll.sc5511a_get_device_status( self._handle, ctypes.byref(self._device_status)) status = self._device_status.operate_status_t.rf1_out_enable print('check status', status) self._dll.sc5511a_close_device(self._handle) self._device_info = Device_info_t(0, 0, 0, 0) self.get_idn() self.do_set_auto_level_disable( 0) # setting this to 1 will lead to unstable output power self.add_parameter('power', label='power', get_cmd=self.do_get_power, get_parser=float, set_cmd=self.do_set_power, set_parser=float, unit='dBm', vals=Numbers(min_value=-144, max_value=19)) self.add_parameter('output_status', label='output_status', get_cmd=self.do_get_output_status, get_parser=int, set_cmd=self.do_set_output_status, set_parser=int, vals=Numbers(min_value=0, max_value=1)) self.add_parameter('frequency', label='frequency', get_cmd=self.do_get_frequency, get_parser=float, set_cmd=self.do_set_frequency, set_parser=float, unit='Hz', vals=Numbers(min_value=0, max_value=20e9)) self.add_parameter('reference_source', label='reference_source', get_cmd=self.do_get_reference_source, get_parser=int, set_cmd=self.do_set_reference_source, set_parser=int, vals=Numbers(min_value=0, max_value=1)) self.add_parameter('auto_level_disable', label='0 = power is leveled on frequency change', get_cmd=self.do_get_auto_level_disable, get_parser=int, set_cmd=self.do_set_auto_level_disable, set_parser=int, vals=Numbers(min_value=0, max_value=1)) self.add_parameter('temperature', label='temperature', get_cmd=self.do_get_device_temp, get_parser=float, unit="C", vals=Numbers(min_value=0, max_value=200)) if self._device_status.operate_status_t.ext_ref_lock_enable == 0: self.do_set_reference_source(1)