Merge branch 'wip' of gitlab.psi.ch-samenv:samenv/frappy into wip

This commit is contained in:
zolliker 2022-09-12 15:16:57 +02:00
commit 12cb0cdade
13 changed files with 108 additions and 156 deletions

9
cfg/stick/uniax.cfg Normal file
View File

@ -0,0 +1,9 @@
[NODE]
id = uniax stick
class = protocol.router.Router
description = router forwarding to linse-uniax apu
node = linse-uniax:5000
#[INTERFACE]
#uri=tcp://5002

View File

@ -18,7 +18,7 @@ io = drv_io
standby_current=0.1 standby_current=0.1
maxcurrent=1.4 maxcurrent=1.4
acceleration=150. acceleration=150.
movelimit=360 move_limit=360
speed=40 speed=40
encoder_tolerance=3.6 encoder_tolerance=3.6
free_wheeling=0.1 free_wheeling=0.1

View File

@ -10,6 +10,7 @@ description = force control
class = secop_psi.uniax.Uniax class = secop_psi.uniax.Uniax
motor = drv motor = drv
transducer = transducer transducer = transducer
force_offset = 15
#[drv_io] #[drv_io]
#description = #description =
@ -23,10 +24,10 @@ class = secop_psi.trinamic.Motor
# io = drv_io # io = drv_io
uri = tcp://192.168.127.254:3002 uri = tcp://192.168.127.254:3002
standby_current=0.1 standby_current=0.1
maxcurrent=0.2 maxcurrent=0.3
acceleration=150. acceleration=150.
move_limit=5 move_limit=5
safe_current=0.2 safe_current=0.3
speed=40 speed=40
encoder_tolerance=3.6 encoder_tolerance=3.6
free_wheeling=0.1 free_wheeling=0.1
@ -39,6 +40,7 @@ class = secop_psi.dpm.DPM3
uri = tcp://192.168.127.254:3001 uri = tcp://192.168.127.254:3001
digits = 2 digits = 2
scale_factor = 0.0156 scale_factor = 0.0156
offset = 15
[res] [res]
description = temperature on uniax stick description = temperature on uniax stick

View File

@ -47,7 +47,7 @@ class SilentError(CommunicationFailedError):
class HasIO(Module): class HasIO(Module):
"""Mixin for modules using a communicator""" """Mixin for modules using a communicator"""
io = Attached() io = Attached(mandatory=False)
uri = Property('uri for automatic creation of the attached communication module', uri = Property('uri for automatic creation of the attached communication module',
StringType(), default='') StringType(), default='')

View File

