waiting 0.5 sec before the first communication helps to start properly + added usb dev names for box
204 lines
8.1 KiB
Python
204 lines
8.1 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>
|
|
# *****************************************************************************
|
|
|
|
import time
|
|
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 = ('\r\n', '\r') # ('\n', '\r') ('\r\n', '\r')
|
|
encoding = 'latin-1' # initial reply might not be ascii for a strange reason
|
|
identification = [('*IDN?', r'.*,ACM1219,.*')]
|
|
|
|
def checkHWIdent(self):
|
|
for _ in range(3):
|
|
time.sleep(0.5)
|
|
try:
|
|
self.communicate('*IDN?')
|
|
break
|
|
except Exception:
|
|
pass
|
|
super().checkHWIdent()
|
|
|
|
|
|
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('Capacitance 1 and 2, and VT',
|
|
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('channels 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=False)
|
|
d0 = Parameter('offset displacement', FloatRange(None, None, unit='um'), readonly=False)
|
|
Cp = Parameter('parallel capacitance', FloatRange(None, None, unit='pF'), readonly=False)
|
|
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=False)
|
|
|
|
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=False)
|
|
f0 = Parameter('offset force', FloatRange(None, None, unit='N'), readonly=False)
|
|
Cp = Parameter('parallel capacitance', FloatRange(None, None, unit='pF'), readonly=False)
|
|
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=False)
|
|
|
|
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
|
|
|
|
|
|
class Stress(Readable):
|
|
|
|
# attached classes for displacement
|
|
force = Attached()
|
|
|
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
|
value = Parameter('stress', FloatRange(None, None, unit='GPa'), readonly=True)
|
|
area = Parameter('cross sectional area of sample in mm^2', FloatRange(None, None, unit='mm^2'), readonly=False)
|
|
|
|
def read_value(self):
|
|
return self.force.value / self.area / 1000
|
|
|
|
|
|
class Strain(Readable):
|
|
|
|
# attached classes for displacement
|
|
displacement = Attached()
|
|
|
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
|
value = Parameter('strain', FloatRange(None, None, unit='m/m'), readonly=True)
|
|
L = Parameter('length of sample in mm', FloatRange(None, None, unit='mm'), readonly=False)
|
|
|
|
def read_value(self):
|
|
return self.displacement.value / (1000*self.L)
|
|
|
|
|
|
class YoungsModulus(Readable):
|
|
|
|
# attached classes for displacement
|
|
stress = Attached()
|
|
strain = Attached()
|
|
|
|
# modifying a property of inherited parameters (unit is propagated to the FloatRange datatype)
|
|
value = Parameter('Young\'s modulus', FloatRange(None, None, unit='GPa'), readonly=True)
|
|
|
|
def read_value(self):
|
|
return self.stress.value / self.strain.value
|
|
|