# OxfordInstruments_Kelvinox_IGH class, to perform the communication between the Wrapper and the device
# Copyright (c) 2017 QuTech (Delft)
# Code is available under the available under the `MIT open-source license
# <https://opensource.org/licenses/MIT>`_
# Sjaak van Diepen <c.j.vandiepen@gmail.com>, 2017
# Guenevere Prawiroatmodjo <guen@vvtp.tudelft.nl>, 2009
# Pieter de Groot <pieterdegroot@gmail.com>, 2009
from time import sleep
import pyvisa
import pyvisa.constants
import logging
import numpy
from qcodes.instrument import VisaInstrument
from qcodes import validators as vals
from functools import partial
log = logging.getLogger(__name__)
[docs]
class OxfordInstruments_Kelvinox_IGH(VisaInstrument):
"""
This is the python driver for the Oxford Instruments Kelvinox IGH Dilution Refrigerator and
Intelligent Dilution Refrigerator Power Supply (IDR PS).
Usage:
Initialize with
fridge = qcodes_contrib_drivers.drivers.oxford.kelvinox.OxfordInstruments_Kelvinox_IGH(name='fridge', address='ASRL4::INSTR')
Note: Since the ISOBUS allows for several instruments to be managed in parallel, the command
which is sent to the device starts with '@n', where n is the ISOBUS instrument number.
"""
[docs]
def __init__(self, name, address, number=5, **kwargs):
"""
Initializes the Oxford Instruments Kelvinox IGH Dilution Refrigerator.
Input:
name (string) : name of the instrument
address (string) : instrument address
number (int) : ISOBUS instrument number
"""
log.debug('Initializing instrument')
super().__init__(name, address, **kwargs)
self._address = address
self._number = number
self._values = {}
self.visa_handle.set_visa_attribute(pyvisa.constants.VI_ATTR_ASRL_STOP_BITS,
pyvisa.constants.VI_ASRL_STOP_TWO)
self._valve_map = {
1: '9',
2: '8',
3: '7',
4: '11A',
5: '13A',
6: '13B',
7: '11B',
8: '12B',
10: '1',
11: '5',
12: '4',
13: '3',
14: '14',
15: '10',
16: '2',
17: '2A',
18: '1A',
19: '5A',
20: '4A'
}
# Add parameters
self.add_parameter('one_K_pot_temp',
unit='K',
get_cmd=self._get_one_K_pot_temp)
self.add_parameter('mix_chamber_temp',
unit='K',
get_cmd=self._get_mix_chamber_temp,
set_cmd=self._set_mix_chamber_temp)
self.add_parameter('G1',
unit='mbar',
get_cmd=self._get_G1)
self.add_parameter('G2',
unit='mbar',
get_cmd=self._get_G2)
self.add_parameter('G3',
unit='mbar',
get_cmd=self._get_G3)
self.add_parameter('P1',
unit='mbar',
get_cmd=self._get_P1)
self.add_parameter('P2',
unit='mbar',
get_cmd=self._get_P2)
self.add_parameter('V6_valve',
unit='%',
get_cmd=self._get_V6_valve,
set_cmd=self._set_V6_valve)
self.add_parameter('V12A_valve',
unit='%',
get_cmd=self._get_V12A_valve,
set_cmd=self._set_V12A_valve)
self.add_parameter('still_status',
get_cmd=self._get_still_status)
self.add_parameter('sorb_status',
get_cmd=self._get_sorb_status)
self.add_parameter('still_power',
unit='mW',
get_cmd=self._get_still_power,
set_cmd=self._set_still_power)
self.add_parameter('sorb_temp',
unit='K',
get_cmd=self._get_sorb_temp,
set_cmd=self._set_sorb_temp)
self.add_parameter('remote_status',
get_cmd=self._get_remote_status,
set_cmd=self._set_remote_status,
vals=vals.Ints())
for valve in self._valve_map:
self.add_parameter('V%s_valve' % self._valve_map[valve],
get_cmd=partial(
self._get_valve_status, valve=valve),
set_cmd=partial(self._set_valve_status, valve=valve))
[docs]
def get_all(self):
"""
Reads all implemented parameters from the instrument,
and updates the wrapper.
"""
log.info('reading all settings from instrument')
self.snapshot(update=True)
# Functions
def _execute(self, message):
"""
Write a command to the device
Args:
message (str) : write command for the device
"""
log.info('Send the following command to the device: %s' % message)
self.visa_handle.write('@%s%s' % (self._number, message))
sleep(70e-3) # wait for the device to be able to respond
result = self._read()
if result.find('?') >= 0:
print("Error: Command %s not recognized" % message)
else:
return result
def _read(self):
"""
Reads the total bytes in the buffer and outputs as a string.
Returns:
message (str)
"""
bytes_in_buffer = self.visa_handle.bytes_in_buffer
with(self.visa_handle.ignore_warning(pyvisa.constants.VI_SUCCESS_MAX_CNT)):
mes = self.visa_handle.visalib.read(
self.visa_handle.session, bytes_in_buffer)
mes = str(mes[0].decode())
return mes
[docs]
def identify(self):
"""Identify the device
Returns:
a string of the form ``'IGH Version 3.02 (c) OXFORD 1998\\r'``
"""
log.info('Identify the device')
return self._execute('V')
[docs]
def remote(self):
"""Set control to remote and unlocked"""
log.info('Set control to remote and unlocked')
self.remote_status(3)
[docs]
def local(self):
"""Set control to local and unlocked"""
log.info('Set control to local and unlocked')
self.remote_status(2)
[docs]
def close(self):
"""Safely close connection"""
log.info('Closing IPS120 connection')
self.local()
super().close()
[docs]
def get_idn(self):
"""
Overides the function of Instrument since IPS120 does not support `*IDN?`
This string is supposed to be a comma-separated list of vendor, model,
serial, and firmware, but semicolon and colon are also common
separators so we accept them here as well.
Returns:
A dict containing vendor, model, serial, and firmware.
"""
identity = self.identify()
idparts = [identity[24:30], identity[:3], None, identity[15:19]]
return dict(zip(('vendor', 'model', 'serial', 'firmware'), idparts))
def _get_remote_status(self):
"""
Get remote control status
Returns:
result(str) :
"Local & locked",
"Remote & locked",
"Local & unlocked",
"Remote & unlocked",
"Auto-run-down",
"Auto-run-down",
"Auto-run-down",
"Auto-run-down"
"""
log.info('Get remote control status')
result = self._execute('X')
val_mapping = {0: "Local and locked",
1: "Remote and locked",
2: "Local and unlocked",
3: "Remote and unlocked",
4: "Auto-run-down",
5: "Auto-run-down",
6: "Auto-run-down",
7: "Auto-run-down"}
return val_mapping[int(result[5])]
def _set_remote_status(self, mode):
"""
Set remote control status.
Args:
mode(int) :
0 : "Local and locked",
1 : "Remote and locked" (not available),
2 : "Local and unlocked",
3 : "Remote and unlocked"
"""
status = {
0: "Local and locked",
2: "Local and unlocked",
3: "Remote and unlocked",
}
if status.__contains__(mode):
log.info('Setting remote control status to %s' %
status.get(
mode,
"Unknown"))
self._execute('C%s' % mode)
else:
print('Invalid mode inserted: %s' % mode)
def _get_one_K_pot_temp(self):
"""
Get 1K Pot Temperature from device.
Output:
result (float) : 1K Pot Temperature in mK
"""
log.info('Read 1K Pot Temperature')
result = self._execute('R2')
return float(result.replace('R', '')) / 1000
def _get_mix_chamber_temp(self):
"""
Get Mix Chamber Temperature
Output:
result (float) : Mix Chamber Temperature in mK
"""
log.info('Read Mix Chamber Temperature')
result = self._execute('R3')
return 1e-3 * float(result.replace('R', ''))
[docs]
def set_mix_chamber_heater_mode(self, mode):
"""
0 : off
1 : fixed heater power
2 : temperature control
"""
log.info('Setting Mix Chamber Power control')
self._execute('A%i' % mode)
[docs]
def set_mix_chamber_heater_power_range(self, mode):
"""
1 : 2uW
2 : 20uW
3 : 200uW
4 : 2mW
5 : 20mW
"""
log.info('Setting Mix Chamber Power range')
self._execute('E%i' % mode)
def _set_mix_chamber_temp(self, temperature):
"""
Temperature in kelvin
Between 0 and 2K
"""
T = round(temperature / 0.1e-3)
log.info('Setting Mix Chamber Temperature')
self._execute('T%i' % T)
def _get_G1(self):
"""
Get the pressure indicated by G1
Output:
result (float) : G1 pressure in mbar
"""
log.info('Read G1')
result = self._execute('R14')
return float(result.replace('R', '')) / 10
def _get_G2(self):
"""
Get the pressure indicated by G2
Output:
result (float) : G2 pressure in mbar
"""
log.info('Read G2')
result = self._execute('R15')
return float(result.replace('R', '')) / 10
def _get_G3(self):
"""
Get the pressure indicated by G3
Output:
result (float) : G3 pressure in mbar
"""
log.info('Read G3')
result = self._execute('R16')
return float(result.replace('R', '')) / 10
def _get_P1(self):
"""
Get the pressure indicated by P1
Output:
result (float) : P1 pressure in mbar
"""
log.info('Read P1')
result = self._execute('R20')
return float(result.replace('R', ''))
def _get_P2(self):
"""
Get the pressure indicated by P2
Output:
result (float) : P2 pressure in mbar
"""
log.info('Read P2')
result = self._execute('R21')
return float(result.replace('R', ''))
def _get_valve_status(self, valve):
"""
Return the status of the valve number "valve" where valve must be a number between 1 and 20 (self._map_valve)
Output: 'On' or 'off'
"""
result = self._execute('X')[7:15]
# change the hexadecimal number to a 20 bit string
full_status = numpy.binary_repr(int(result, 16), width=20)
# reverse the order of the binary string
full_status = full_status[::-1]
status = full_status[valve - 1]
val_mapping = {0: 'off', 1: 'on'}
return val_mapping[int(status)]
def _set_valve_status(self, status, valve):
"""
Return the status of the valve number "valve" where valve must be a number between 1 and 20 (self._map_valve)
status: 0 for off and 1 for on
"""
self.remote()
log.info('Setting valve %s status' % self._valve_map[valve])
self._execute('P%i' % (2 * valve + numpy.mod(status + 1, 2)))
self.local()
def _set_V6_valve(self, status):
"""
This set the opening of the stepper valve 6. Status should be a percentage.
"""
self.remote()
log.info('Setting valve 6 status')
self._execute('G%i' % int(10 * status))
self.local()
def _get_V6_valve(self):
"""
Return the opening of valve 6.
Output:
result(float): Opening of V6 valve in percent
"""
result = self._execute('R7')
return float(result.replace('R', '')) / 10
def _set_V12A_valve(self, status):
"""
This set the opening of the stepper valve 12. Status should be a percentage.
"""
self.remote()
log.info('Setting valve V12A status')
self._execute('H%i' % int(10 * status))
self.local()
def _get_V12A_valve(self):
"""
Return the opening of valve V12A.
Output:
result(float): Opening of valve V12A in percent
"""
result = self._execute('R8')
return float(result.replace('R', '')) / 10
[docs]
def rotate_Nvalve(self, value):
"""
This set the opening of the stepper valve N. Status should be a percentage.
"""
self.remote()
log.info('Setting valve N status')
value = int(value)
if value < 1000:
if value > -1:
self._execute('N%i' % int(value))
self.local()
else:
print('Wrong value....')
else:
print('Wrong value....')
def _get_still_sorb_status(self):
"""Get the the still and sorb status
O0 : both off
O1 : Still ON, sorb OFF
O2 : Still OFF, sorb on T control
O3 : Still ON, sorb ON T control
O4 : Still OFF, sorb on power control
O5 : Still ON, sorb ON power control
"""
result = self._execute('X')
result = result[17:19]
return result
def _set_still_sorb_status(self, status):
"""Get the the still and sorb status
O0 : both off
O1 : Still ON, sorb OFF
O2 : Still OFF, sorb on T control
O3 : Still ON, sorb ON T control
O4 : Still OFF, sorb on power control
O3 : Still ON, sorb ON power control
"""
self.remote()
log.info('Set still and sorb status')
self._execute(status)
self.local()
def _get_still_status(self):
""" get the status of the still (on/off)"""
status = self._get_still_sorb_status()
if (status == 'O0') | (status == 'O2') | (status == 'O4'):
still_status = 0
else:
still_status = 1
val_mapping = {0: 'off', 1: 'on'}
return val_mapping[int(still_status)]
def _get_sorb_status(self):
""" get the status of the still (on/off)"""
status = self._get_still_sorb_status()
if (status == 'O0') | (status == 'O1'):
sorb_status = 0
elif (status == 'O2') | (status == 'O3'):
sorb_status = 1
elif (status == 'O4') | (status == 'O5'):
sorb_status = 2
val_mapping = {0: 'off', 1: 'on T control', 2: 'on P control'}
return val_mapping[sorb_status]
def _get_still_power(self):
""" get the power on the still"""
log.info('Read still power')
result = self._execute('R5')
return float(result.replace('R', '')) / 10
def _get_sorb_temp(self):
""" get the temperature of the sorb"""
log.info('Read sorb temperature')
result = self._execute('R1')
return float(result.replace('R', '')) / 10
def _set_sorb_temp(self, temperature):
""" Temperature in kelvin """
T = round(temperature / 0.1)
status = self._get_still_sorb_status()
self.remote()
if (status == 'O0'): # turn the sorb ON
self._set_still_sorb_status('O2')
elif (status == 'O1'): # turn the sorb ON
self._set_still_sorb_status('O3')
elif (status == 'O2') | (status == 'O3'): # the sorb already on T control
pass
else:
log.error('The sorb must be either OFF or on temperature control')
log.info('Setting sorb temperature')
self._execute('K%i' % T)
self.get_sorb_status()
self.local()
def _set_still_power(self, temperature):
""" power in mW"""
P = round(temperature / 0.1)
status = self._get_still_sorb_status()
self.remote()
if (status == 'O0'): # turn the sorb ON
self._set_still_sorb_status('O1')
elif (status == 'O2'): # turn the sorb ON
self._set_still_sorb_status('O3')
elif (status == 'O4'): # the sorb already on T control
self._set_still_sorb_status('O5')
else:
pass
log.info('Setting still power')
self._execute('S%i' % P)
self._get_still_status()
self.local()