@ -18,83 +18,86 @@
# Module authors: # Module authors:
# Daniel Margineda <daniel.margineda@psi.ch> # Daniel Margineda <daniel.margineda@psi.ch>
# ***************************************************************************** # *****************************************************************************
"""WAVE FUNCTION LECROY XX: SIGNAL GENERATOR""" """WAVE FUNCTION LECROY XX: SIGNAL GENERATOR
modifications not tested!
"""
from secop.core import Readable, Parameter, FloatRange, \ from secop.core import Readable, Parameter, FloatRange, \
IntRange, BoolType, EnumType, Module, Property IntRange, BoolType, EnumType, Module, Property, HasIO
class Channel(Module): class Channel(HasIO, Module):
channel = Property('choose channel to manipulate', IntRange(1, 2)) channel = Property('choose channel to manipulate', IntRange(1, 2))
freq = Parameter('frequency', FloatRange(1e-6, 20e6, unit='Hz'), freq = Parameter('frequency', FloatRange(1e-6, 20e6, unit='Hz'),
poll=True, initwrite=True, default=1000) initwrite=True, default=1000)
amp = Parameter('exc_volt_int', FloatRange(0.00, 5, unit='Vrms'), amp = Parameter('exc_volt_int', FloatRange(0.00, 5, unit='Vrms'),
poll=True, readonly=False, initwrite=True, default=0.1) readonly=False, initwrite=True, default=0.1)
offset = Parameter('offset_volt_int', FloatRange(0.0, 10, unit='V'), offset = Parameter('offset_volt_int', FloatRange(0.0, 10, unit='V'),
poll=True, readonly=False, initwrite=True, default=0.0) readonly=False, initwrite=True, default=0.0)
wave = Parameter('type of wavefunction', wave = Parameter('type of wavefunction',
EnumType('WaveFunction', SINE=1, SQUARE=2, RAMP=3, PULSE=4, NOISE=5, ARB=6, DC=7), EnumType('WaveFunction', SINE=1, SQUARE=2, RAMP=3, PULSE=4, NOISE=5, ARB=6, DC=7),
poll=True, readonly=False, default='SINE') readonly=False, default='SINE')
phase = Parameter('signal phase', FloatRange(0, 360, unit='deg'), phase = Parameter('signal phase', FloatRange(0, 360, unit='deg'),
poll=True, readonly=False, initwrite=True, default=0) readonly=False, initwrite=True, default=0)
enabled = Parameter('enable output channel', datatype=EnumType('OnOff', OFF=0, ON=1), enabled = Parameter('enable output channel', datatype=EnumType('OnOff', OFF=0, ON=1),
readonly=False, default='OFF') readonly=False, default='OFF')
symm = Parameter('wavefunction symmetry', FloatRange(0, 100, unit=''), symm = Parameter('wavefunction symmetry', FloatRange(0, 100, unit=''),
poll=True, readonly=False, default=0) readonly=False, default=0)
def read_value(self): def read_value(self):
return self.sendRecv('C%d:BSWV FRQ?' % self.channel) return self.communicate('C%d:BSWV FRQ?' % self.channel)
def write_target(self, value): def write_target(self, value):
self.sendRecv('C%d:BSWV FRQ, %gHz' % (self.channel, value)) self.communicate('C%d:BSWV FRQ, %gHz' % (self.channel, value))
return value return value
# signal wavefunction parameter # signal wavefunction parameter
def read_wave(self): def read_wave(self):
return self.sendRecv('C%d:BSWV WVTP?' % self.channel) return self.communicate('C%d:BSWV WVTP?' % self.channel)
def write_wave(self, value): # string value def write_wave(self, value): # string value
self.sendRecv('C%d:BSWV WVTP, %s' % (self.channel, value.name)) self.communicate('C%d:BSWV WVTP, %s' % (self.channel, value.name))
return value return value
# signal amplitude parameter # signal amplitude parameter
def read_amp(self): def read_amp(self):
return self.sendRecv('C%d:BSWV AMP?' % self.channel) return self.communicate('C%d:BSWV AMP?' % self.channel)
def write_amp(self, value): def write_amp(self, value):
self.sendRecv('C%d:BSWV AMP, %g' % (self.channel, value)) self.communicate('C%d:BSWV AMP, %g' % (self.channel, value))
return value return value
# offset value parameter # offset value parameter
def read_offset(self): def read_offset(self):
return self.sendRecv('C%d:BSWV OFST?' % self.channel) return self.communicate('C%d:BSWV OFST?' % self.channel)
def write_offset(self, value): def write_offset(self, value):
self.sendRecv('C%d:BSWV OFST %g' % (self.channel, value)) self.communicate('C%d:BSWV OFST %g' % (self.channel, value))
return value return value
# channel symmetry # channel symmetry
def read_symm(self): def read_symm(self):
return self.sendRecv('C%d:BSWV SYM?' % self.channel) return self.communicate('C%d:BSWV SYM?' % self.channel)
def write_symm(self, value): def write_symm(self, value):
self.sendRecv('C%d:BSWV SYM %g' % (self.channel, value)) self.communicate('C%d:BSWV SYM %g' % (self.channel, value))
return value return value
# wave phase parameter # wave phase parameter
def read_phase(self): def read_phase(self):
return self.sendRecv('C%d:BSWV PHSE?' % self.channel) return self.communicate('C%d:BSWV PHSE?' % self.channel)
def write_phase(self, value): def write_phase(self, value):
self.sendRecv('C%d:BSWV PHSE %g' % (self.channel, value)) self.communicate('C%d:BSWV PHSE %g' % (self.channel, value))
return value return value
# dis/enable output channel # dis/enable output channel
def read_enabled(self): def read_enabled(self):
return self.sendRecv('C%d: OUTP?' % self.channel) return self.communicate('C%d: OUTP?' % self.channel)
def write_enabled(self, value): def write_enabled(self, value):
self.sendRecv('C%d: OUTP %s' % (self.channel, value.name)) self.communicate('C%d: OUTP %s' % (self.channel, value.name))
return value return value

View File

