"""Basic drivers for an Oxford Instruments model ITC503 temperature controller
Note that this module does **not** represent an `Instrument` subclass, since
the ITC503 is never used by itself---it is always part of a larger system
driving both a magnet power supply and temperature controllers, and the
temperature controller cannot usually be used in isolation.
"""
from src.instruments.noauto.oxford_common import OxfordCommon
[docs]class ITC503(OxfordCommon):
"""An Oxford Instruments ITC
This is a basic driver for an Oxford Instruments ITC503 (Intelligent
Temperature Controller). It should typically be included in an `Instrument`
class representing a cryostat-magnet system.
Parameters
----------
name : str
A name to identify the instrument.
protocol : {'ISOBUS', 'GPIB', 'Serial', 'Gateway Master', 'Gateway Slave'}
The protocol for communication between the computer and the temperature
controller.
isobusAddress : str
An integer string representing the ISOBUS address, if relevant. An
integer will be accepted and converted.
visaAddress : str
A full VISA resource address (including the bus) to locate the
instrument (e.g. "GPIB0::27").
serialConfig : dict
A dictionary to indicate how to configure a serial port, which is used
with both the 'ISOBUS' and 'Serial' protocols.
"""
def __init__(self, name="Temperatures", protocol='ISOBUS', isobusAddress=0,
visaAddress='GPIB0::23', serialConfig=None):
"""Create a new temperature controller instance.
Initialization for this object really only involves passing all
arguments into the `OxfordCommon` superclass.
"""
super(ITC503, self).__init__(name, protocol, isobusAddress,
visaAddress, serialConfig)
self._controlMode = '0'
self._autoPID = '0'
self._heaterSensor = '1'
self._autoStatus = (False, False)
[docs] def initialize(self):
"""Prepare the temperature controller for use.
Prepare the temperature controller for use by
1. opening the appropriate communication channel;
2. setting the control mode to 'remote and unlocked';
3. setting the auto/manual status to full auto;
4. get the currently set PID values;
5. get the current temperature readings from all sensors; and
6. read the status.
"""
super(ITC503, self).initialize()
self.setControlMode()
self.setAutoStatus()
self.getPID()
self.getTemperatures()
self._readStatus()
def _readStatus(self):
"""Read the temperature controller status.
Returns
-------
dict
A dictionary specifying the status of the temperature controller.
It includes the following keys: 'autoManualStatus', 'controlMode',
'sweepStatus', 'sensor', and 'autoPID'. The meanings are
specified in the Notes section.
Notes
-----
The temperature controller returns a string of the form
``XnAnCnSnnHnLn``. The meanings and values of the parts of this are
described here.
``Xn``: System status
Currently always zero
``An``: Auto/Manual status
- 0: Heater manual, gas manual
- 1: Heater auto, gas manual
- 2: Heater manual, gas auto
- 3: Heater auto, gas auto
``Cn``: Control status
- 0: Local and locked
- 1: Remote and locked
- 2: Local and unlocked
- 3: Remote and unlocked
``Snn``: Sweep status
- nn=0: Sweep not running
- nn=2P-1: Sweeping to step P
- nn=2P: Holding at step P
``Hn``: Control sensor
- 1: Sensor 1
- 2: Sensor 2
- 3: Sensor 3
``Ln``: Auto-PID status
- 0: Disable use of auto-PID
- 1: Enable use of auto-PID
"""
status = self.communicate('X')
if status[3] == '0':
self._autoStatus = (False, False)
elif status[3] == '1':
self._autoStatus = (True, False)
elif status[3] == '2':
self._autoStatus = (False, True)
else:
self._autoStatus = (True, True)
self._controlMode = status[5]
self._heaterSensor = status[10]
self._autoPID = status[12]
[docs] def setControlMode(self, controlMode='3'):
"""Set the control mode for the temperature controller.
Parameters
----------
controlMode : str, optional
An integer string representing the desired control mode. Allowed
values are the following.
- '0': Local and locked (power-up state).
- '1': Remote and locked.
- '2': Local and unlocked.
- '3': Remote and unlocked (default).
"""
self.communicate('C' + controlMode)
self._controlMode = controlMode
[docs] def getAutoStatus(self):
"""Return whether the heater and needle valve are in automatic mode.
Returns
-------
bool
Whether the heater is in automatic mode.
bool
Whether the needle valve (gas controller) is in automatic mode.
"""
return self._autoStatus
[docs] def setAutoStatus(self, heater=True, needleValve=True):#status='3'):
"""Set the auto/manual status of the heater and gas controller.
Parameters
----------
heater : bool
Whether the temperature controller's heater should be placed in
automatic mode. If `False`, the heater will be placed in manual
mode.
needleValve : bool
Whether the temperature controller's needle valve (gas flow
controller) should be placed in automatic mode. If `False`, the
needle valve will be placed in manual mode.
"""
if heater == False and needleValve == False:
status = '0'
elif heater == True and needleValve == False:
status = '1'
elif heater == False and needleValve == True:
status = '2'
else:
status = '3'
self.communicate('A' + status)
self._autoStatus = (heater, needleValve)
[docs] def setP(self, newP):
"""Set the PID values for the temperature controller.
Parameters
----------
newP : float
The proportional band in Kelvin, to a resolution of 0.001 K.
"""
self.communicate('P%.3f' % newP)
[docs] def setI(self, newI):
"""Set the PID values for the temperature controller.
Parameters
----------
newI : float
The integral action time in minutes. Values between 0 and
140 minutes (inclusive), in steps of 0.1 minutes, are accepted.
"""
self.communicate('I%.1f' % newI)
[docs] def setD(self, newD):
"""Set the PID values for the temperature controller.
Parameters
----------
newD : float
The derivative action time in minutes. The allowed range is
0 to 273 minutes.
"""
self.communicate('D%.1f' % newD)
[docs] def setPID(self, newP=None, newI=None, newD=None):
"""Set the PID values for the temperature controller.
Parameters
----------
newP : float, optional
The proportional band in Kelvin, to a resolution of 0.001 K.
newI : float, optional
The integral action time in minutes. Values between 0 and
140 minutes (inclusive), in steps of 0.1 minutes, are accepted.
newD : float, optional
The derivative action time in minutes. The allowed range is
0 to 273 minutes.
"""
self.communicate('P%.3f' % newP)
self.communicate('I%.1f' % newI)
self.communicate('D%.1f' % newD)
[docs] def getHeaterSensor(self):
"""Return which sensor is currently active.
Returns
-------
str
An integer string, '1', '2', or '3', indicating which sensor is
currently being used to control the temperature(s).
"""
return self._heaterSensor
[docs] def setHeaterSensor(self, sensor='1'):
"""Set the active temperature sensor.
Parameters
----------
sensor : str
An integer string representing the sensor to activate. Allowed
values are '1', '2', and '3', the meanings of which should be
obvious.
"""
self.communicate('H' + sensor)
self._heaterSensor = sensor
[docs] def toggleAutoPid(self, autoPID='0'):
"""Set the auto-PID status of the temperature controller.
If a PID table has been programmed into the instrument, you can enable
auto-PID, so that the PID values will be automatically chosen based
on the temperature range.
.. warning:: If a PID table has not been programmed, attempting to
enable auto-PID will return an error.
Parameters
----------
autoPID : str
An integer string representing the desired auto-PID status. The
allowed values are below.
- 0: Disable auto-PID
- 1: Enable auto-PID
This method also accepts a boolean value for `autoPID`, and this
value would be interpreted in the obvious way.
"""
if isinstance(autoPID, bool):
if autoPID:
autoPID = '1'
else:
autoPID = '0'
self.communicate('L' + autoPID)
self._autoPID = autoPID
[docs] def getAutoPID(self):
"""Return whether auto-PID is enabled.
Returns
-------
bool
Whether the ITC is configured for auto-PID.
"""
return self._autoPID == '1'
[docs] def setTemperature(self, temperature):
"""Set the target temperature for the controller.
Set the target temperature for the currently-selected sensor to
`temperature`, and begin moving toward the setpoint.
.. note:: If a sweep is in progress, the sweep will override the value
set by this command.
Parameters
----------
temperature : float
The temperature setpoint for the currently-enabled sensor.
"""
self.communicate('T%.4s' % temperature)
[docs] def setMaximumHeaterVoltage(self, voltage):
"""Set the maximum voltage for the currently controlled heater.
Parameters
----------
voltage : float
The largest voltage allowed to be supplied to the heater.
"""
self.communicate('M%d' % (voltage * 10))
[docs] def getPID(self):
"""Get the PID values for the temperature controller.
Return the values for the proportional band, the integral action time,
and the derivative action time which the temperature controller is
currently using.
Returns
-------
tuple of float
A tuple of floats containing, in order, P, I, and D.
"""
return (float(self.communicate('R8')),
float(self.communicate('R9')),
float(self.communicate('R10')))
[docs] def getSetpoint(self):
"""Read the setpoint temperature.
Returns
-------
float
The setpoint temperature in Kelvin.
"""
return float(self.communicate('R0'))
[docs] def getTemperature(self, sensor):
"""Get the temperature measured by the specified sensor.
Parameters
----------
sensor : {'1', '2', '3'}
An integer string representing the temperature sensor from which
to read.
Returns
-------
float
The temperature measured by the specified sensor in Kelvin.
"""
return float(self.communicate('R' + sensor))
[docs] def getTemperatures(self):
"""Get the readings from all three temperature sensors.
Returns
-------
tuple of float
The temperatures measured by sensors 1, 2, and 3, expressed as
floats in a tuple with the obvious order.
"""
return (float(self.communicate('R1')),
float(self.communicate('R2')),
float(self.communicate('R3')))
[docs] def setHeaterOutput(self, output):
"""Set the output for the selected heater.
Parameters
----------
output : float
The desired heater output as a percent of the maximum range.
"""
self.communicate('O%f' % output)