improvements on PPMS and LS370

- improved machanism for 10 K waiting
- fixed an issue with auto range

Change-Id: Ia6454141917893f0e5c6c4351df3a864942bb629
This commit is contained in:
zolliker 2020-06-25 12:02:17 +02:00
parent 1655e252fc
commit ab00c45db0
3 changed files with 64 additions and 29 deletions

View File

@ -50,7 +50,7 @@ scan = IOHandler('scan', 'SCAN?', '%d,%d')
STATUS_TEXT = {0: ''} STATUS_TEXT = {0: ''}
for bit, text in enumerate('CS_OVL VCM_OVL VMIX_OVL R_OVER R_UNDER T_OVER T_UNDER'.split()): for bit, text in enumerate('CS_OVL VCM_OVL VMIX_OVL VDIF_OVL R_OVER R_UNDER T_OVER T_UNDER'.split()):
for i in range(1 << bit, 2 << bit): for i in range(1 << bit, 2 << bit):
STATUS_TEXT[i] = text STATUS_TEXT[i] = text
@ -177,10 +177,10 @@ class ResChannel(HasIodev, Readable):
return result return result
def read_status(self): def read_status(self):
if self.channel != self._main.channel:
return Done
if not self.enabled: if not self.enabled:
return [self.Status.DISABLED, 'disabled'] return [self.Status.DISABLED, 'disabled']
if self.channel != self._main.channel:
return Done
result = int(self.sendRecv('RDGST?%d' % self.channel)) result = int(self.sendRecv('RDGST?%d' % self.channel))
result &= 0x37 # mask T_OVER and T_UNDER (change this when implementing temperatures instead of resistivities) result &= 0x37 # mask T_OVER and T_UNDER (change this when implementing temperatures instead of resistivities)
statustext = STATUS_TEXT[result] statustext = STATUS_TEXT[result]
@ -191,10 +191,11 @@ class ResChannel(HasIodev, Readable):
def analyze_rdgrng(self, iscur, exc, rng, autorange, excoff): def analyze_rdgrng(self, iscur, exc, rng, autorange, excoff):
result = dict(range=rng) result = dict(range=rng)
if autorange: if autorange:
result['auotrange'] = 'hard' result['autorange'] = 'hard'
elif self.autorange == 'hard': #elif self.autorange == 'hard':
result['autorange'] = 'soft' # result['autorange'] = 'soft'
# else: do not change autorange # else: do not change autorange
self.log.info('%s range %r %r %r' % (self.name, rng, autorange, self.autorange))
if excoff: if excoff:
result.update(iexc=0, vexc=0) result.update(iexc=0, vexc=0)
elif iscur: elif iscur:
@ -225,6 +226,7 @@ class ResChannel(HasIodev, Readable):
if change.autorange == 'soft': if change.autorange == 'soft':
if rng < self.minrange: if rng < self.minrange:
rng = self.minrange rng = self.minrange
self.autorange = change.autorange
return iscur, exc, rng, autorange, excoff return iscur, exc, rng, autorange, excoff
def analyze_inset(self, on, dwell, pause, curve, tempco): def analyze_inset(self, on, dwell, pause, curve, tempco):

View File

