128 lines
5.2 KiB
Python
128 lines
5.2 KiB
Python
# *****************************************************************************
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation; either version 2 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
#
|
|
# Module authors:
|
|
# Paul M. Neves <pmneves@mit.edu>
|
|
# *****************************************************************************
|
|
|
|
from frappy.core import Readable, Parameter, FloatRange, HasIO, StringIO, Property, IntRange,\
|
|
IDLE, BUSY, WARN, ERROR, Drivable, BoolType, Attached
|
|
from ast import literal_eval
|
|
|
|
|
|
class RP100IO(StringIO):
|
|
"""communication with RP100"""
|
|
end_of_line = '\n'
|
|
#wait_before = 0.05
|
|
identification = [('*IDN?', r'Razorbill,.*')]
|
|
|
|
|
|
class VoltageChannel(HasIO, Drivable):
|
|
"""a voltage output with loop"""
|
|
|
|
temp = Attached()
|
|
|
|
# define the communication class for automatic creation of the IO module
|
|
ioClass = RP100IO
|
|
|
|
# internal property to configure the channel
|
|
channel = Property('the voltage channel', datatype=IntRange(1,2))
|
|
|
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
|
value = Parameter('output voltage', FloatRange(-210, 210, unit='V'),
|
|
readonly=True)
|
|
target = Parameter('target voltage', FloatRange(-210, 210, unit='V'),
|
|
readonly=False)
|
|
meas_voltage = Parameter('measured output voltage', FloatRange(-250, 250, unit='V'),
|
|
readonly=True)
|
|
meas_current = Parameter('measured output current', FloatRange(-0.007, 0.007, unit='A'),
|
|
readonly=True)
|
|
max_target = Parameter('max. target', FloatRange(0, 210, unit='V'), readonly=False)
|
|
min_target = Parameter('max. target', FloatRange(-210, 0, unit='V'), readonly=False)
|
|
slew_rate = Parameter('voltage slew rate', FloatRange(0.1e-3, 100e3, unit='V/s'), readonly=False)
|
|
output_state = Parameter('output on or off', BoolType(), readonly=False)
|
|
|
|
def doPoll(self):
|
|
super().doPoll()
|
|
|
|
# calculate temperature dependent voltage limits
|
|
temp = self.temp.target
|
|
if temp > 250:
|
|
self.max_target = 120
|
|
self.min_target = -20
|
|
elif temp >= 100:
|
|
self.max_target = 120
|
|
self.min_target = -50 + (temp-100)/5
|
|
elif temp >= 10:
|
|
self.max_target = 200 - 8*(temp-10)/9
|
|
self.min_target = -200 + 5*(temp-10)/3
|
|
elif temp < 10:
|
|
self.max_target = 200
|
|
self.min_target = -200
|
|
|
|
# if the current voltage exceeds these limits, reduce voltage to max/min
|
|
if self.target > self.max_target:
|
|
self.write_target(self.max_target)
|
|
if self.target < self.min_target:
|
|
self.write_target(self.min_target)
|
|
|
|
def read_value(self):
|
|
# using the inherited HasIO.communicate method to send a command and get the reply
|
|
reply = self.communicate(f'SOUR{self.channel}:VOLT:NOW?')
|
|
return float(reply)
|
|
|
|
def read_status(self):
|
|
while 1:
|
|
code, text = literal_eval(self.communicate(f'SYST:ERR?'))
|
|
if code == 0:
|
|
break
|
|
self.log.warning('got error %d %s', code, text)
|
|
return IDLE, ''
|
|
|
|
def read_target(self):
|
|
# read back the target value
|
|
target = float(self.communicate(f'SOUR{self.channel}:VOLT?'))
|
|
return target
|
|
|
|
def write_target(self, target):
|
|
# write here the target to the hardware
|
|
if target > self.max_target:
|
|
target = self.max_target
|
|
self.log.warning('Attempted to set voltage above maximum allowed voltage. Setting to max allowed instead.')
|
|
if target < self.min_target:
|
|
target = self.min_target
|
|
self.log.warning('Attempted to set voltage below minimum allowed voltage. Setting to min allowed instead.')
|
|
self.communicate(f'SOUR{self.channel}:VOLT {target};*OPC?')
|
|
return self.read_target() # return the read back value
|
|
|
|
def read_slew_rate(self):
|
|
return float(self.communicate(f'SOUR{self.channel}:VOLT:SLEW?'))
|
|
|
|
def write_slew_rate(self, slew_rate):
|
|
self.communicate(f'SOUR{self.channel}:VOLT:SLEW {slew_rate};*OPC?')
|
|
return self.read_slew_rate()
|
|
|
|
def read_output_state(self):
|
|
return bool(self.communicate(f'OUTP{self.channel}?'))
|
|
|
|
def write_output_state(self, output_state):
|
|
self.communicate(f'OUTP{self.channel} {int(output_state)};*OPC?')
|
|
return self.read_slew_rate()
|
|
|
|
def read_meas_voltage(self):
|
|
return float(self.communicate(f'MEAS{self.channel}:VOLT?'))
|
|
|
|
def read_meas_current(self):
|
|
return float(self.communicate(f'MEAS{self.channel}:CURR?')) |