added RP100, ACM1219, and dummy classes, and razorbillUC220T config file
This commit is contained in:
parent
b1f9c74269
commit
ee040ce98a
66
cfg/razorbillUC220T_cfg.py
Normal file
66
cfg/razorbillUC220T_cfg.py
Normal file
@ -0,0 +1,66 @@
|
||||
# call $ bin/frappy-server razorbillUC220T
|
||||
# in frappy directory, with python with frappy libraries installed.
|
||||
|
||||
Node('UC220T.psi.ch',
|
||||
'A Razorbill UC220T controlled by a RP100 high voltage powersupply and a ACM1219 (AD7746) capacitance meter',
|
||||
interface='tcp://5123')
|
||||
|
||||
Mod('io1',
|
||||
'frappy_psi.RP100.RP100IO',
|
||||
'communication',
|
||||
uri='serial:///dev/tty.usbmodem1401?baudrate=9600+bytesize=8+parity=none+stopbits=1')
|
||||
Mod('V1',
|
||||
'frappy_psi.RP100.VoltageChannel',
|
||||
'Voltage Channel 1',
|
||||
temp='T',
|
||||
io='io1',
|
||||
target=Param(min=-200, max=200),
|
||||
max_target=120,
|
||||
min_target=-20,
|
||||
slew_rate=100,
|
||||
channel=1)
|
||||
Mod('V2',
|
||||
'frappy_psi.RP100.VoltageChannel',
|
||||
'Voltage Channel 2',
|
||||
temp='T',
|
||||
io='io1',
|
||||
target=Param(min=-200, max=200),
|
||||
max_target=120,
|
||||
min_target=-20,
|
||||
slew_rate=100,
|
||||
channel=2)
|
||||
|
||||
Mod('io2',
|
||||
'frappy_psi.ACM1219.ACM1219IO',
|
||||
'communication',
|
||||
uri='serial:///dev/tty.usbserial-A700fmAI?baudrate=9600+bytesize=8+parity=none+stopbits=1')
|
||||
Mod('C1C2',
|
||||
'frappy_psi.ACM1219.BothChannels',
|
||||
'Capacitance channels 1 and 2',
|
||||
io='io2')
|
||||
Mod('d',
|
||||
'frappy_psi.ACM1219.Displacement',
|
||||
'razorbill displacement from capacitance',
|
||||
cap='C1C2',
|
||||
channel=1,
|
||||
alpha290K=56.710,
|
||||
d0=95.443,
|
||||
Cp=0.01883,
|
||||
d0_curve={'a':4.21,'b':-0.00157,'c':-3.38e-5,'d':5.28e-8,'e':-6.93e-11},
|
||||
temp='T')
|
||||
Mod('F',
|
||||
'frappy_psi.ACM1219.Force',
|
||||
'razorbill force from capacitance',
|
||||
cap='C1C2',
|
||||
channel=2,
|
||||
alpha290K=374.23,
|
||||
f0=315.63,
|
||||
Cp=0.0755,
|
||||
f0_curve={'a':38.9,'b':-0.0147,'c':-0.000346,'d':8.96e-7,'e':-1.58e-9},
|
||||
temp='T')
|
||||
|
||||
Mod('T',
|
||||
'frappy_psi.dummy.Temp',
|
||||
'dummy T written from client',
|
||||
target=Param(min=1, max=325),
|
||||
)
|
136
frappy_psi/ACM1219.py
Normal file
136
frappy_psi/ACM1219.py
Normal file
@ -0,0 +1,136 @@
|
||||
from frappy.core import Readable, Parameter, FloatRange, HasIO, StringIO, Property, IntRange,\
|
||||
IDLE, BUSY, WARN, ERROR, Drivable, BoolType, Attached, StructOf
|
||||
|
||||
|
||||
class ACM1219IO(StringIO):
|
||||
"""communication with ACM1219"""
|
||||
end_of_line = '\n'
|
||||
wait_before = 0.05
|
||||
identification = [('*IDN?', r'.*')]
|
||||
|
||||
|
||||
class BothChannels(HasIO, Readable):
|
||||
"""read both capacitance channels in multiplex mode"""
|
||||
|
||||
# define the communication class for automatic creation of the IO module
|
||||
ioClass = ACM1219IO
|
||||
|
||||
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||
value = Parameter('output voltage',
|
||||
StructOf(C1=FloatRange(0, 21.096, unit='pF'), C2=FloatRange(0, 21.096, unit='pF'), VT=FloatRange(-1, 1000, unit='')),
|
||||
readonly=True)
|
||||
channels_enabled = Parameter('channel 1 on or off', BoolType(), readonly=False)
|
||||
_ch_enabled = False
|
||||
|
||||
def read_value(self):
|
||||
# using the inherited HasIO.communicate method to send a command and get the reply
|
||||
natempt = 0
|
||||
maxAttempts = 5
|
||||
|
||||
while natempt < maxAttempts:
|
||||
try:
|
||||
reply = self.communicate(f'readMUC')
|
||||
print(reply)
|
||||
reply = reply.split(',')
|
||||
C1 = float(reply[0])
|
||||
C2 = float(reply[1])
|
||||
VT = float(reply[2])
|
||||
return {'C1': C1, 'C2': C2, 'VT': VT}
|
||||
except:
|
||||
''
|
||||
natempt+=1
|
||||
if natempt >= maxAttempts:
|
||||
print('Max attempt reached for reading arduino.')
|
||||
return self.value
|
||||
|
||||
def read_status(self):
|
||||
# code = self.communicate(f'readStatus') # returns tons of data
|
||||
return IDLE, ''
|
||||
|
||||
def read_channels_enabled(self):
|
||||
return self._ch_enabled
|
||||
|
||||
def write_channels_enabled(self, channels_enabled):
|
||||
if channels_enabled:
|
||||
self.communicate(f'setCIN 1,0,00.0,00.0,0,00.0,00.0')
|
||||
self.communicate(f'setCIN 2,0,00.0,00.0,0,00.0,00.0')
|
||||
self._ch_enabled = True
|
||||
else:
|
||||
self.communicate(f'setCIN 0,0,00.0,00.0,0,00.0,00.0')
|
||||
self._ch_enabled = False
|
||||
|
||||
return self.read_channels_enabled()
|
||||
|
||||
|
||||
class Displacement(Readable):
|
||||
|
||||
# attached classes for capacitance and temperature
|
||||
cap = Attached()
|
||||
temp = Attached()
|
||||
|
||||
# internal property to configure the channel
|
||||
channel = Property('the voltage channel for displacement capacitor', datatype=IntRange(1,2))
|
||||
|
||||
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||
value = Parameter('displacement', FloatRange(None, None, unit='um'), readonly=True)
|
||||
alpha290K = Parameter('capacitor constant at 290 K', FloatRange(None, None, unit='um pF'), readonly=True)
|
||||
d0 = Parameter('offset displacement', FloatRange(None, None, unit='um'), readonly=True)
|
||||
Cp = Parameter('parallel capacitance', FloatRange(None, None, unit='pF'), readonly=True)
|
||||
d0_curve = Parameter('calibration curve for offset displacement',
|
||||
StructOf(a=FloatRange(None, None, unit='um'),
|
||||
b=FloatRange(None, None, unit='um/K'),
|
||||
c=FloatRange(None, None, unit='um/K^2'),
|
||||
d=FloatRange(None, None, unit='um/K^3'),
|
||||
e=FloatRange(None, None, unit='um/K^4'),),
|
||||
readonly=True)
|
||||
|
||||
def read_value(self):
|
||||
# get temperature and capacitance
|
||||
temp = self.temp.target
|
||||
cap = self.cap.value[f'C{self.channel}']
|
||||
|
||||
# calculate displacement from temperature and capacitance
|
||||
d0_T = self.d0_curve['a'] + self.d0_curve['b']*temp + self.d0_curve['c']*temp**2 + self.d0_curve['d']*temp**3 + self.d0_curve['e']*temp**4
|
||||
|
||||
disp = self.alpha290K / (cap - self.Cp) - self.d0 - d0_T
|
||||
|
||||
return disp
|
||||
|
||||
|
||||
class Force(Readable):
|
||||
|
||||
# attached classes for capacitance and temperature
|
||||
cap = Attached()
|
||||
temp = Attached()
|
||||
|
||||
# internal property to configure the channel
|
||||
channel = Property('the voltage channel for force capacitor', datatype=IntRange(1,2))
|
||||
|
||||
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
||||
value = Parameter('force', FloatRange(None, None, unit='N'), readonly=True)
|
||||
alpha290K = Parameter('capacitor constant at 290 K', FloatRange(None, None, unit='N pF'), readonly=True)
|
||||
f0 = Parameter('offset force', FloatRange(None, None, unit='N'), readonly=True)
|
||||
Cp = Parameter('parallel capacitance', FloatRange(None, None, unit='pF'), readonly=True)
|
||||
f0_curve = Parameter('calibration curve for offset force',
|
||||
StructOf(a=FloatRange(None, None, unit='N'),
|
||||
b=FloatRange(None, None, unit='N/K'),
|
||||
c=FloatRange(None, None, unit='N/K^2'),
|
||||
d=FloatRange(None, None, unit='N/K^3'),
|
||||
e=FloatRange(None, None, unit='N/K^4'),),
|
||||
readonly=True)
|
||||
|
||||
def read_value(self):
|
||||
# get temperature and capacitance
|
||||
temp = self.temp.target
|
||||
cap = self.cap.value[f'C{self.channel}']
|
||||
|
||||
# calculate force from temperature and capacitance
|
||||
alpha = self.alpha290K * (0.91 + 5e-5*temp + 9e-7*temp**2)
|
||||
f0_T = self.f0_curve['a'] + self.f0_curve['b']*temp + self.f0_curve['c']*temp**2 + self.f0_curve['d']*temp**3 + self.f0_curve['e']*temp**4
|
||||
|
||||
force = alpha / (cap - self.Cp) - self.f0 - f0_T
|
||||
|
||||
return force
|
||||
|
||||
|
||||
|
109
frappy_psi/RP100.py
Normal file
109
frappy_psi/RP100.py
Normal file
@ -0,0 +1,109 @@
|
||||
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?'))
|
12
frappy_psi/dummy.py
Normal file
12
frappy_psi/dummy.py
Normal file
@ -0,0 +1,12 @@
|
||||
from frappy.core import Readable, Parameter, FloatRange, HasIO, StringIO, Property, IntRange,\
|
||||
IDLE, BUSY, WARN, ERROR, Drivable, BoolType, Attached, Writable
|
||||
|
||||
|
||||
class Temp(Writable):
|
||||
"""a voltage output with loop"""
|
||||
|
||||
target = Parameter('target voltage', FloatRange(0, 325, unit='K'),
|
||||
readonly=False)
|
||||
|
||||
def write_target(self, target):
|
||||
self.value = target
|
Loading…
x
Reference in New Issue
Block a user