@ -209,8 +209,18 @@ class UserChannel(Channel):
'no': 'no':
Property('channel number', Property('channel number',
datatype=IntRange(0, 0), export=False, default=0), datatype=IntRange(0, 0), export=False, default=0),
'linkenable':
Property('name of linked channel for enabling',
datatype=StringType(), export=False, default=''),
} }
def write_enabled(self, enabled):
other = self._iodev.modules.get(self.linkenable, None)
if other:
other.enabled = enabled
return enabled
class DriverChannel(Channel): class DriverChannel(Channel):
drvout = IOHandler('drvout', 'DRVOUT? %(no)d', '%d,%g,%g') drvout = IOHandler('drvout', 'DRVOUT? %(no)d', '%d,%g,%g')
@ -410,8 +420,11 @@ class Temp(PpmsMixin, Drivable):
Parameter('intermediate set point', Parameter('intermediate set point',
datatype=FloatRange(1.7, 402.0, unit='K'), handler=temp), datatype=FloatRange(1.7, 402.0, unit='K'), handler=temp),
'ramp': 'ramp':
Parameter('ramping speed', readonly=False, handler=temp, default=0, Parameter('ramping speed', readonly=False, default=0,
datatype=FloatRange(0, 20, unit='K/min')), datatype=FloatRange(0, 20, unit='K/min')),
'workingramp':
Parameter('intermediate ramp value',
datatype=FloatRange(0, 20, unit='K/min'), handler=temp),
'approachmode': 'approachmode':
Parameter('how to approach target!', readonly=False, handler=temp, Parameter('how to approach target!', readonly=False, handler=temp,
datatype=EnumType(ApproachMode)), datatype=EnumType(ApproachMode)),
@ -458,6 +471,7 @@ class Temp(PpmsMixin, Drivable):
general_stop = False general_stop = False
_cool_deadline = 0 _cool_deadline = 0
_wait_at10 = False _wait_at10 = False
_ramp_at_limit = False
def update_value_status(self, value, packed_status): def update_value_status(self, value, packed_status):
"""update value and status""" """update value and status"""
@ -469,10 +483,10 @@ class Temp(PpmsMixin, Drivable):
status = self.STATUS_MAP.get(status_code, (self.Status.ERROR, 'unknown status code %d' % status_code)) status = self.STATUS_MAP.get(status_code, (self.Status.ERROR, 'unknown status code %d' % status_code))
now = time.time() now = time.time()
if value > 11: if value > 11:
# when starting from T > 40, this will be 15 min. # when starting from T > 50, this will be 15 min.
# when starting from lower T, it will be less # when starting from lower T, it will be less
# when ramping with 2 K/min or less, the deadline is now # when ramping with 2 K/min or less, the deadline is now
self._cool_deadline = max(self._cool_deadline, now + min(30, value - 10) * 30) # 30 sec / K self._cool_deadline = max(self._cool_deadline, now + min(40, value - 10) * 30) # 30 sec / K
elif self._wait_at10: elif self._wait_at10:
if now > self._cool_deadline: if now > self._cool_deadline:
self._wait_at10 = False self._wait_at10 = False
@ -506,24 +520,40 @@ class Temp(PpmsMixin, Drivable):
self._expected_target_time = 0 self._expected_target_time = 0
self.status = status self.status = status
def analyze_temp(self, setpoint, ramp, approachmode): def analyze_temp(self, setpoint, workingramp, approachmode):
if (setpoint, workingramp, approachmode) == self._last_settings:
# update parameters only on change, as 'ramp' and 'approachmode' are
# not always sent to the hardware
return {}
self._last_settings = setpoint, workingramp, approachmode
if setpoint != 10 or not self._wait_at10: if setpoint != 10 or not self._wait_at10:
self.log.debug('read back target %g %r' % (setpoint, self._wait_at10))
self.target = setpoint self.target = setpoint
result = dict(setpoint=setpoint) if workingramp != 2 or not self._ramp_at_limit:
# we update ramp and approachmode only at init self.log.debug('read back ramp %g %r' % (workingramp, self._ramp_at_limit))
if self.ramp == 0: self.ramp = workingramp
result['ramp'] = ramp result = dict(setpoint=setpoint, workingramp=workingramp)
result['approachmode'] = approachmode self.log.debug('analyze_temp %r %r' % (result, (self.target, self.ramp)))
return result return result
def change_temp(self, change): def change_temp(self, change):
if 10 >= self.value > change.setpoint:
ramp = min(2, change.ramp)
print('ramplimit', change.ramp, self.value, ramp)
else:
ramp = change.ramp ramp = change.ramp
self.calc_expected(change.setpoint, ramp) setpoint = change.setpoint
return change.setpoint, ramp, change.approachmode wait_at10 = False
ramp_at_limit = False
if self.value > 11:
if setpoint <= 10:
wait_at10 = True
setpoint = 10
elif self.value > setpoint:
if ramp >= 2:
ramp = 2
ramp_at_limit = True
self._wait_at10 = wait_at10
self._ramp_at_limit = ramp_at_limit
self.calc_expected(setpoint, ramp)
self.log.debug('change_temp v %r s %r r %r w %r l %r' % (self.value, setpoint, ramp, wait_at10, ramp_at_limit))
return setpoint, ramp, change.approachmode
def write_target(self, target): def write_target(self, target):
self._stopped = False self._stopped = False
@ -532,24 +562,22 @@ class Temp(PpmsMixin, Drivable):
self._status_before_change = self.status self._status_before_change = self.status
self.status = (self.Status.BUSY, 'changed target') self.status = (self.Status.BUSY, 'changed target')
self._last_change = time.time() self._last_change = time.time()
if self.value > 10 > target and self.ramp > 2:
self._wait_at10 = True
self.temp.write(self, 'setpoint', 10)
else:
self._wait_at10 = False
self.temp.write(self, 'setpoint', target) self.temp.write(self, 'setpoint', target)
self.log.debug('write_target %s' % repr((self.setpoint, target, self._wait_at10)))
return target return target
def write_approachmode(self, value): def write_approachmode(self, value):
if self.isDriving(): if self.isDriving():
self.temp.write(self, 'approachmode', value) self.temp.write(self, 'approachmode', value)
return Done return Done
self.approachmode = value
return None # do not execute TEMP command, as this would trigger an unnecessary T change return None # do not execute TEMP command, as this would trigger an unnecessary T change
def write_ramp(self, value): def write_ramp(self, value):
if self.isDriving(): if self.isDriving():
self.temp.write(self, 'ramp', value) self.temp.write(self, 'ramp', value)
return Done return Done
# self.ramp = value
return None # do not execute TEMP command, as this would trigger an unnecessary T change return None # do not execute TEMP command, as this would trigger an unnecessary T change
def calc_expected(self, target, ramp): def calc_expected(self, target, ramp):
@ -656,6 +684,7 @@ class Field(PpmsMixin, Drivable):
self.status = status self.status = status
def analyze_field(self, target, ramp, approachmode, persistentmode): def analyze_field(self, target, ramp, approachmode, persistentmode):
# print('last_settings tt %s' % repr(self._last_settings))
if (target, ramp, approachmode, persistentmode) == self._last_settings: if (target, ramp, approachmode, persistentmode) == self._last_settings:
# we update parameters only on change, as 'ramp' and 'approachmode' are # we update parameters only on change, as 'ramp' and 'approachmode' are
# not always sent to the hardware # not always sent to the hardware
@ -669,6 +698,7 @@ class Field(PpmsMixin, Drivable):
def write_target(self, target): def write_target(self, target):
if abs(self.target - self.value) <= 2e-5 and target == self.target: if abs(self.target - self.value) <= 2e-5 and target == self.target:
self.target = target
return None # avoid ramping leads return None # avoid ramping leads
self._status_before_change = self.status self._status_before_change = self.status
self._stopped = False self._stopped = False
@ -679,6 +709,7 @@ class Field(PpmsMixin, Drivable):
def write_persistentmode(self, mode): def write_persistentmode(self, mode):
if abs(self.target - self.value) <= 2e-5 and mode == self.persistentmode: if abs(self.target - self.value) <= 2e-5 and mode == self.persistentmode:
self.persistentmode = mode
return None # avoid ramping leads return None # avoid ramping leads
self._last_change = time.time() self._last_change = time.time()
self._status_before_change = self.status self._status_before_change = self.status
@ -688,6 +719,7 @@ class Field(PpmsMixin, Drivable):
return Done return Done
def write_ramp(self, value): def write_ramp(self, value):
self.ramp = value
if self.isDriving(): if self.isDriving():
self.field.write(self, 'ramp', value) self.field.write(self, 'ramp', value)
return Done return Done
@ -805,6 +837,7 @@ class Position(PpmsMixin, Drivable):
if self.isDriving(): if self.isDriving():
self.move.write(self, 'speed', value) self.move.write(self, 'speed', value)
return Done return Done
self.speed = value
return None # do not execute MOVE command, as this would trigger an unnecessary move return None # do not execute MOVE command, as this would trigger an unnecessary move
def do_stop(self): def do_stop(self):

View File

@ -53,7 +53,7 @@ class PpmsSim:
def __init__(self): def __init__(self):
self.status = NamedList('t mf ch pos', 1, 1, 1, 1) self.status = NamedList('t mf ch pos', 1, 1, 1, 1)
self.st = 0x1111 self.st = 0x1111
self.t = 200 self.t = 15
self.temp = NamedList('target ramp amode', 200., 1, 0, fast=self.t, delay=10) self.temp = NamedList('target ramp amode', 200., 1, 0, fast=self.t, delay=10)
self.mf = 100 self.mf = 100
self.field = NamedList('target ramp amode pmode', 0, 50, 0, 0) self.field = NamedList('target ramp amode pmode', 0, 50, 0, 0)