Source code for qcodes.instrument_drivers.AimTTi.AimTTi_PL601P_channels

from typing import Any, Dict, Optional

from qcodes import validators as vals
from qcodes.instrument import ChannelList, Instrument, InstrumentChannel, VisaInstrument
from qcodes.parameters import create_on_off_val_mapping


[docs]class NotKnownModel(Exception): pass
[docs]class AimTTiChannel(InstrumentChannel): """ This is the class that holds the output channels of AimTTi power supply. """ def __init__(self, parent: Instrument, name: str, channel: int, **kwargs: Any) -> None: """ Args: parent: The Instrument instance to which the channel is to be attached. name: The 'colloquial' name of the channel. channel: The name used by the AimTTi. """ super().__init__(parent, name, **kwargs) self.channel = channel # The instrument can store up to ten configurations # internally. self.set_up_store_slots = [i for i in range(0, 10)] self.add_parameter('volt', get_cmd=self._get_voltage_value, get_parser=float, set_cmd=f'V{channel} {{}}', label='Voltage', unit='V') self.add_parameter('volt_step_size', get_cmd=self._get_voltage_step_size, get_parser=float, set_cmd=f'DELTAV{channel} {{}}', label='Voltage Step Size', unit='V') self.add_parameter('curr', get_cmd=self._get_current_value, get_parser=float, set_cmd=f'I{channel} {{}}', label='Current', unit='A') self.add_parameter('curr_range', get_cmd=f'IRANGE{channel}?', get_parser=int, set_cmd=self._set_current_range, label='Current Range', unit='A', vals=vals.Numbers(1, 2), docstring='Set the current range of the output.' 'Here, the integer 1 is for the Low range, ' 'and integer 2 is for the High range.') self.add_parameter('curr_step_size', get_cmd=self._get_current_step_size, get_parser=float, set_cmd=f'DELTAI{channel} {{}}', label='Current Step Size', unit='A') self.add_parameter('output', get_cmd=f'OP{channel}?', get_parser=float, set_cmd=f'OP{channel} {{}}', val_mapping=create_on_off_val_mapping(on_val=1, off_val=0)) def _get_voltage_value(self) -> float: channel_id = self.channel _voltage = self.ask_raw(f'V{channel_id}?') _voltage_split = _voltage.split() return float(_voltage_split[1]) def _get_current_value(self) -> float: channel_id = self.channel _current = self.ask_raw(f'I{channel_id}?') _current_split = _current.split() return float(_current_split[1]) def _get_voltage_step_size(self) -> float: channel_id = self.channel _voltage_step_size = self.ask_raw(f'DELTAV{channel_id}?') _v_step_size_split = _voltage_step_size.split() return float(_v_step_size_split[1]) def _get_current_step_size(self) -> float: channel_id = self.channel _current_step_size = self.ask_raw(f'DELTAI{channel_id}?') _c_step_size_split = _current_step_size.split() return float(_c_step_size_split[1]) def _set_current_range(self, val: int) -> None: """ This is the private function that ensures that the output is switched off before changing the current range, as pointed out by the instrument manual. """ channel_id = self.channel with self.output.set_to(False): self.write(f'IRANGE{channel_id} {val}')
[docs] def increment_volt_by_step_size(self) -> None: """ A bound method that increases the voltage output of the corresponding channel by an amount the step size set by the user via ``volt_step_size`` parameter. """ channel_id = self.channel self.write(f'INCV{channel_id}') # Clear the cache. _ = self.volt.get()
[docs] def decrement_volt_by_step_size(self) -> None: """ A bound method that decreases the voltage output of the corresponding channel by an amount the step size set by the user via ``volt_step_size`` parameter. """ channel_id = self.channel self.write(f'DECV{channel_id}') # Clear the cache. _ = self.volt.get()
[docs] def increment_curr_by_step_size(self) -> None: """ A bound method that increases the current output of the corresponding channel by an amount the step size set by the user via ``curr_step_size`` parameter. """ channel_id = self.channel self.write(f'INCI{channel_id}') # Clear the cache. _ = self.curr.get()
[docs] def decrement_curr_by_step_size(self) -> None: """ A bound method that decreases the current output of the corresponding channel by an amount the step size set by the user via ``curr_step_size`` parameter. """ channel_id = self.channel self.write(f'DECI{channel_id}') # Clear the cache. _ = self.curr.get()
[docs] def save_setup(self, slot: int) -> None: """ A bound function that saves the output setup to the internal store specified by the numbers 0-9. """ if not slot in self.set_up_store_slots: raise RuntimeError("Slote number should be an integer between" "0 adn 9.") channel_id = self.channel self.write(f'SAV{channel_id} {slot}')
[docs] def load_setup(self, slot: int) -> None: """ A bound function that loadss the output setup from the internal store specified by the numbers 0-9. """ if not slot in self.set_up_store_slots: raise RuntimeError("Slote number should be an integer between" "0 adn 9.") channel_id = self.channel self.write(f'RCL{channel_id} {slot}') # Update snapshot after load. _ = self.snapshot(update=True)
[docs] def set_damping(self, val: int) -> None: """ Sets the current meter measurement averaging on and off. """ if not val in [0, 1]: raise RuntimeError("To 'turn on' and 'turn off' the averaging, " "use '1' and '0', respectively.") channel_id = self.channel self.write(f'DAMPING{channel_id} {val}')
[docs]class AimTTi(VisaInstrument): """ This is the QCoDeS driver for the Aim TTi PL-P series power supply. Tested with Aim TTi PL601-P equipped with a single output channel. """ def __init__(self, name: str, address: str, **kwargs: Any) -> None: """ Args: name: Name to use internally in QCoDeS. address: VISA resource address """ super().__init__(name, address, terminator='\n', **kwargs) channels = ChannelList(self, "Channels", AimTTiChannel, snapshotable=False) _model = self.get_idn()['model'] _numOutputChannels = {'PL068-P': 1, 'PL155-P': 1, 'PL303-P': 1, 'PL601-P': 1, 'PL303QMD-P': 2, 'PL303QMT': 3} if (not _model in _numOutputChannels.keys()) or (_model is None): raise NotKnownModel("Unknown model, connection cannot be " "established.") self.numOfChannels = _numOutputChannels[_model] for i in range(1, self.numOfChannels+1): channel = AimTTiChannel(self, f'ch{i}', i) channels.append(channel) self.add_submodule(f'ch{i}', channel) self.add_submodule("channels", channels.to_channel_tuple()) self.connect_message() # Interface Management
[docs] def get_idn(self) -> Dict[str, Optional[str]]: """ Returns the instrument identification including vendor, model, serial number and the firmware. """ IDNstr = self.ask_raw('*IDN?') vendor, model, serial, firmware = map(str.strip, IDNstr.split(',')) IDN: Dict[str, Optional[str]] = {'vendor': vendor, 'model': model, 'serial': serial, 'firmware': firmware} return IDN
[docs] def get_address(self) -> int: """ Returns the bus address. """ busAddressStr = self.ask_raw('ADDRESS?') busAddress = busAddressStr.strip() return int(busAddress)
[docs] def get_IP(self) -> str: """ Returns the IP address of the LAN interface, if the connection exists. If there is a pre-configured static IP and the instrument is not connected to a LAN interface, that static IP will be returned. Otherwise, the return value is '0.0.0.0'. """ ipAddress = self.ask_raw('IPADDR?') return ipAddress.strip()
[docs] def get_netMask(self) -> str: """ Returns the netmask of the LAN interface, if the connection exists. """ netMask = self.ask_raw('NETMASK?') return netMask.strip()
[docs] def get_netConfig(self) -> str: """ Returns the means by which an IP address is acquired, i.e., DHCP, AUTO or STATIC. """ netConfig = self.ask_raw('NETCONFIG?') return netConfig.strip()
[docs] def local_mode(self) -> None: """ Go to local mode until the next remote command is recieved. This function does not release any active interface lock. """ self.write(f'LOCAL')
[docs] def is_interface_locked(self) -> int: """ Returns '1' if the interface lock is owned by the requesting instance, '0' if there is no active lock and '-1' if the lock is unavailable. """ is_lockedSTR = self.ask_raw('IFLOCK?') is_locked = is_lockedSTR.strip() return int(is_locked)
[docs] def lock_interface(self) -> int: """ Requests instrument interface lock. Returns '1' if successful and '-1' if the lock is unavailable. """ lockSTR = self.ask_raw('IFLOCK') lock = lockSTR.strip() return int(lock)
[docs] def unlock_interface(self) -> int: """ Requests the release of instrument interface lock. Returns '0' if successful and '-1' if unsuccessful. """ unlockSTR = self.ask_raw('IFUNLOCK') unlock = unlockSTR.strip() return int(unlock)