@ -21,7 +21,7 @@
"""Signal Recovery SR7270: lockin amplifier for AC susceptibility""" """Signal Recovery SR7270: lockin amplifier for AC susceptibility"""
from secop.core import Readable, Parameter, Command, FloatRange, TupleOf, \ from secop.core import Readable, Parameter, Command, FloatRange, TupleOf, \
HasIodev, StringIO, Attached, IntRange, BoolType, EnumType HasIO, StringIO, Attached, IntRange, BoolType, EnumType
class SR7270(StringIO): class SR7270(StringIO):
@ -33,7 +33,7 @@ class SR7270(StringIO):
return reply + ';%d;%d' % tuple(status) return reply + ';%d;%d' % tuple(status)
class XY(HasIodev, Readable): class XY(HasIO, Readable):
x = Attached() x = Attached()
y = Attached() y = Attached()
freq_arg = Attached() freq_arg = Attached()
@ -46,29 +46,29 @@ class XY(HasIodev, Readable):
value = Parameter('X, Y', datatype=TupleOf(FloatRange(unit='V'), FloatRange(unit='V'))) value = Parameter('X, Y', datatype=TupleOf(FloatRange(unit='V'), FloatRange(unit='V')))
freq = Parameter('exc_freq_int', freq = Parameter('exc_freq_int',
FloatRange(0.001, 250e3, unit='Hz'), FloatRange(0.001, 250e3, unit='Hz'),
poll=True, readonly=False, initwrite=True, default=1000) readonly=False, initwrite=True, default=1000)
amp = Parameter('exc_volt_int', amp = Parameter('exc_volt_int',
FloatRange(0.00, 5, unit='Vrms'), FloatRange(0.00, 5, unit='Vrms'),
poll=True, readonly=False, initwrite=True, default=0.1) readonly=False, initwrite=True, default=0.1)
range = Parameter('sensitivity value', FloatRange(0.00, 1, unit='V'), poll=True, default=1) range = Parameter('sensitivity value', FloatRange(0.00, 1, unit='V'), default=1)
irange = Parameter('sensitivity index', IntRange(0, 27), poll=True, readonly=False, default=25) irange = Parameter('sensitivity index', IntRange(0, 27), readonly=False, default=25)
autorange = Parameter('autorange_on', EnumType('autorange', off=0, soft=1, hard=2), autorange = Parameter('autorange_on', EnumType('autorange', off=0, soft=1, hard=2),
readonly=False, default=0, initwrite=True) readonly=False, default=0, initwrite=True)
tc = Parameter('time constant value', FloatRange(10e-6, 100, unit='s'), poll=True, default=0.1) tc = Parameter('time constant value', FloatRange(10e-6, 100, unit='s'), default=0.1)
itc = Parameter('time constant index', IntRange(0, 30), poll=True, readonly=False, initwrite=True, default=14) itc = Parameter('time constant index', IntRange(0, 30), readonly=False, initwrite=True, default=14)
nm = Parameter('noise mode', BoolType(), readonly=False, default=0) nm = Parameter('noise mode', BoolType(), readonly=False, default=0)
phase = Parameter('Reference phase control', FloatRange(-360, 360, unit='deg'), phase = Parameter('Reference phase control', FloatRange(-360, 360, unit='deg'),
poll=True, readonly=False, initwrite=True, default=0) readonly=False, initwrite=True, default=0)
vmode = Parameter('Voltage input configuration', IntRange(0, 3), readonly=False, default=3), vmode = Parameter('Voltage input configuration', IntRange(0, 3), readonly=False, default=3),
# dac = Parameter('output DAC channel value', datatype=TupleOf(IntRange(1, 4), FloatRange(0.0, 5000, unit='mV')), # dac = Parameter('output DAC channel value', datatype=TupleOf(IntRange(1, 4), FloatRange(0.0, 5000, unit='mV')),
# poll=True, readonly=False, initwrite=True, default=(3,0)) # readonly=False, initwrite=True, default=(3,0))
dac = Parameter('output DAC channel value', FloatRange(-10000, 10000, unit='mV'), dac = Parameter('output DAC channel value', FloatRange(-10000, 10000, unit='mV'),
poll=True, readonly=False, initwrite=True, default=0) readonly=False, initwrite=True, default=0)
iodevClass = SR7270 ioClass = SR7270
def comm(self, command): def comm(self, command):
reply, status, overload = self.sendRecv(command).split(';') reply, status, overload = self.communicate(command).split(';')
if overload != '0': if overload != '0':
self.status = self.Status.WARN, 'overload %s' % overload self.status = self.Status.WARN, 'overload %s' % overload
else: else:
@ -200,10 +200,10 @@ class XY(HasIodev, Readable):
class Comp(Readable): class Comp(Readable):
pollerClass = None enablePoll = False
value = Parameter(datatype=FloatRange(unit='V')) value = Parameter(datatype=FloatRange(unit='V'))
class arg(Readable): class arg(Readable):
pollerClass = None enablePoll = False
value = Parameter(datatype=FloatRange(unit='')) value = Parameter(datatype=FloatRange(unit=''))

View File

