Source code for pylab_ml.smu.tti.base_tti

"""Baseclass Interfaces to the TTI Power Supply Instruments.

:Date: |today|
:Author: Semi-ATE <info@Semi-ATE.org>

"""
from enum import Enum
from pylab_ml.base_instrument import logger
from pylab_ml.base_instrument import InvalidInstrumentConnection
from pylab_ml.collate_instrument import Interface
from pylab_ml.baseclass.base_measurement import Measure
from pylab_ml.attributes import create_attributes


[docs] class TTI (create_attributes, Measure): """ Baseclass Interface to the TTI Power Supply Instruments. The TTI baseclass can connect to TTI Power Supply instruments Methods: ask=inst.query(':READ?') Write and read the answer """ # create functions or proberty and call inst.write) or inst.read(): # if state != necessary state -> switch to the necessary state # proberty/function name -> inst.funcname (get,set) , range, call functions # range : None, Enum or range value # call functions: see help in attributes # in function self.write() replace $ with channel _properties = { 'current': (('I$O?', None), None, {'ga': '_tr2number(value, A, 20)'}), 'i_clamp': (('I$?', 'I$ '), None, {'ga': '_tr2number(value, I$, 1)', 'sa': 'errmsg()'}), 'onoff': ((None, 'OP$ '), [0, 1], None), 'ocp': (('OCP$?', 'OCP$ '), None, {'ga': '_tr2number(value, CP$, 1)', 'sa': 'errmsg()'}), 'ovp': (('OVP$?', 'OVP$ '), None, {'ga': '_tr2number(value, VP$, 1)', 'sa': 'errmsg()'}), 'sense': ((None, 'SENSE$ '), 'Sense', None), 'state': (('*ESE?', '*ESE '), None, None), 'voltage': (('V$?', 'V$ '), None, {'ga': '_tr2number(value, V$, 1)', 'sa': 'errmsg()'}), 'v_range': (('RANGE$?', 'RANGE$ '), [0, 2], {'ga': '_tr2number(value, R$, 11)'}), }
[docs] class Sense(Enum): """Enum for Sense control.""" local = 0 """value for sense mode = local""" remote = 1 """value for sense mode = remote"""
_eer_states = { 1: ' Indicates a hardware error has been encountered.', # 1-99 116: ' A recall of set up data has been requested but the store specified does not contain any data.', 117: ' A recall of set up data has been requested but the store specified contains corrupted data. This indicates either a hardware fault or a temporary data corruption which can be corrected by writing data to the store again.', 120: ' The numerical value sent with the command was too big or too small. Includes negative numbers where only positive numbers are accepted.', 123: ' A recall/store of set up data has been requested from/to an illegal store number.', 124: ' A range change has been requested but the current psu settings make it illegal – see manual operation instructions for details.', 255: ' unknown error' }
[docs] def __init__(self, **kwargs): """ Connect and initialize TTI instrument. Args: addr (int): Interface address interface (Interface): GPIB, USBSerial backend (str): VISA backend is either '@ni' for NI-Library or '@py' for pure python pyvisa-py backend. On default it uses '@ni' on win32 and '@py' on other platforms. Example: Initialization >>> instrument = TTI(addr=24) # GPIB or USB address >>> instrument.init() # connect and initialize instrument """ self.is_local = False super().__init__(**kwargs) logger.debug("Class {}".format(self.__class__.__name__)) self.com._init(self)
[docs] def setup_inst(self): """Start setup instrument settings, called from class instruments.""" super().setup_inst() if self.inst: if self.interface == Interface.gpib: self.inst.read_termination = '\n' # self.inst.read_termination = '\r' self.inst.write_termination = '\n' self.inst.encoding = 'iso8859' else: self.inst.baud_rate = 19200 # in USB-Mode: 19200 self.inst.read_termination = '\n' # LF (=10d) # self.inst.write_termination = '\r\n' # CR (=13d), LF self._endstring = -1 # string include \r as last character, to ignore this use -1 else 0 self.inst.write_termination = '\n' self.inst.encoding = 'iso8859' # self.inst.write('\x04') # lock normal RS232-Mode self.inst.timeout = 1000 self.createattributes(self._properties)
[docs] def init(self, identify=False): """ Connect to TTI instrument and initialize. Parameters ---------- identify : bool, optional Whether to identify the instrument. The default is False. """ super().init(identify)
[docs] def reset(self): """Reset and switch beep off.""" self.budget.set_slack(self) self.inst.clear() self.inst.write('*RST') for ch in self.channels: # reset shadow-values from _properties self[ch]._onoff = 0 self[ch]._local = 'local'
[docs] def clear(self): """Clear error status.""" self.budget.set_slack(self) self.inst.clear()
[docs] def message(self, message=None): """Device has no display, message display to logger.info.""" logger.info(message)
@property def id(self): """Query IDN.""" self.budget.set_slack(self) try: # value = self.inst.query('*IDN?').split(',')[1] value = self.inst.query('*IDN?') value = self.inst.read().split(',')[1] except Exception: value = "" if value != self.__class__.__name__: if value == "": value = "no answer" msg = ('{}.id: The connected Device has wrong Model-No, readed Model is: {}'.format(self.__class__.__name__, value)) raise InvalidInstrumentConnection(msg) return value
[docs] def local(self): """Switch back to local instrument control.""" self.budget.set_slack(self) self.inst.write('LOCAL') self.is_local = True
[docs] def write(self, cmd): """ Write direct to instrument. Example: send command reset : >>> inst.write('*RST') Parameters ---------- cmd : str Command to send to instrument, use $ for channel number. """ if cmd.find('$') > -1: cmd = cmd.replace('$', str(self.channel+1)) self.inst.write(cmd)
[docs] def read(self): """Read the instruent directly.""" return self.inst.read()
[docs] def errmsg(self): """Read Execution Error Register.""" error = int(self.inst.query('EER?')) if error != 0: if error < 99: error = 1 if error not in self._eer_states: error = 255 logger.error(self._eer_states[error])
def _tr2number(self, value): """Translate answer from device to float or integer. Index depend from the function call, see _properties Parameters ---------- value : str Answer from device, for example 'V1 12.34' or 'I1 0.1234A' Returns ------- result : float or int The numerical value extracted from the device response. """ value = value.split(',') index = int(value[2]) % 10 convert = round(int(value[2]) / 10) # 0= float, 1= integer, 2 = A verify_target = value[1][1:].replace('$', str(self.channel+1)) verify_answ = value[0].split(' ')[0] result = value[0].split(' ')[index] if convert == 0: result = float(result) elif convert == 1: result = int(result) elif convert == 2: verify_answ = verify_answ[self._endstring-1] result = float(result[:self._endstring-1]) if verify_answ != verify_target: logger.error('Unexpected Answer from Device! (got:{})'.format) return result # ---------------------------------------------------------- # doc strings for _properties, propertie themselve will create form dictionary _properties @property def current(self): """Measure DC current of actual Channel.""" @property def i_clamp(self): """Set the current clamping (A) of actual Channel.""" @property def onoff(self): """ Get/Set the Output-State of actual Channel. | 0 = Output off | 1 = Output on """ @property def ocp(self): """Get/Set over current protection trip point at x Amps.""" @property def ovp(self): """Get/Set over voltage protection trip point at x Volts.""" @property def sense(self): """ Get/Set sense. | 'local' = Local sensing (2-Wire) | 'remote' = Remote sensing (4-Wire) """ @property def state(self): """ Get/Set Standard Event Status Enable Register. See QL355T Instruction Manual for more details """ @property def voltage(self): """Get/Set voltage."""
# end doc strings for _properties # ----------------------------------------------------------