"""Basic drivers for an Oxford Instruments model PS120 power supply
Note that this module does **not** represent an `Instrument` subclass, since
the PS120 is never used by itself---it is always part of a larger system
driving both a magnet power supply and temperature controllers, and the
power supply cannot usually be used in isolation.
"""
from src.instruments.noauto.oxford_common import OxfordCommon
from math import copysign
from time import sleep
[docs]class PS120(OxfordCommon):
"""This is a basic driver for an Oxford Instruments model PS120 power
supply. It should 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="Magnet", protocol='ISOBUS', isobusAddress='0',
visaAddress='GPIB0::23', serialConfig=None):
"""Create a new power supply instance.
Initialization for this object really only involves passing all
arguments into the `OxfordCommon` superclass.
"""
super(PS120, self).__init__(name, protocol, isobusAddress,
visaAddress, serialConfig)
self._activity = None
self._polarity1 = None
self._polarity2 = None
[docs] def initialize(self):
"""Prepare the power supply for use.
Prepare the power supply for use by
1. opening the appropriate communication channel;
2. setting the control mode to 'remote and unlocked';
3. setting the activity to 'hold';
4. setting the polarity to 'forward';
5. setting the sweep rate to 0.5 T/min;
6. setting the sweep mode to its default (Tesla, auto,
unaffected); and
7. setting the switch heater to 'Off, magnet at zero'.
"""
self.openCommunication()
self.getStatus()
self.setControlMode()
self.setActivity()
self.setPolarity('1')
self.setSweepRate()
self.setSweepMode()
self.setSwitchHeater()
self.getStatus()
[docs] def getStatus(self):
"""Update the power supply status.
Read the power supply status, and update the local polarity variables
to reflect this change.
Returns
-------
dict
The status dictionary, containing the following items:
'system_status_1', 'system_status_2', 'activity', 'control_mode',
'switch_heater', 'mode_1', 'mode_2', 'polarity_1', and
'polarity_2'. The meanings are described in the Notes section.
Notes
-----
The power supply returns a string of the form ``XmnAnCnHnMmnPmn``. The
meanings and values of the parts of this are described here.
``Xmn``: System status
``m``: Status 1
- 0: Normal
- 1: Quenched
- 2: Over-heated
- 4: Warming up
``n``: Status 2
- 0: Normal
- 1: On positive voltage limit
- 2: On negative voltage limit
- 4: Outside negative current limit
- 8: Outside positive current limit
``An``: Activity
- 0: Hold
- 1: To setpoint
- 2: To zero
- 4: Output clamped
``Cn``: Control status
- 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
``Hn``: Switch heater status
- 0: Off---magnet at zero (switch closed)
- 1: On (switch open)
- 2: Off---magnet at field (switch closed)
- 8: No switch fitted
``Mmn``: Mode
``m``: Mode 1
- 0: Amps, Immediate, Fast
- 1: Tesla, Immediate, Fast
- 2: Amps, Sweep, Fast
- 3: Tesla, Sweep, Fast
- 4: Amps, Immediate, Train
- 5: Tesla, Immediate, Train
- 6: Amps, Sweep, Train
- 7: Tesla, Sweep, Train
``n``: Mode 2
- 0: At rest (output constant)
- 1: Sweeping (output changing)
- 2: Rate limiting (output changing)
- 3: Sweeping and rate limiting (output changing)
``Pmn``: Polarity
``m``: Polarity 1 (see below)
- 0: pos, pos, pos
- 1: pos, pos, neg
- 2: pos, neg, pos
- 3: pos, neg, neg
- 4: neg, pos, pos
- 5: neg, pos, neg
- 6: neg, neg, pos
- 7: neg, neg, neg
``n``: Polarity 2
- 0: Output clamped (transition)
- 1: Forward (verification)
- 2: Reverse (verification)
- 4: Output clamped (requested)
The Polarity 1 value actually contains the values of three separate
polarities: desired, magnet, and commanded (in order). "pos" represents
forward polarity, and "neg" means reverse polarity. The three values
are defined to be
- "desired": the final or target polarity (polarity of the setpoint
current)
- "magnet": the polarity of the power supply last time the magnet
was left persistent
- "commanded": the present polarity of the power supply unless the
output is clamped
"""
status = self.communicate('X')
self._activity = status[4]
self._polarity1 = status[13]
self._polarity2 = status[14]
return {'system_status_1': status[1],
'system status 2': status[2],
'activity': self._activity,
'control_mode': status[6],
'switch_heater': status[8],
'mode_1': status[10],
'mode_2': status[11],
'polarity_1': self._polarity1,
'polarity_2': self._polarity2}
[docs] def setControlMode(self, controlMode='3'):
"""Set the control mode for the power supply.
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)
[docs] def setActivity(self, activity='0'):
"""Set the activity mode of the power supply.
Parameters
----------
activity : str
An integer string representing the desired activity. The accepted
values are listed below.
- '0': Hold at current field (default).
- '1': Ramp the field to the setpoint.
- '2': Ramp the field to zero.
- '4': Clamp the output.
"""
self.communicate('A' + activity)
[docs] def getActivity(self):
"""Get the activity status.
Returns
-------
str
An integer contained in a string representing the current
activity status. The recognized values are these:
- 0: Hold at current field,
- 1: Ramp the field to the setpoint,
- 2: Ramp the field to zero, and
- 4: Clamp the output.
"""
self.getStatus()
return self._activity
[docs] def setPolarity(self, polarity='0'):
"""Set the power supply polarity.
Parameters
----------
polarity : str
A string containing a single integer representing the desired
polarity (or polarity-changing action). The following values are
accepted.
- '0': Do nothing (default).
- '1': Set the polarity to forward.
- '2': Set the polarity to reverse.
- '4': Swap the polarity.
"""
self.communicate('P' + polarity)
self.getStatus()
[docs] def setSweepMode(self, sweepMode='9'):
"""Set the power supply's sweep mode.
The power supply's sweep mode consists of three parameters:
Display:
The instrument's front-panel display units (amps or tesla)
Mode:
Either "immediate" or "sweep"; in "immediate" mode, the power
supply ramps current at its maximum allowed value, while in
"sweep" mode, the power supply uses a user-specified sweep rate
Magnet sweep:
One of two user-defined sweep profiles: "fast" and "train"; the
"fast" mode is entered upon power supply startup
Parameters
----------
sweepMode : str, optional
The integer string code to specify the sweep mode of the power
supply. The allowed codes are the following.
- '0': Amps, Immediate, Fast
- '1': Tesla, Immediate, Fast
- '2': Amps, Sweep, Fast
- '3': Tesla, Sweep, Fast
- '4': Amps, Immediate, Train
- '5': Tesla, Immediate, Train
- '6': Amps, Sweep, Train
- '7': Tesla, Sweep, Train
- '8': Amps, Auto, Unaffected
- '9': Tesla, Auto, Unaffected (default)
"""
self.communicate('M' + sweepMode)
self.getStatus()
[docs] def setSwitchHeater(self, heaterStatus='0', delay=20.0):
"""Set the status of the switch heater.
Turn the switch heater on or off, optionally checking whether it is
safe to do so. Then wait a specified amount of time for the power
supply to carry out the command.
Parameters
----------
heaterStatus : {'0', '1', '2'}, optional
An integer string representing the desired switch heater status.
The following values are accepted.
- '0': Turn the heater off (default).
- '1': Turn the heater on if the power supply current and the
magnet current are equal. Otherwise, do nothing.
- '2': Turn the heater on without checking the currents.
delay : float, optional
The time to wait (in seconds) after commanding the switch heater to
adopt the specified status. The value should be *at least* 15s.
"""
self.communicate('H' + heaterStatus)
sleep(delay)
self.getStatus()
[docs] def setField(self, field):
"""Set the magnetic field in Tesla.
Parameters
----------
field : float
The magnetic field setpoint in Tesla.
"""
self.getStatus()
fieldSign = copysign(1, field)
fieldMagnitude = int(round(abs(field*1000)))
currentlyForward = self._polarity2 == '1'
if fieldSign > 0:
if currentlyForward:
pass
else:
self.setPolarity('1')
else:
if currentlyForward:
self.setPolarity('2')
else:
pass
self.communicate('J' + str(fieldMagnitude))
self.setActivity('1')
[docs] def setSweepRate(self, sweepRate=0.5):
"""Set the magnetic field sweep rate in Tesla/min.
Parameters
----------
sweepRate : float, optional
The desired sweep rate for the magnet in Tesla/min (default 0.5).
"""
self.communicate('T' + int(round(abs(sweepRate*1000))))
[docs] def getField(self):
"""Return the magnetic field in Tesla.
Returns
-------
float
The magnetic field in Tesla.
"""
self.getStatus()
field = float(self.communicate('R7'))/1000
if self._polarity2 == '2':
return (-1)*field
return field
[docs] def getFieldSetpoint(self):
"""Return the field setpoint in Tesla.
Returns
-------
float
The magnetic field setpoint in Tesla.
"""
self.getStatus()
setpointMagnitude = float(self.communicate('R8'))/1000
if int(self._polarity1) >= 4:
return (-1)*setpointMagnitude
return setpointMagnitude
[docs] def getSweepRate(self):
"""Return the field sweep rate in Tesla/min.
Returns
-------
float
The magnetic field sweep rate in Tesla/min.
"""
return float(self.communicate('R9'))/1000