@ -22,7 +22,7 @@
"""driver for cryotel stirling cryocooler""" """driver for cryotel stirling cryocooler"""
from secop.core import Command, EnumType, FloatRange, HasIodev, Parameter, Drivable, StringIO, StringType from secop.core import Command, EnumType, FloatRange, HasIO, Parameter, Drivable, StringIO, StringType
from secop.errors import CommunicationFailedError, HardwareError from secop.errors import CommunicationFailedError, HardwareError
@ -48,27 +48,27 @@ class CryotelIO(StringIO):
return reply return reply
class Cryo(HasIodev, Drivable): class Cryo(HasIO, Drivable):
value = Parameter('current temperature', FloatRange(unit='deg')) value = Parameter('current temperature', FloatRange(unit='deg'))
target = Parameter('target temperature', FloatRange(unit='deg'), readonly=False) target = Parameter('target temperature', FloatRange(unit='deg'), readonly=False)
mode = Parameter('control mode', EnumType('mode', off=0, power=1, temperature=2), readonly=False, poll=True) mode = Parameter('control mode', EnumType('mode', off=0, power=1, temperature=2), readonly=False)
power = Parameter('power', FloatRange(unit='W'), poll=True) power = Parameter('power', FloatRange(unit='W'))
setpower = Parameter('requested power', FloatRange(unit='W'), default=0) setpower = Parameter('requested power', FloatRange(unit='W'), default=0)
cool_power = Parameter('power for cooling', FloatRange(unit='W'), default=240, readonly=False) cool_power = Parameter('power for cooling', FloatRange(unit='W'), default=240, readonly=False)
hold_power = Parameter('power for holding T', FloatRange(unit='W'), default=120, readonly=False) hold_power = Parameter('power for holding T', FloatRange(unit='W'), default=120, readonly=False)
cool_threshold = Parameter('switch to cool_power once above this value', FloatRange(unit='K'), default=100, readonly=False) cool_threshold = Parameter('switch to cool_power once above this value', FloatRange(unit='K'), default=100, readonly=False)
hold_threshold = Parameter('switch to hold_power once below this value', FloatRange(unit='K'), default=95, readonly=False) hold_threshold = Parameter('switch to hold_power once below this value', FloatRange(unit='K'), default=95, readonly=False)
iodevClass = CryotelIO ioClass = CryotelIO
cnt_inside = 0 cnt_inside = 0
def get(self, cmd): def get(self, cmd):
return float(self._iodev.communicate(cmd)) return float(self.communicate(cmd))
def set(self, cmd, value, check=False): def set(self, cmd, value, check=False):
setcmd = '%s=%.2f' % (cmd, value) setcmd = '%s=%.2f' % (cmd, value)
self._iodev.communicate(setcmd) self.communicate(setcmd)
reply = float(self._iodev.communicate(cmd)) reply = float(self.communicate(cmd))
if check: if check:
if value != reply: if value != reply:
raise HardwareError('illegal reply from %s: %g' % (cmd, reply)) raise HardwareError('illegal reply from %s: %g' % (cmd, reply))

View File

