# This file is part of the scanning-squid package.
#
# Copyright (c) 2018 Logan Bishop-Van Horn
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from qcodes import VisaInstrument
from qcodes.utils.validators import Strings, Enum
[docs]class Keithley_2400(VisaInstrument):
"""
QCoDeS driver for the Keithley 2400 voltage source.
"""
def __init__(self, name, address, **kwargs):
super().__init__(name, address, terminator='\n', **kwargs)
self.add_parameter('rangev',
get_cmd='SENS:VOLT:RANG?',
get_parser=float,
set_cmd='SOUR:VOLT:RANG {:f}',
label='Voltage range')
self.add_parameter('rangei',
get_cmd='SENS:CURR:RANG?',
get_parser=float,
set_cmd='SOUR:CURR:RANG {:f}',
label='Current range')
self.add_parameter('compliancev',
get_cmd='SENS:VOLT:PROT?',
get_parser=float,
set_cmd='SENS:VOLT:PROT {:f}',
label='Voltage Compliance')
self.add_parameter('compliancei',
get_cmd='SENS:CURR:PROT?',
get_parser=float,
set_cmd='SENS:CURR:PROT {:f}',
label='Current Compliance')
self.add_parameter('volt',
get_cmd=self._get_read_output_protected,
get_parser=self._volt_parser,
set_cmd=':SOUR:VOLT:LEV {:.8f}',
label='Voltage',
unit='V',
snapshot_get=False,
docstring="Sets voltage in 'VOLT' mode. "
"Get returns measured voltage if "
"sensing 'VOLT' otherwise it returns "
"setpoint value. "
"Note that it is an error to read voltage with "
"output off")
self.add_parameter('curr',
get_cmd=self._get_read_output_protected,
get_parser=self._curr_parser,
set_cmd=':SOUR:CURR:LEV {:.8f}',
label='Current',
unit='A',
snapshot_get=False,
docstring = "Sets current in 'CURR' mode. "
"Get returns measured current if "
"sensing 'CURR' otherwise it returns "
"setpoint value. "
"Note that it is an error to read current with "
"output off")
self.add_parameter('mode',
vals=Enum('VOLT', 'CURR'),
get_cmd=':SOUR:FUNC?',
set_cmd=self._set_mode_and_sense,
label='Mode')
self.add_parameter('sense',
vals=Strings(),
get_cmd=':SENS:FUNC?',
set_cmd=':SENS:FUNC "{:s}"',
label='Sense mode')
self.add_parameter('output',
get_parser=int,
set_cmd=':OUTP:STAT {:d}',
get_cmd=':OUTP:STAT?')
self.add_parameter('nplcv',
get_cmd='SENS:VOLT:NPLC?',
get_parser=float,
set_cmd='SENS:VOLT:NPLC {:f}',
label='Voltage integration time')
self.add_parameter('nplci',
get_cmd='SENS:CURR:NPLC?',
get_parser=float,
set_cmd='SENS:CURR:NPLC {:f}',
label='Current integration time')
self.add_parameter('resistance',
get_cmd=self._get_read_output_protected,
get_parser=self._resistance_parser,
label='Resistance',
unit='Ohm',
snapshot_get=False,
docstring="Measure resistance from current and voltage "
"Note that it is an error to read current "
"and voltage with output off")
self.connect_message()
def _get_read_output_protected(self) -> str:
"""
This wrapper function around ":READ?" exists because calling
":READ?" on an instrument with output disabled is an error.
So first we check that output is on and if not we return
nan for volt, curr etc.
"""
output = self.output.get_latest()
if output is None:
# if get_latest returns None we have
# to ask the instrument for the status of output
output = self.output.get()
if output == 1:
msg = self.ask(':READ?')
else:
raise RuntimeError("Cannot perform read with output off")
return msg
def _set_mode_and_sense(self, msg):
# This helps set the correct read out curr/volt
if msg == 'VOLT':
self.sense('CURR')
elif msg == 'CURR':
self.sense('VOLT')
else:
raise AttributeError('Mode does not exist')
self.write(':SOUR:FUNC {:s}'.format(msg))
[docs] def reset(self):
"""
Reset the instrument. When the instrument is reset, it performs the
following actions.
Returns the SourceMeter to the GPIB default conditions.
Cancels all pending commands.
Cancels all previously send `*OPC` and `*OPC?`
"""
self.write(':*RST')
def _volt_parser(self, msg):
fields = [float(x) for x in msg.split(',')]
return fields[0]
def _curr_parser(self, msg):
fields = [float(x) for x in msg.split(',')]
return fields[1]
def _resistance_parser(self, msg):
fields = [float(x) for x in msg.split(',')]
res = fields[0] / fields[1]
return res