def __init__(self, name, testing=False, **kwargs): self._t0 = time.time() if kwargs.pop('server_name', False): warnings.warn("server_name argument not supported any more", stacklevel=0) super().__init__(name, testing=testing, **kwargs) self.add_parameter('IDN', get_cmd=self.get_idn, vals=Anything()) self._meta_attrs = ['name'] self.record_instance(self)
def __init__(self, name: str, metadata: Optional[Dict]=None, **kwargs) -> None: self._t0 = time.time() if kwargs.pop('server_name', False): warnings.warn("server_name argument not supported any more", stacklevel=0) super().__init__(name, **kwargs) self.add_parameter('IDN', get_cmd=self.get_idn, vals=Anything()) self.record_instance(self)
def __init__( self, name: str, metadata: Optional[Mapping[Any, Any]] = None ) -> None: self._t0 = time.time() super().__init__(name, metadata) self.add_parameter('IDN', get_cmd=self.get_idn, vals=Anything())
def __init__(self, name, server_name=None, **kwargs): self._t0 = time.time() super().__init__(**kwargs) self.parameters = {} self.functions = {} self.name = str(name) self.add_parameter('IDN', get_cmd=self.get_idn, vals=Anything()) self._meta_attrs = ['name'] self._no_proxy_methods = {'__getstate__'}
def test_real_anything(): a = Anything() for v in [ None, 0, 1, 0.0, 1.2, '', 'hi!', [1, 2, 3], [], { 'a': 1, 'b': 2 }, {}, {1, 2, 3}, a, range(10), True, False, float("nan"), float("inf"), b'good', AClass, AClass(), a_func ]: a.validate(v) assert repr(a) == '<Anything>'
def test_real_anything(self): a = Anything() for v in [ None, 0, 1, 0.0, 1.2, '', 'hi!', [1, 2, 3], [], { 'a': 1, 'b': 2 }, {}, set([1, 2, 3]), a, range(10), True, False, float("nan"), float("inf"), b'good', AClass, AClass(), a_func ]: a.validate(v) self.assertEqual(repr(a), '<Anything>')
def test_failed_anything(self): with self.assertRaises(TypeError): Anything(1) with self.assertRaises(TypeError): Anything(values=[1, 2, 3])
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), unit='mV', docstring='Set and get input range of channel {} (in mV)'.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, reset: bool = False, **kwargs: Any): 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] elif model is None: raise KeyError('Could not determine model') 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 {}', unit='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 {}', unit='Ohm', val_mapping={ 50: '50', 1e6: '1M' }) self.add_parameter('counter_trigger_level', get_cmd='COUN:LEVE?', get_parser=float, set_cmd='COUN:LEVE {}', unit='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(f'counter_{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 {}', unit='%', vals=Numbers(min_value=0, max_value=100)) # Output and Source parameters for both channel 1 and 2 for i in [1, 2]: ch = f'ch{i}_' output = f'OUTP{i}:' source = f'SOUR{i}:' self.add_parameter(ch + 'output_impedance', get_cmd=output + 'IMP?', get_parser=parse_string_output, set_cmd=output + 'IMP {}', unit='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', unit='%', 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 {}', unit='s', vals=Numbers(1e-6)) self.add_parameter(ch + 'burst_phase', get_cmd=source + 'BURS:PHAS?', get_parser=float, set_cmd=source + 'BURS:PHAS {}', unit='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 {}', unit='Hz', vals=Numbers(1e-6)) self.add_parameter(ch + 'frequency', get_cmd=source + 'FREQ?', get_parser=float, set_cmd=source + 'FREQ {}', unit='Hz', vals=Numbers(1e-6)) self.add_parameter(ch + 'frequency_start', get_cmd=source + 'FREQ:STAR?', get_parser=float, set_cmd=source + 'FREQ:STAR {}', unit='Hz', vals=Numbers(1e-6)) self.add_parameter(ch + 'frequency_stop', get_cmd=source + 'FREQ:STOP?', get_parser=float, set_cmd=source + 'FREQ:STOP {}', unit='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 {}', unit='%', 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 {}', unit='%', 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 {}', unit='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 {}', unit='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 {}', unit='%', vals=Numbers(0, 100)) self.add_parameter(ch + 'pulse_delay', get_cmd=source + 'PULS:DEL?', get_parser=float, set_cmd=source + 'PULS:DEL {}', unit='s', vals=Numbers(0)) self.add_parameter(ch + 'pulse_hold', get_cmd=source + 'PULS:HOLD?', set_cmd=source + 'PULS:HOLD {}', unit='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 {}', unit='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 {}', unit='s', vals=Numbers(0)) self.add_parameter(ch + 'pulse_width', get_cmd=source + 'PULS:WIDT?', get_parser=float, set_cmd=source + 'PULS:WIDT {}', unit='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 {}', unit='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 {}', unit='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 {}', unit='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 {}', unit='s', vals=Numbers(1e-3, 300)) # Source Voltage self.add_parameter(ch + 'amplitude', get_cmd=source + 'VOLT?', get_parser=float, set_cmd=source + 'VOLT {}', unit='V', vals=Numbers()) self.add_parameter(ch + 'offset', get_cmd=source + 'VOLT:OFFS?', get_parser=float, set_cmd=source + 'VOLT:OFFS {}', unit='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, address=None, port=None, axes=None, **kwargs): super().__init__(name, address=address, port=port, terminator='\n', **kwargs) self.axes = axes self._ATOB = [] self._latest_response = '' # for some reason the first call is always invalid?! # need some kind of init? self.ask('*IDN?') if axes is None: self._determine_magnet_axes() self._determine_current_to_field() self.add_parameter( 'setpoint', names=['B' + ax.lower() + '_setpoint' for ax in self.axes], get_cmd=partial(self._get_fld, self.axes, 'FSET'), set_cmd=partial(self._ramp_to_setpoint, self.axes, 'FSET'), units=['T' for ax in self.axes], vals=Anything()) self.add_parameter('rate', names=['rate_B' + ax.lower() for ax in self.axes], get_cmd=partial(self._get_fld, self.axes, 'RFST'), set_cmd=partial(self._ramp_to_setpoint, self.axes, 'RFST'), units=['T/m' for ax in self.axes], vals=Anything()) self.add_parameter('fld', names=['B' + ax.lower() for ax in self.axes], get_cmd=partial(self._get_fld, self.axes, 'FLD'), set_cmd=partial(self._ramp_to_setpoint, self.axes, 'FSET'), units=['T' for ax in self.axes], vals=Anything()) self.add_parameter('fldC', names=['B' + ax.lower() for ax in self.axes], get_cmd=partial(self._get_fld, self.axes, 'CURR'), set_cmd=partial(self._ramp_to_setpoint, self.axes, 'CSET'), units=['T' for ax in self.axes], vals=Anything()) self.add_parameter('rtp', names=['radius', 'theta', 'phi'], get_cmd=partial(self._get_rtp, self.axes, 'FLD'), set_cmd=partial(self._set_rtp, self.axes, 'FSET'), units=['|B|', 'rad', 'rad'], vals=Anything()) self.add_parameter('rtpC', names=['radius', 'theta', 'phi'], get_cmd=partial(self._get_rtp, self.axes, 'CURR'), set_cmd=partial(self._set_rtp, self.axes, 'CSET'), units=['|B|', 'rad', 'rad'], vals=Anything()) # so we have radius, theta and phi in buffer self.rtp.get() self.add_parameter('radius', get_cmd=self._get_r, set_cmd=self._set_r, units='|B|') self.add_parameter('theta', get_cmd=self._get_theta, set_cmd=self._set_theta, units='rad') self.add_parameter('phi', get_cmd=self._get_phi, set_cmd=self._set_phi, units='rad') for ax in self.axes: self.add_parameter(ax.lower() + '_fld', get_cmd=partial(self._get_fld, ax, 'FLD'), set_cmd=partial(self._ramp_to_setpoint, ax, 'FSET'), label='B' + ax.lower(), units='T') self.add_parameter(ax.lower() + '_fldC', get_cmd=partial(self._get_fld, ax, 'CURR'), set_cmd=partial(self._ramp_to_setpoint, ax, 'CSET'), label='B' + ax.lower(), units='T') self.add_parameter(ax.lower() + '_fld_wait', get_cmd=partial(self._get_fld, ax, 'CURR'), set_cmd=partial(self._ramp_to_setpoint_and_wait, ax, 'CSET'), label='B' + ax.lower(), units='T') self.add_parameter(ax.lower() + '_ACTN', get_cmd=partial( self._get_cmd, 'READ:DEV:GRP' + ax + ':PSU:ACTN?'), set_cmd='SET:DEV:GRP' + ax + ':PSU:ACTN:{}', vals=Enum('HOLD', 'RTOS', 'RTOZ', 'CLMP')) self.add_parameter(ax.lower() + '_setpoint', get_cmd=partial(self._get_fld, ax, 'FSET'), set_cmd=partial(self._set_fld, ax, 'FSET'), units='T') self.add_parameter(ax.lower() + '_setpointC', get_cmd=partial(self._get_fld, ax, 'CSET'), set_cmd=partial(self._set_fld, ax, 'CSET'), units='T') self.add_parameter(ax.lower() + '_rate', get_cmd=partial(self._get_fld, ax, 'RFST'), set_cmd=partial(self._set_fld, ax, 'RFST'), units='T/m') self.add_parameter(ax.lower() + '_rateC', get_cmd=partial(self._get_fld, ax, 'RCST'), set_cmd=partial(self._set_fld, ax, 'RCST'), units='T/m') self.connect_message()
def test_failed_anything(): with pytest.raises(TypeError): Anything(1) with pytest.raises(TypeError): Anything(values=[1, 2, 3])
def __init__(self, name: str, instrument_x: Union[AMI430, str], instrument_y: Union[AMI430, str], instrument_z: Union[AMI430, str], field_limit: Union[numbers.Real, Iterable[CartesianFieldLimitFunction]], **kwargs: Any): """ Driver for controlling three American Magnetics Model 430 magnet power supplies simultaneously for setting magnetic field vectors. The individual magnet power supplies can be passed in as either instances of AMI430 driver or as names of existing AMI430 instances. In the latter case, the instances will be found via the passed names. Args: name: a name for the instrument instrument_x: AMI430 instance or a names of existing AMI430 instance for controlling the X axis of magnetic field instrument_y: AMI430 instance or a names of existing AMI430 instance for controlling the Y axis of magnetic field instrument_z: AMI430 instance or a names of existing AMI430 instance for controlling the Z axis of magnetic field field_limit: a number for maximum allows magnetic field or an iterable of callable field limit functions that define region(s) of allowed values in 3D magnetic field space """ super().__init__(name, **kwargs) if not isinstance(name, str): raise ValueError("Name should be a string") for instrument, arg_name in zip( (instrument_x, instrument_y, instrument_z), ("instrument_x", "instrument_y", "instrument_z"), ): if not isinstance(instrument, (AMI430, str)): raise ValueError( f"Instruments need to be instances of the class AMI430 " f"or be valid names of already instantiated instances " f"of AMI430 class; {arg_name} argument is " f"neither of those" ) def find_ami430_with_name(ami430_name: str) -> AMI430: found_ami430 = AMI430.find_instrument( name=ami430_name, instrument_class=AMI430 ) return cast(AMI430, found_ami430) self._instrument_x = ( instrument_x if isinstance(instrument_x, AMI430) else find_ami430_with_name(instrument_x) ) self._instrument_y = ( instrument_y if isinstance(instrument_y, AMI430) else find_ami430_with_name(instrument_y) ) self._instrument_z = ( instrument_z if isinstance(instrument_z, AMI430) else find_ami430_with_name(instrument_z) ) self._field_limit: Union[float, Iterable[CartesianFieldLimitFunction]] if isinstance(field_limit, collections.abc.Iterable): self._field_limit = field_limit elif isinstance(field_limit, numbers.Real): # Conversion to float makes related driver logic simpler self._field_limit = float(field_limit) else: raise ValueError("field limit should either be a number or " "an iterable of callable field limit functions.") self._set_point = FieldVector( x=self._instrument_x.field(), y=self._instrument_y.field(), z=self._instrument_z.field() ) # Get-only parameters that return a measured value self.add_parameter( 'cartesian_measured', get_cmd=partial(self._get_measured, 'x', 'y', 'z'), unit='T' ) self.add_parameter( 'x_measured', get_cmd=partial(self._get_measured, 'x'), unit='T' ) self.add_parameter( 'y_measured', get_cmd=partial(self._get_measured, 'y'), unit='T' ) self.add_parameter( 'z_measured', get_cmd=partial(self._get_measured, 'z'), unit='T' ) self.add_parameter( 'spherical_measured', get_cmd=partial( self._get_measured, 'r', 'theta', 'phi' ), unit='T' ) self.add_parameter( 'phi_measured', get_cmd=partial(self._get_measured, 'phi'), unit='deg' ) self.add_parameter( 'theta_measured', get_cmd=partial(self._get_measured, 'theta'), unit='deg' ) self.add_parameter( 'field_measured', get_cmd=partial(self._get_measured, 'r'), unit='T') self.add_parameter( 'cylindrical_measured', get_cmd=partial(self._get_measured, 'rho', 'phi', 'z'), unit='T') self.add_parameter( 'rho_measured', get_cmd=partial(self._get_measured, 'rho'), unit='T' ) # Get and set parameters for the set points of the coordinates self.add_parameter( 'cartesian', get_cmd=partial(self._get_setpoints, ('x', 'y', 'z')), set_cmd=partial(self._set_setpoints, ('x', 'y', 'z')), unit='T', vals=Anything() ) self.add_parameter( 'x', get_cmd=partial(self._get_setpoints, ('x',)), set_cmd=partial(self._set_setpoints, ('x',)), unit='T', vals=Numbers() ) self.add_parameter( 'y', get_cmd=partial(self._get_setpoints, ('y',)), set_cmd=partial(self._set_setpoints, ('y',)), unit='T', vals=Numbers() ) self.add_parameter( 'z', get_cmd=partial(self._get_setpoints, ('z',)), set_cmd=partial(self._set_setpoints, ('z',)), unit='T', vals=Numbers() ) self.add_parameter( 'spherical', get_cmd=partial( self._get_setpoints, ('r', 'theta', 'phi') ), set_cmd=partial( self._set_setpoints, ('r', 'theta', 'phi') ), unit='tuple?', vals=Anything() ) self.add_parameter( 'phi', get_cmd=partial(self._get_setpoints, ('phi',)), set_cmd=partial(self._set_setpoints, ('phi',)), unit='deg', vals=Numbers() ) self.add_parameter( 'theta', get_cmd=partial(self._get_setpoints, ('theta',)), set_cmd=partial(self._set_setpoints, ('theta',)), unit='deg', vals=Numbers() ) self.add_parameter( 'field', get_cmd=partial(self._get_setpoints, ('r',)), set_cmd=partial(self._set_setpoints, ('r',)), unit='T', vals=Numbers() ) self.add_parameter( 'cylindrical', get_cmd=partial( self._get_setpoints, ('rho', 'phi', 'z') ), set_cmd=partial( self._set_setpoints, ('rho', 'phi', 'z') ), unit='tuple?', vals=Anything() ) self.add_parameter( 'rho', get_cmd=partial(self._get_setpoints, ('rho',)), set_cmd=partial(self._set_setpoints, ('rho',)), unit='T', vals=Numbers() ) self.add_parameter( 'block_during_ramp', set_cmd=None, initial_value=True, unit='', vals=Bool() )
def __init__(self, name: str, instrument_x: Union[AMI430, str], instrument_y: Union[AMI430, str], instrument_z: Union[AMI430, str], field_limit: Union[numbers.Real, Iterable[CartesianFieldLimitFunction]], **kwargs: Any): super().__init__(name, **kwargs) if not isinstance(name, str): raise ValueError("Name should be a string") for instrument, arg_name in zip( (instrument_x, instrument_y, instrument_z), ("instrument_x", "instrument_y", "instrument_z"), ): if not isinstance(instrument, (AMI430, str)): raise ValueError( f"Instruments need to be instances of the class AMI430 " f"or be valid names of already instantiated instances " f"of AMI430 class; {arg_name} argument is " f"neither of those") def find_ami430_with_name(ami430_name: str) -> AMI430: found_ami430 = AMI430.find_instrument(name=ami430_name, instrument_class=AMI430) return cast(AMI430, found_ami430) self._instrument_x = (instrument_x if isinstance(instrument_x, AMI430) else find_ami430_with_name(instrument_x)) self._instrument_y = (instrument_y if isinstance(instrument_y, AMI430) else find_ami430_with_name(instrument_y)) self._instrument_z = (instrument_z if isinstance(instrument_z, AMI430) else find_ami430_with_name(instrument_z)) self._field_limit: Union[float, Iterable[CartesianFieldLimitFunction]] if isinstance(field_limit, collections.abc.Iterable): self._field_limit = field_limit elif isinstance(field_limit, numbers.Real): # Conversion to float makes related driver logic simpler self._field_limit = float(field_limit) else: raise ValueError("field limit should either be a number or " "an iterable of callable field limit functions.") self._set_point = FieldVector(x=self._instrument_x.field(), y=self._instrument_y.field(), z=self._instrument_z.field()) # Get-only parameters that return a measured value self.add_parameter('cartesian_measured', get_cmd=partial(self._get_measured, 'x', 'y', 'z'), unit='T') self.add_parameter('x_measured', get_cmd=partial(self._get_measured, 'x'), unit='T') self.add_parameter('y_measured', get_cmd=partial(self._get_measured, 'y'), unit='T') self.add_parameter('z_measured', get_cmd=partial(self._get_measured, 'z'), unit='T') self.add_parameter('spherical_measured', get_cmd=partial(self._get_measured, 'r', 'theta', 'phi'), unit='T') self.add_parameter('phi_measured', get_cmd=partial(self._get_measured, 'phi'), unit='deg') self.add_parameter('theta_measured', get_cmd=partial(self._get_measured, 'theta'), unit='deg') self.add_parameter('field_measured', get_cmd=partial(self._get_measured, 'r'), unit='T') self.add_parameter('cylindrical_measured', get_cmd=partial(self._get_measured, 'rho', 'phi', 'z'), unit='T') self.add_parameter('rho_measured', get_cmd=partial(self._get_measured, 'rho'), unit='T') # Get and set parameters for the set points of the coordinates self.add_parameter('cartesian', get_cmd=partial(self._get_setpoints, ('x', 'y', 'z')), set_cmd=partial(self._set_setpoints, ('x', 'y', 'z')), unit='T', vals=Anything()) self.add_parameter('x', get_cmd=partial(self._get_setpoints, ('x', )), set_cmd=partial(self._set_setpoints, ('x', )), unit='T', vals=Numbers()) self.add_parameter('y', get_cmd=partial(self._get_setpoints, ('y', )), set_cmd=partial(self._set_setpoints, ('y', )), unit='T', vals=Numbers()) self.add_parameter('z', get_cmd=partial(self._get_setpoints, ('z', )), set_cmd=partial(self._set_setpoints, ('z', )), unit='T', vals=Numbers()) self.add_parameter('spherical', get_cmd=partial(self._get_setpoints, ('r', 'theta', 'phi')), set_cmd=partial(self._set_setpoints, ('r', 'theta', 'phi')), unit='tuple?', vals=Anything()) self.add_parameter('phi', get_cmd=partial(self._get_setpoints, ('phi', )), set_cmd=partial(self._set_setpoints, ('phi', )), unit='deg', vals=Numbers()) self.add_parameter('theta', get_cmd=partial(self._get_setpoints, ('theta', )), set_cmd=partial(self._set_setpoints, ('theta', )), unit='deg', vals=Numbers()) self.add_parameter('field', get_cmd=partial(self._get_setpoints, ('r', )), set_cmd=partial(self._set_setpoints, ('r', )), unit='T', vals=Numbers()) self.add_parameter('cylindrical', get_cmd=partial(self._get_setpoints, ('rho', 'phi', 'z')), set_cmd=partial(self._set_setpoints, ('rho', 'phi', 'z')), unit='tuple?', vals=Anything()) self.add_parameter('rho', get_cmd=partial(self._get_setpoints, ('rho', )), set_cmd=partial(self._set_setpoints, ('rho', )), unit='T', vals=Numbers()) self.add_parameter('block_during_ramp', set_cmd=None, initial_value=True, unit='', vals=Bool())
def __init__(self, name=None, names=None, label=None, labels=None, units=None, shape=None, shapes=None, setpoints=None, setpoint_names=None, setpoint_labels=None, vals=None, docstring=None, snapshot_get=True, **kwargs): super().__init__(**kwargs) self._snapshot_get = snapshot_get self.has_get = hasattr(self, 'get') self.has_set = hasattr(self, 'set') self._meta_attrs = ['setpoint_names', 'setpoint_labels'] # always let the parameter have a single name (in fact, require this!) # even if it has names too self.name = str(name) if names is not None: # check for names first - that way you can provide both name # AND names for instrument parameters - name is how you get the # object (from the parameters dict or the delegated attributes), # and names are the items it returns self.names = names self.labels = names if labels is None else names self.units = units if units is not None else [''] * len(names) self.set_validator(vals or Anything()) self.__doc__ = os.linesep.join( ('Parameter class:', '* `names` %s' % ', '.join(self.names), '* `labels` %s' % ', '.join(self.labels), '* `units` %s' % ', '.join(self.units))) self._meta_attrs.extend(['names', 'labels', 'units']) elif name is not None: self.label = name if label is None else label self.units = units if units is not None else '' # vals / validate only applies to simple single-value parameters self.set_validator(vals) # generate default docstring self.__doc__ = os.linesep.join(( 'Parameter class:', '* `name` %s' % self.name, '* `label` %s' % self.label, # TODO is this unit s a typo? shouldnt that be unit? '* `units` %s' % self.units, '* `vals` %s' % repr(self._vals))) self._meta_attrs.extend(['name', 'label', 'units', 'vals']) else: raise ValueError('either name or names is required') if shape is not None or shapes is not None: nt = type(None) if shape is not None: if not is_sequence_of(shape, int): raise ValueError('shape must be a tuple of ints, not ' + repr(shape)) self.shape = shape depth = 1 container_str = 'tuple' else: if not is_sequence_of(shapes, int, depth=2): raise ValueError('shapes must be a tuple of tuples ' 'of ints, not ' + repr(shape)) self.shapes = shapes depth = 2 container_str = 'tuple of tuples' sp_types = (nt, DataArray, collections.Sequence, collections.Iterator) if (setpoints is not None and not is_sequence_of(setpoints, sp_types, depth)): raise ValueError( 'setpoints must be a {} of arrays'.format(container_str)) if (setpoint_names is not None and not is_sequence_of(setpoint_names, (nt, str), depth)): raise ValueError('setpoint_names must be a {} ' 'of strings'.format(container_str)) if (setpoint_labels is not None and not is_sequence_of(setpoint_labels, (nt, str), depth)): raise ValueError('setpoint_labels must be a {} ' 'of strings'.format(container_str)) self.setpoints = setpoints self.setpoint_names = setpoint_names self.setpoint_labels = setpoint_labels # record of latest value and when it was set or measured # what exactly this means is different for different subclasses # but they all use the same attributes so snapshot is consistent. self._latest_value = None self._latest_ts = None if docstring is not None: self.__doc__ = docstring + os.linesep + self.__doc__ self.get_latest = GetLatest(self)
def __init__( self, name: str, instrument_x: Union[AMI430, str], instrument_y: Union[AMI430, str], instrument_z: Union[AMI430, str], field_limit: Union[numbers.Real, Iterable[CartesianFieldLimitFunction]], **kwargs: Any, ): """ Driver for controlling three American Magnetics Model 430 magnet power supplies simultaneously for setting magnetic field vectors. The individual magnet power supplies can be passed in as either instances of AMI430 driver or as names of existing AMI430 instances. In the latter case, the instances will be found via the passed names. Args: name: a name for the instrument instrument_x: AMI430 instance or a names of existing AMI430 instance for controlling the X axis of magnetic field instrument_y: AMI430 instance or a names of existing AMI430 instance for controlling the Y axis of magnetic field instrument_z: AMI430 instance or a names of existing AMI430 instance for controlling the Z axis of magnetic field field_limit: a number for maximum allows magnetic field or an iterable of callable field limit functions that define region(s) of allowed values in 3D magnetic field space """ super().__init__(name, **kwargs) if not isinstance(name, str): raise ValueError("Name should be a string") for instrument, arg_name in zip( (instrument_x, instrument_y, instrument_z), ("instrument_x", "instrument_y", "instrument_z"), ): if not isinstance(instrument, (AMI430, str)): raise ValueError( f"Instruments need to be instances of the class AMI430 " f"or be valid names of already instantiated instances " f"of AMI430 class; {arg_name} argument is " f"neither of those") def find_ami430_with_name(ami430_name: str) -> AMI430: found_ami430 = AMI430.find_instrument(name=ami430_name, instrument_class=AMI430) return found_ami430 self._instrument_x = (instrument_x if isinstance(instrument_x, AMI430) else find_ami430_with_name(instrument_x)) self._instrument_y = (instrument_y if isinstance(instrument_y, AMI430) else find_ami430_with_name(instrument_y)) self._instrument_z = (instrument_z if isinstance(instrument_z, AMI430) else find_ami430_with_name(instrument_z)) self._field_limit: Union[float, Iterable[CartesianFieldLimitFunction]] if isinstance(field_limit, collections.abc.Iterable): self._field_limit = field_limit elif isinstance(field_limit, numbers.Real): # Conversion to float makes related driver logic simpler self._field_limit = float(field_limit) else: raise ValueError("field limit should either be a number or " "an iterable of callable field limit functions.") self._set_point = FieldVector( x=self._instrument_x.field(), y=self._instrument_y.field(), z=self._instrument_z.field(), ) # Get-only parameters that return a measured value self.add_parameter( "cartesian_measured", get_cmd=partial(self._get_measured, "x", "y", "z"), unit="T", ) self.add_parameter("x_measured", get_cmd=partial(self._get_measured, "x"), unit="T") self.add_parameter("y_measured", get_cmd=partial(self._get_measured, "y"), unit="T") self.add_parameter("z_measured", get_cmd=partial(self._get_measured, "z"), unit="T") self.add_parameter( "spherical_measured", get_cmd=partial(self._get_measured, "r", "theta", "phi"), unit="T", ) self.add_parameter("phi_measured", get_cmd=partial(self._get_measured, "phi"), unit="deg") self.add_parameter("theta_measured", get_cmd=partial(self._get_measured, "theta"), unit="deg") self.add_parameter("field_measured", get_cmd=partial(self._get_measured, "r"), unit="T") self.add_parameter( "cylindrical_measured", get_cmd=partial(self._get_measured, "rho", "phi", "z"), unit="T", ) self.add_parameter("rho_measured", get_cmd=partial(self._get_measured, "rho"), unit="T") # Get and set parameters for the set points of the coordinates self.add_parameter( "cartesian", get_cmd=partial(self._get_setpoints, ("x", "y", "z")), set_cmd=partial(self._set_setpoints, ("x", "y", "z")), unit="T", vals=Anything(), ) self.add_parameter( "x", get_cmd=partial(self._get_setpoints, ("x", )), set_cmd=partial(self._set_setpoints, ("x", )), unit="T", vals=Numbers(), ) self.add_parameter( "y", get_cmd=partial(self._get_setpoints, ("y", )), set_cmd=partial(self._set_setpoints, ("y", )), unit="T", vals=Numbers(), ) self.add_parameter( "z", get_cmd=partial(self._get_setpoints, ("z", )), set_cmd=partial(self._set_setpoints, ("z", )), unit="T", vals=Numbers(), ) self.add_parameter( "spherical", get_cmd=partial(self._get_setpoints, ("r", "theta", "phi")), set_cmd=partial(self._set_setpoints, ("r", "theta", "phi")), unit="tuple?", vals=Anything(), ) self.add_parameter( "phi", get_cmd=partial(self._get_setpoints, ("phi", )), set_cmd=partial(self._set_setpoints, ("phi", )), unit="deg", vals=Numbers(), ) self.add_parameter( "theta", get_cmd=partial(self._get_setpoints, ("theta", )), set_cmd=partial(self._set_setpoints, ("theta", )), unit="deg", vals=Numbers(), ) self.add_parameter( "field", get_cmd=partial(self._get_setpoints, ("r", )), set_cmd=partial(self._set_setpoints, ("r", )), unit="T", vals=Numbers(), ) self.add_parameter( "cylindrical", get_cmd=partial(self._get_setpoints, ("rho", "phi", "z")), set_cmd=partial(self._set_setpoints, ("rho", "phi", "z")), unit="tuple?", vals=Anything(), ) self.add_parameter( "rho", get_cmd=partial(self._get_setpoints, ("rho", )), set_cmd=partial(self._set_setpoints, ("rho", )), unit="T", vals=Numbers(), ) self.add_parameter("block_during_ramp", set_cmd=None, initial_value=True, unit="", vals=Bool()) self.ramp_mode = Parameter( name="ramp_mode", instrument=self, get_cmd=None, set_cmd=None, vals=Enum("default", "simultaneous"), initial_value="default", ) self.ramping_state_check_interval = Parameter( name="ramping_state_check_interval", instrument=self, initial_value=0.05, unit="s", vals=Numbers(0, 10), set_cmd=None, get_cmd=None, ) self.vector_ramp_rate = Parameter( name="vector_ramp_rate", instrument=self, unit="T/s", vals=Numbers(min_value=0.0), set_cmd=None, get_cmd=None, set_parser=self._set_vector_ramp_rate_units, docstring="Ramp rate along a line (vector) in 3D space. Only active" " if `ramp_mode='simultaneous'`.", ) """Ramp rate along a line (vector) in 3D field space"""
def __init__(self, name, 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=self._set_cartesian, unit='T', vals=Anything()) self.add_parameter('x', get_cmd=partial(self._get_setpoints, 'x'), set_cmd=self._set_x, unit='T', vals=Numbers()) self.add_parameter('y', get_cmd=partial(self._get_setpoints, 'y'), set_cmd=self._set_y, unit='T', vals=Numbers()) self.add_parameter('z', get_cmd=partial(self._get_setpoints, 'z'), set_cmd=self._set_z, unit='T', vals=Numbers()) self.add_parameter('spherical', get_cmd=partial(self._get_setpoints, 'r', 'theta', 'phi'), set_cmd=self._set_spherical, unit='tuple?', vals=Anything()) self.add_parameter('phi', get_cmd=partial(self._get_setpoints, 'phi'), set_cmd=self._set_phi, unit='deg', vals=Numbers()) self.add_parameter('theta', get_cmd=partial(self._get_setpoints, 'theta'), set_cmd=self._set_theta, unit='deg', vals=Numbers()) self.add_parameter('field', get_cmd=partial(self._get_setpoints, 'r'), set_cmd=self._set_r, unit='T', vals=Numbers()) self.add_parameter('cylindrical', get_cmd=partial(self._get_setpoints, 'rho', 'phi', 'z'), set_cmd=self._set_cylindrical, unit='tuple?', vals=Anything()) self.add_parameter('rho', get_cmd=partial(self._get_setpoints, 'rho'), set_cmd=self._set_rho, unit='T', vals=Numbers())
def __init__(self, name: str, instrument_x: AMI430, instrument_y: AMI430, instrument_z: AMI430, field_limit: Union[numbers.Real, Iterable[CartesianFieldLimitFunction]], **kwargs: Any): super().__init__(name, **kwargs) if not isinstance(name, str): raise ValueError("Name should be a string") instruments = [instrument_x, instrument_y, instrument_z] if not all([isinstance(instrument, AMI430) for instrument in instruments]): raise ValueError("Instruments need to be instances " "of the class AMI430") self._instrument_x = instrument_x self._instrument_y = instrument_y self._instrument_z = instrument_z self._field_limit: Union[float, Iterable[CartesianFieldLimitFunction]] if isinstance(field_limit, collections.abc.Iterable): self._field_limit = field_limit elif isinstance(field_limit, numbers.Real): # Conversion to float makes related driver logic simpler self._field_limit = float(field_limit) else: raise ValueError("field limit should either be a number or " "an iterable of callable field limit functions.") self._set_point = FieldVector( x=self._instrument_x.field(), y=self._instrument_y.field(), z=self._instrument_z.field() ) # Get-only parameters that return a measured value self.add_parameter( 'cartesian_measured', get_cmd=partial(self._get_measured, 'x', 'y', 'z'), unit='T' ) self.add_parameter( 'x_measured', get_cmd=partial(self._get_measured, 'x'), unit='T' ) self.add_parameter( 'y_measured', get_cmd=partial(self._get_measured, 'y'), unit='T' ) self.add_parameter( 'z_measured', get_cmd=partial(self._get_measured, 'z'), unit='T' ) self.add_parameter( 'spherical_measured', get_cmd=partial( self._get_measured, 'r', 'theta', 'phi' ), unit='T' ) self.add_parameter( 'phi_measured', get_cmd=partial(self._get_measured, 'phi'), unit='deg' ) self.add_parameter( 'theta_measured', get_cmd=partial(self._get_measured, 'theta'), unit='deg' ) self.add_parameter( 'field_measured', get_cmd=partial(self._get_measured, 'r'), unit='T') self.add_parameter( 'cylindrical_measured', get_cmd=partial(self._get_measured, 'rho', 'phi', 'z'), unit='T') self.add_parameter( 'rho_measured', get_cmd=partial(self._get_measured, 'rho'), unit='T' ) # Get and set parameters for the set points of the coordinates self.add_parameter( 'cartesian', get_cmd=partial(self._get_setpoints, ('x', 'y', 'z')), set_cmd=partial(self._set_setpoints, ('x', 'y', 'z')), unit='T', vals=Anything() ) self.add_parameter( 'x', get_cmd=partial(self._get_setpoints, ('x',)), set_cmd=partial(self._set_setpoints, ('x',)), unit='T', vals=Numbers() ) self.add_parameter( 'y', get_cmd=partial(self._get_setpoints, ('y',)), set_cmd=partial(self._set_setpoints, ('y',)), unit='T', vals=Numbers() ) self.add_parameter( 'z', get_cmd=partial(self._get_setpoints, ('z',)), set_cmd=partial(self._set_setpoints, ('z',)), unit='T', vals=Numbers() ) self.add_parameter( 'spherical', get_cmd=partial( self._get_setpoints, ('r', 'theta', 'phi') ), set_cmd=partial( self._set_setpoints, ('r', 'theta', 'phi') ), unit='tuple?', vals=Anything() ) self.add_parameter( 'phi', get_cmd=partial(self._get_setpoints, ('phi',)), set_cmd=partial(self._set_setpoints, ('phi',)), unit='deg', vals=Numbers() ) self.add_parameter( 'theta', get_cmd=partial(self._get_setpoints, ('theta',)), set_cmd=partial(self._set_setpoints, ('theta',)), unit='deg', vals=Numbers() ) self.add_parameter( 'field', get_cmd=partial(self._get_setpoints, ('r',)), set_cmd=partial(self._set_setpoints, ('r',)), unit='T', vals=Numbers() ) self.add_parameter( 'cylindrical', get_cmd=partial( self._get_setpoints, ('rho', 'phi', 'z') ), set_cmd=partial( self._set_setpoints, ('rho', 'phi', 'z') ), unit='tuple?', vals=Anything() ) self.add_parameter( 'rho', get_cmd=partial(self._get_setpoints, ('rho',)), set_cmd=partial(self._set_setpoints, ('rho',)), unit='T', vals=Numbers() ) self.add_parameter( 'block_during_ramp', set_cmd=None, initial_value=True, unit='', vals=Bool() )
def __init__(self, name, magnet_x, magnet_y, magnet_z, field_limit, **kwargs): super().__init__(name, **kwargs) # Register this instrument as the parent of the individual magnets for m in [magnet_x, magnet_y, magnet_z]: m._parent_instrument = self self._magnet_x = magnet_x self._magnet_y = magnet_y self._magnet_z = magnet_z # Make this into a parameter? self._field_limit = field_limit # Initialize the internal magnet field setpoints self.update_internal_setpoints() # 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, 'field', '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, 'field'), 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 setpoints of the coordinates self.add_parameter('cartesian', get_cmd=partial(self._get_setpoints, 'x', 'y', 'z'), set_cmd=self._set_fields, unit='T', vals=Anything()) self.add_parameter('x', get_cmd=partial(self._get_setpoints, 'x'), set_cmd=self._set_x, unit='T', vals=Numbers()) self.add_parameter('y', get_cmd=partial(self._get_setpoints, 'y'), set_cmd=self._set_y, unit='T', vals=Numbers()) self.add_parameter('z', get_cmd=partial(self._get_setpoints, 'z'), set_cmd=self._set_z, unit='T', vals=Numbers()) self.add_parameter('spherical', get_cmd=partial(self._get_setpoints, 'field', 'theta', 'phi'), set_cmd=self._set_spherical, unit='tuple?', vals=Anything()) self.add_parameter('phi', get_cmd=partial(self._get_setpoints, 'phi'), set_cmd=self._set_phi, unit='deg', vals=Numbers()) self.add_parameter('theta', get_cmd=partial(self._get_setpoints, 'theta'), set_cmd=self._set_theta, unit='deg', vals=Numbers()) self.add_parameter('field', get_cmd=partial(self._get_setpoints, 'field'), set_cmd=self._set_field, unit='T', vals=Numbers()) self.add_parameter('cylindrical', get_cmd=partial(self._get_setpoints, 'rho', 'phi', 'z'), set_cmd=self._set_cylindrical, unit='tuple?', vals=Anything()) self.add_parameter('rho', get_cmd=partial(self._get_setpoints, 'rho'), set_cmd=self._set_rho, unit='T', vals=Numbers())