@ -21,10 +21,8 @@
# ***************************************************************************** # *****************************************************************************
"""transducer DPM3 read out""" """transducer DPM3 read out"""
import time
from secop.core import Readable, Parameter, FloatRange, StringIO,\ from secop.core import Readable, Parameter, FloatRange, StringIO,\
HasIodev, IntRange, Done HasIO, IntRange, Done
class DPM3IO(StringIO): class DPM3IO(StringIO):
@ -47,22 +45,22 @@ def float2hex(value, digits):
return '%06X' % intvalue return '%06X' % intvalue
class DPM3(HasIodev, Readable): class DPM3(HasIO, Readable):
OFFSET = 0x8f OFFSET = 0x8f
SCALE = 0x8c SCALE = 0x8c
MAGNITUDE = {'1': 1, '2': 10, '3': 100, '4': 1e3, '5': 1e4, '6': 1e5, MAGNITUDE = {'1': 1, '2': 10, '3': 100, '4': 1e3, '5': 1e4, '6': 1e5,
'9': -1, 'A': -10, 'B': -100, 'C': -1e3, 'D': -1e4, 'E': -1e5} '9': -1, 'A': -10, 'B': -100, 'C': -1e3, 'D': -1e4, 'E': -1e5}
iodevClass = DPM3IO ioClass = DPM3IO
value = Parameter(datatype=FloatRange(unit='N')) value = Parameter(datatype=FloatRange(unit='N'))
digits = Parameter('number of digits for value', IntRange(0, 5), initwrite=True, readonly=False) digits = Parameter('number of digits for value', IntRange(0, 5), initwrite=True, readonly=False)
# Note: we have to treat the units properly. # Note: we have to treat the units properly.
# We got an output of 150 for 10N. The maximal force we want to deal with is 100N, # We got an output of 150 for 10N. The maximal force we want to deal with is 100N,
# thus a maximal output of 1500. 10=150/f # thus a maximal output of 1500. 10=150/f
offset = Parameter('', FloatRange(-1e5, 1e5), readonly=False, poll=True) offset = Parameter('', FloatRange(-1e5, 1e5), readonly=False)
scale_factor = Parameter('', FloatRange(-1e5, 1e5, unit='input_units/N'), readonly=False, poll=True) scale_factor = Parameter('', FloatRange(-1e5, 1e5, unit='input_units/N'), readonly=False)
def query(self, adr, value=None): def query(self, adr, value=None):
if value is not None: if value is not None:
@ -87,7 +85,7 @@ class DPM3(HasIodev, Readable):
else: else:
cmd = "" cmd = ""
cmd = cmd + '*1G3%02X' % adr cmd = cmd + '*1G3%02X' % adr
hexvalue = self._iodev.communicate(cmd) hexvalue = self.communicate(cmd)
if adr == self.SCALE: if adr == self.SCALE:
mag = self.MAGNITUDE[hexvalue[0:1]] mag = self.MAGNITUDE[hexvalue[0:1]]
value = int(hexvalue[1:], 16) value = int(hexvalue[1:], 16)
@ -97,7 +95,7 @@ class DPM3(HasIodev, Readable):
def write_digits(self, value): def write_digits(self, value):
# value defines the number of digits # value defines the number of digits
back_value = self._iodev.communicate('*1F135%02X\r*1G135' % (value + 1)) back_value = self.communicate('*1F135%02X\r*1G135' % (value + 1))
self.digits = int(back_value, 16) - 1 self.digits = int(back_value, 16) - 1
# recalculate proper scale and offset # recalculate proper scale and offset
self.write_scale_factor(self.scale_factor) self.write_scale_factor(self.scale_factor)
@ -105,11 +103,11 @@ class DPM3(HasIodev, Readable):
return Done return Done
def read_digits(self): def read_digits(self):
back_value = self._iodev.communicate('*1G135') back_value = self.communicate('*1G135')
return int(back_value,16) - 1 return int(back_value,16) - 1
def read_value(self): def read_value(self):
return float(self._iodev.communicate('*1B1')) return float(self.communicate('*1B1'))
def read_offset(self): def read_offset(self):
reply = self.query(self.OFFSET) reply = self.query(self.OFFSET)

View File

@ -24,7 +24,7 @@ import time
from secop.datatypes import StringType, FloatRange from secop.datatypes import StringType, FloatRange
from secop.modules import Parameter, Property, Readable from secop.modules import Parameter, Property, Readable
from secop.io import HasIodev, StringIO from secop.io import HasIO, StringIO
class LscIO(StringIO): class LscIO(StringIO):
@ -33,13 +33,13 @@ class LscIO(StringIO):
wait_before = 0.05 wait_before = 0.05
class ResChannel(HasIodev, Readable): class ResChannel(HasIO, Readable):
"""temperature channel on Lakeshore 340""" """temperature channel on Lakeshore 340"""
iodevClass = LscIO ioClass = LscIO
value = Parameter(datatype=FloatRange(unit='Ohm')) value = Parameter(datatype=FloatRange(unit='Ohm'))
channel = Property('the channel A,B,C or D', StringType()) channel = Property('the channel A,B,C or D', StringType())
def read_value(self): def read_value(self):
return self._iodev.communicate('SRDG?%s' % self.channel) return float(self.communicate('SRDG?%s' % self.channel))

View File

@ -1,61 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# *****************************************************************************
# 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:
# Markus Zolliker <markus.zolliker@psi.ch>
# *****************************************************************************
"""PPMS mf proxy"""
import secop_psi.ppms
from secop.core import Drivable, Enum, EnumType, FloatRange, Override, Parameter
from secop.datatypes import StatusType
from secop.proxy import proxy_class
class Field(proxy_class(secop_psi.ppms.Field)):
"""magnetic field"""
# pylint: disable=invalid-name
Status = Enum(Drivable.Status,
PREPARED = 150,
PREPARING = 340,
RAMPING = 370,
FINALIZING = 390,
)
# pylint: disable=invalid-name
PersistentMode = Enum('PersistentMode', persistent=0, driven=1)
ApproachMode = Enum('ApproachMode', linear=0, no_overshoot=1, oscillate=2)
remoteParameters = {
'value':
Override(datatype=FloatRange(-1, 1, unit='T'), poll=True),
'status':
Override(datatype=StatusType(Status), poll=True),
'target':
Override(datatype=FloatRange(-15, 15, unit='T'), poll=True),
'ramp':
Parameter('ramping speed', readonly=False,
datatype=FloatRange(0.064, 1.19, unit='T/min'), poll=True),
'approachmode':
Parameter('how to approach target', readonly=False,
datatype=EnumType(ApproachMode), poll=True),
'persistentmode':
Parameter('what to do after changing field', readonly=False,
datatype=EnumType(PersistentMode), poll=True),
'pollinterval':
Override(visibility=3),
}

View File

@ -32,20 +32,20 @@ from secop.core import Attached, BoolType, FloatRange, IntRange, \
class Temperature(Readable): class Temperature(Readable):
pollerClass = None enablePoll = False
value = Parameter(datatype=FloatRange(unit='degC')) value = Parameter(datatype=FloatRange(unit='degC'))
class Bcomp(Readable): class Bcomp(Readable):
pollerClass = None enablePoll = False
value = Parameter(datatype=FloatRange(unit='T')) value = Parameter(datatype=FloatRange(unit='T'))
range = Parameter('working range', FloatRange(unit='T'), default=0) range = Parameter('working range', FloatRange(unit='T'), default=0)
class Raw(Readable): class Raw(Readable):
pollerClass = None enablePoll = False
value = Parameter(datatype=FloatRange()) value = Parameter(datatype=FloatRange())
@ -62,7 +62,7 @@ class TeslameterBase(Readable):
y = Attached() y = Attached()
z = Attached() z = Attached()
value = Parameter('B vector', poll=True, value = Parameter('B vector',
datatype=TupleOf(FloatRange(unit='T'), FloatRange(unit='T'), FloatRange(unit='T'))) datatype=TupleOf(FloatRange(unit='T'), FloatRange(unit='T'), FloatRange(unit='T')))
usb = Parameter('usb device', StringType(), readonly=False) usb = Parameter('usb device', StringType(), readonly=False)
enabled = Parameter('enable data acq', datatype=BoolType(), readonly=False, default=True) enabled = Parameter('enable data acq', datatype=BoolType(), readonly=False, default=True)
@ -152,7 +152,7 @@ class Teslameter3MH6(TeslameterBase):
range = Parameter('range or 0 for autorange', FloatRange(0, 20, unit='T'), readonly=False, default=0) range = Parameter('range or 0 for autorange', FloatRange(0, 20, unit='T'), readonly=False, default=0)
rate = Parameter('sampling rate', datatype=FloatRange(10, 15000, unit='Hz'), rate = Parameter('sampling rate', datatype=FloatRange(10, 15000, unit='Hz'),
readonly=False, poll=True) readonly=False)
avtime = Parameter('data acquisition time', FloatRange(), default=0) avtime = Parameter('data acquisition time', FloatRange(), default=0)
SAMPLING_RATES = {0xe0: 15000, 0xd0: 7500, 0xc0: 3750, 0xb0: 2000, 0xa1: 1000, SAMPLING_RATES = {0xe0: 15000, 0xd0: 7500, 0xc0: 3750, 0xb0: 2000, 0xa1: 1000,

View File

@ -22,17 +22,17 @@
from secop.datatypes import FloatRange, IntRange, StringType from secop.datatypes import FloatRange, IntRange, StringType
from secop.modules import Drivable, Parameter, Readable from secop.modules import Drivable, Parameter, Readable
from secop.io import HasIodev from secop.io import HasIO
Status = Drivable.Status Status = Drivable.Status
class TempLoop(HasIodev, Drivable): class TempLoop(HasIO, Drivable):
"""temperature channel on Lakeshore 336""" """temperature channel on Lakeshore 336"""
value = Parameter(datatype=FloatRange(unit='K'), default=0, poll=True) value = Parameter(datatype=FloatRange(unit='K'), default=0)
status = Parameter(poll=False) status = Parameter(poll=False)
target = Parameter(datatype=FloatRange(1.0, 402.0, unit='K'), default=1.3, poll=True) target = Parameter(datatype=FloatRange(1.0, 402.0, unit='K'), default=1.3)
tolerance = Parameter('the tolerance', FloatRange(-400, 400), default=1, readonly=False) tolerance = Parameter('the tolerance', FloatRange(-400, 400), default=1, readonly=False)
pollinterval = Parameter(visibility=3) pollinterval = Parameter(visibility=3)
channel = Parameter('the Lakeshore channel', datatype=StringType(), export=False) channel = Parameter('the Lakeshore channel', datatype=StringType(), export=False)

View File

@ -32,7 +32,7 @@ class Uniax(PersistentMixin, Drivable):
value = Parameter(unit='N') value = Parameter(unit='N')
motor = Attached() motor = Attached()
transducer = Attached() transducer = Attached()
limit = Parameter('abs limit of force', FloatRange(0, 150, unit='N'), readonly=False, default=150) limit = Parameter('abs limit of force', FloatRange(0, 190, unit='N'), readonly=False, default=150)
tolerance = Parameter('force tolerance', FloatRange(0, 10, unit='N'), readonly=False, default=0.1) tolerance = Parameter('force tolerance', FloatRange(0, 10, unit='N'), readonly=False, default=0.1)
slope = PersistentParam('spring constant', FloatRange(unit='deg/N'), readonly=False, slope = PersistentParam('spring constant', FloatRange(unit='deg/N'), readonly=False,
default=0.5, persistent='auto') default=0.5, persistent='auto')
@ -41,7 +41,7 @@ class Uniax(PersistentMixin, Drivable):
current_step = Parameter('', FloatRange(unit='deg'), default=0) current_step = Parameter('', FloatRange(unit='deg'), default=0)
force_offset = PersistentParam('transducer offset', FloatRange(unit='N'), readonly=False, default=0, force_offset = PersistentParam('transducer offset', FloatRange(unit='N'), readonly=False, default=0,
initwrite=True, persistent='auto') initwrite=True, persistent='auto')
hysteresis = PersistentParam('force hysteresis', FloatRange(0, 150, unit='N'), readonly=False, default=5, hysteresis = PersistentParam('force hysteresis', FloatRange(0, 190, unit='N'), readonly=False, default=5,
persistent='auto') persistent='auto')
adjusting = Parameter('', BoolType(), readonly=False, default=False, initwrite=True) adjusting = Parameter('', BoolType(), readonly=False, default=False, initwrite=True)
adjusting_current = PersistentParam('current when adjusting force', FloatRange(0, 2.8, unit='A'), readonly=False, adjusting_current = PersistentParam('current when adjusting force', FloatRange(0, 2.8, unit='A'), readonly=False,
@ -71,19 +71,20 @@ class Uniax(PersistentMixin, Drivable):
_find_target = 0 _find_target = 0
def earlyInit(self): def earlyInit(self):
super().earlyInit()
self._zero_pos_tol = {} self._zero_pos_tol = {}
self._action = self.idle self._action = self.idle
def drive_relative(self, step, ntry=3): def drive_relative(self, step, ntry=3):
"""drive relative, try 3 times""" """drive relative, try 3 times"""
mot = self._motor mot = self.motor
mot.read_value() # make sure motor value is fresh mot.read_value() # make sure motor value is fresh
if self.adjusting and abs(step) > self.safe_step: if self.adjusting and abs(step) > self.safe_step:
step = math.copysign(self.safe_step, step) step = math.copysign(self.safe_step, step)
self.current_step = step self.current_step = step
for _ in range(ntry): for _ in range(ntry):
try: try:
self._mot_target = self._motor.write_target(mot.value + step) self._mot_target = self.motor.write_target(mot.value + step)
self._cnt_wrerr = max(0, self._cnt_wrerr - 1) self._cnt_wrerr = max(0, self._cnt_wrerr - 1)
return True return True
except Exception as e: except Exception as e:
@ -92,7 +93,7 @@ class Uniax(PersistentMixin, Drivable):
if self._cnt_wrerr > 5: if self._cnt_wrerr > 5:
raise raise
self.log.warning('motor reset') self.log.warning('motor reset')
self._motor.reset() self.motor.reset()
return False return False
def reset_filter(self, now=0.0): def reset_filter(self, now=0.0):
@ -100,7 +101,7 @@ class Uniax(PersistentMixin, Drivable):
self._filter_start = now or time.time() self._filter_start = now or time.time()
def motor_busy(self): def motor_busy(self):
mot = self._motor mot = self.motor
if mot.isBusy(): if mot.isBusy():
if mot.target != self._mot_target: if mot.target != self._mot_target:
self.action = self.idle self.action = self.idle
@ -162,12 +163,12 @@ class Uniax(PersistentMixin, Drivable):
if force * sign > self.hysteresis or force * sign > target * sign: if force * sign > self.hysteresis or force * sign > target * sign:
if self.motor_busy(): if self.motor_busy():
self.log.info('motor stopped - substantial force detected: %g', force) self.log.info('motor stopped - substantial force detected: %g', force)
self._motor.stop() self.motor.stop()
elif self.init_action(): elif self.init_action():
self.next_action(self.adjust) self.next_action(self.adjust)
return return
if abs(force) > self.hysteresis: if abs(force) > self.hysteresis:
self.set_zero_pos(force, self._motor.read_value()) self.set_zero_pos(force, self.motor.read_value())
self.next_action(self.adjust) self.next_action(self.adjust)
return return
if force * sign < -self.hysteresis: if force * sign < -self.hysteresis:
@ -176,7 +177,7 @@ class Uniax(PersistentMixin, Drivable):
return return
if self.motor_busy(): if self.motor_busy():
if sign * self._find_target < 0: # target sign changed if sign * self._find_target < 0: # target sign changed
self._motor.stop() self.motor.stop()
self.next_action(self.find) # restart find self.next_action(self.find) # restart find
return return
else: else:
@ -184,26 +185,26 @@ class Uniax(PersistentMixin, Drivable):
zero_pos = self.zero_pos(target) zero_pos = self.zero_pos(target)
side_name = 'positive' if target > 0 else 'negative' side_name = 'positive' if target > 0 else 'negative'
if not self.init_action(): if not self.init_action():
if abs(self._motor.target - self._motor.value) > self._motor.tolerance: if abs(self.motor.target - self.motor.value) > self.motor.tolerance:
# no success on last find try, try short and strong step # no success on last find try, try short and strong step
self.write_adjusting(True) self.write_adjusting(True)
self.log.info('one step to %g', self._motor.value + self.safe_step) self.log.info('one step to %g', self.motor.value + self.safe_step)
self.drive_relative(sign * self.safe_step) self.drive_relative(sign * self.safe_step)
return return
if zero_pos is not None: if zero_pos is not None:
self.status = 'BUSY', 'change to %s side' % side_name self.status = 'BUSY', 'change to %s side' % side_name
zero_pos += sign * (self.hysteresis * self.slope - self._motor.tolerance) zero_pos += sign * (self.hysteresis * self.slope - self.motor.tolerance)
if (self._motor.value - zero_pos) * sign < -self._motor.tolerance: if (self.motor.value - zero_pos) * sign < -self.motor.tolerance:
self.write_adjusting(False) self.write_adjusting(False)
self.log.info('change side to %g', zero_pos) self.log.info('change side to %g', zero_pos)
self.drive_relative(zero_pos - self._motor.value) self.drive_relative(zero_pos - self.motor.value)
return return
# we are already at or beyond zero_pos # we are already at or beyond zero_pos
self.next_action(self.adjust) self.next_action(self.adjust)
return return
self.write_adjusting(False) self.write_adjusting(False)
self.status = 'BUSY', 'find %s side' % side_name self.status = 'BUSY', 'find %s side' % side_name
self.log.info('one turn to %g', self._motor.value + sign * 360) self.log.info('one turn to %g', self.motor.value + sign * 360)
self.drive_relative(sign * 360) self.drive_relative(sign * 360)
def free(self, force, target): def free(self, force, target):
@ -293,7 +294,7 @@ class Uniax(PersistentMixin, Drivable):
def read_value(self): def read_value(self):
try: try:
force = self._transducer.read_value() force = self.transducer.read_value()
self._cnt_rderr = max(0, self._cnt_rderr - 1) self._cnt_rderr = max(0, self._cnt_rderr - 1)
except Exception as e: except Exception as e:
self._cnt_rderr += 1 self._cnt_rderr += 1
@ -323,7 +324,7 @@ class Uniax(PersistentMixin, Drivable):
self.log.error(self.status[1]) self.log.error(self.status[1])
return Done return Done
if self.zero_pos(force) is None and abs(force) > self.hysteresis and self._filtered: if self.zero_pos(force) is None and abs(force) > self.hysteresis and self._filtered:
self.set_zero_pos(force, self._motor.read_value()) self.set_zero_pos(force, self.motor.read_value())
self._action(self.value, self.target) self._action(self.value, self.target)
return Done return Done
@ -351,18 +352,18 @@ class Uniax(PersistentMixin, Drivable):
@Command() @Command()
def stop(self): def stop(self):
self._action = self.idle self._action = self.idle
if self._motor.isBusy(): if self.motor.isBusy():
self.log.info('stop motor') self.log.info('stop motor')
self._motor.stop() self.motor.stop()
self.next_action(self.idle) self.next_action(self.idle)
def write_force_offset(self, value): def write_force_offset(self, value):
self.force_offset = value self.force_offset = value
self._transducer.write_offset(value) self.transducer.write_offset(value)
return Done return Done
def write_adjusting(self, value): def write_adjusting(self, value):
mot = self._motor mot = self.motor
if value: if value:
mot_current = self.adjusting_current mot_current = self.adjusting_current
mot.write_move_limit(self.safe_step) mot.write_move_limit(self.safe_step)