changes for leiden dil

- fixes on frappy_psiahcapbridge
- fixes on cfg files
- add cp1000 sea cfg files
This commit is contained in:
2025-11-17 16:01:51 +01:00
parent e786c5ec77
commit 495ad01ff6
10 changed files with 838 additions and 471 deletions

View File

@@ -3,20 +3,16 @@ Node('ah2700.frappy.psi.ch',
) )
Mod('cap_io', Mod('cap_io',
'frappy_psi.ah2700.Ah2700IO', 'frappy_psi.ahcapbridge.IO', '',
'', uri='linse-leiden-ts:3002'
uri='linse-976d-ts:3006',
) )
Mod('cap', Mod('cap',
'frappy_psi.ah2700.Capacitance', 'frappy_psi.ahcapbridge.AH2700',
'capacitance', 'capacitance',
io='cap_io', io='cap_io',
loss_module = 'loss',
freq_module = 'freq',
) )
Mod('loss',
'frappy_psi.parmod.Par',
'loss parameter',
read='cap.loss',
unit='deg',
)

31
cfg/addons/ahtwo_cfg.py Normal file
View File

@@ -0,0 +1,31 @@
Node('ahtwo.frappy.psi.ch',
'Andeen Hagerlin 2700 and 2550 Capacitance Bridges',
)
# TODO: adapt names (cap, cap2) to your experiment
Mod('cap_io',
'frappy_psi.ahcapbridge.IO', '',
uri='linse-leiden-ts:3002'
)
Mod('cap',
'frappy_psi.ahcapbridge.AH2700',
'capacitance',
io='cap_io',
loss_module = 'loss',
freq_module = 'freq',
)
Mod('cap2_io',
'frappy_psi.ahcapbridge.IO', '',
uri='linse-leiden-ts:3001'
)
#Mod('cap2',
# 'frappy_psi.ahcapbridge.AH2550',
# 'capacitance',
# io='cap2_io',
# loss_module = 'loss2',
#)

View File

@@ -3,8 +3,15 @@ Node('AH2700Test.psi.ch',
'tcp://5000', 'tcp://5000',
) )
Mod('cap', Mod('io',
'frappy_psi.ah2700.Capacitance', 'frappy_psi.ahcapbridge.IO', '',
'capacitance', uri='linse-leiden-ts:3002'
uri='ldmse3-ts:3015', )
Mod('cap',
'frappy_psi.ahcapbridge.AH2700',
'capacitance',
io='io',
loss_module = 'loss',
freq_module = 'freq',
) )

View File

@@ -1,110 +0,0 @@
Node('leiden370.config.sea.psi.ch',
'''30 K - Leidendil Pulsetube with ls370''',
)
Mod('sea_main',
'frappy_psi.sea.SeaClient',
'main sea connection for leiden370.config',
config = 'leiden370.config',
service = 'main',
)
for name in ['T3K', 'Tstill', 'T50mK', 'Tmxlow', 'Tmxhigh', 'Tmxcx', 'Tblueo',
'Tpt50', 'Tpt3high', 'Tpt3low', 'Twhite', 'Tgreen']:
mname = name.replace('T','T_')
Mod(mname,
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
sea_object='tt',
rel_paths=[name],
value=Param(unit='K'),
extra_modules = ['raw'],
)
Mod(name.replace('T', 'R_'),
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
value=Param(unit='Ohm'),
single_module=f'{mname}.raw'
)
Mod('cmn',
'frappy_psi.sea.SeaReadable', '',
io = 'sea_main',
sea_object = 'cmn',
extra_modules = ['u1', 'u2', 'temp'],
)
Mod('T_cmn',
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
value=Param(unit='K'),
single_module='cmn.temp',
)
Mod('V_fixp',
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
value=Param(unit='V'),
single_module='cmn.u2',
)
Mod('V_cmn',
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
value=Param(unit='V'),
single_module='cmn.u1',
)
Mod('tcs_io',
'frappy_psi.tcs.IO',
'tcs communication',
uri='linse-leiden-ts:3005',
)
Mod('still_htr',
'frappy_psi.tcs.Heater',
'still heater',
io='tcs_io',
channel=2,
)
Mod('mix_htr',
'frappy_psi.tcs.WrappedHeater',
'mixing chamber heater',
io='tcs_io',
channel=3,
)
Mod('drive_mix',
'frappy_psi.picontrol.PIctrl',
'controlled temperature ',
input_module = 'T_mxlow',
output_module = 'mix_htr',
output_min = 0,
output_max = 0.02,
p = 5,
itime = 60,
)
Mod('drive_cmn',
'frappy_psi.picontrol.PIctrl',
'controlled temperature ',
input_module = 'T_cmn',
output_module = 'mix_htr',
output_min = 0,
output_max = 1e-3,
p = 5,
itime = 120,
)
Mod('drive_fixp',
'frappy_psi.picontrol.PI',
'controlled temperature ',
value=Param(unit='V'),
input_module = 'V_fixp',
output_module = 'drive_mix',
output_min = 0.0,
output_max = 0.01,
p = 1,
itime = 120,
)

224
cfg/main/leiden_cfg.py Normal file
View File

@@ -0,0 +1,224 @@
Node('leiden.psi.ch',
'''Leiden Dilution''',
)
ah2700_uri = 'linse-leiden-ts:3002' # used in cfg/addons/ahtwo_cfg.pt
ls370_uri = 'linse-leiden-ts:3004' # used in ~/sea/tcl/leiden.config
tcs_uri = 'linse-leiden-ts:3005'
#nanov_uri = 'linse-leiden-ts:3006' # used in ~/sea/tcl/leiden.config
k2601b_uri = 'linse-leiden-ts:3006' # used for HC experiment as heater
dilhtr_uri = 'linse-leiden-ts:3007'
srbridge_uri = 'linse-leiden-ts:3008'
Mod('sea_main',
'frappy_psi.sea.SeaClient',
'main sea connection for leiden.config',
config = 'leiden.config',
service = 'main',
)
for name in ['T3K', 'Tstill', 'T50mK', 'Tmxlow', 'Tmxhigh', 'Tmxcx', 'Tblueo',
'Tpt50', 'Tpt3high', 'Tpt3low', 'Twhite', 'Tgreen']:
mname = name.replace('T','T_')
Mod(mname,
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
sea_object='tt',
rel_paths=[name],
value=Param(unit='K'),
extra_modules = ['raw'],
)
Mod(name.replace('T', 'R_'),
'frappy_psi.sea.SeaReadable', '',
io='sea_main',
value=Param(unit='Ohm'),
single_module=f'{mname}.raw'
)
#Mod('cmn',
# 'frappy_psi.sea.SeaReadable', '',
# io = 'sea_main',
# sea_object = 'cmn',
# extra_modules = ['u1', 'u2', 'temp'],
#)
#Mod('T_cmn',
# 'frappy_psi.sea.SeaReadable', '',
# io='sea_main',
# value=Param(unit='K'),
# single_module='cmn.temp',
#)
#Mod('V_fixp',
# 'frappy_psi.sea.SeaReadable', '',
# io='sea_main',
# value=Param(unit='V'),
# single_module='cmn.u2',
#)
#Mod('V_cmn',
# 'frappy_psi.sea.SeaReadable', '',
# io='sea_main',
# value=Param(unit='V'),
# single_module='cmn.u1',
#)
Mod('tcs_io',
'frappy_psi.tcs.IO',
'tcs communication',
uri=tcs_uri,
)
Mod('still_htr',
'frappy_psi.tcs.Heater',
'still heater',
io='tcs_io',
channel=2,
)
Mod('mix_io',
'frappy_psi.dilhtr.IO',
'dilhtr communication',
uri=dilhtr_uri,
)
Mod('mix_htr',
'frappy_psi.dilhtr.WrappedHeater',
'mixing chamber heater',
io='mix_io',
)
Mod('drive_mix',
'frappy_psi.picontrol.PIctrl',
'controlled mix ch. temperature',
input_module = 'T_mxlow',
output_module = 'mix_htr',
output_min = 0,
output_max = 0.02,
p = 5,
itime = 60,
)
#Mod('drive_cmn',
# 'frappy_psi.picontrol.PIctrl',
# 'controlled cmn temperature',
# input_module = 'T_cmn',
# output_module = 'mix_htr',
# output_min = 0,
# output_max = 3e-2,
# p = 2,
# itime = 120,
# )
#Mod('drive_fixp',
# 'frappy_psi.picontrol.PI',
# 'controlled fixpoint voltage',
# value=Param(unit='V'),
# input_module = 'V_fixp',
# output_module = 'drive_mix',
# output_min = 0.0,
# output_max = 0.01,
# p = 1,
# itime = 120,
# )
Mod('simio',
'frappy_psi.bridge.BridgeIO',
'communication to sim900',
uri=srbridge_uri,
)
Mod('res1',
'frappy_psi.bridge.Resistance',
'please add description',
io='simio',
port=1,
)
Mod('res2',
'frappy_psi.bridge.Resistance',
'please add description',
io='simio',
port=3,
)
Mod('phase1',
'frappy_psi.bridge.Phase',
'please add description',
resistance='res1',
)
Mod('phase2',
'frappy_psi.bridge.Phase',
'please add description',
resistance='res2',
)
Mod('dev1',
'frappy_psi.bridge.Deviation',
'please add description',
resistance='res1',
)
Mod('dev2',
'frappy_psi.bridge.Deviation',
'please add description',
resistance='res2',
)
Mod('vsource_io',
'frappy_psi.k2601b.K2601bIO',
'source meter',
# uri = '129.129.156.90:5025',
uri = k2601b_uri,
)
Mod('source',
'frappy_psi.k2601b.SourceMeter'
'',
description = "keithley sourcemeter",
mode = 2,
vlimit = 0.5,
ilimit = .0005,
io = 'vsource_io',
)
Mod('hvolt',
'frappy_psi.k2601b.Voltage'
'',
description = "Heater Voltage",
active = False,
limit = 1.0,
target = 0.0,
sourcemeter = 'source',
io = 'vsource_io',
)
Mod('hcur',
'frappy_psi.k2601b.Current'
'',
description = "Heater Current Source",
active = True,
limit = 0.0001,
target = 0.0,
sourcemeter = 'source',
io = 'vsource_io',
)
Mod('hres',
'frappy_psi.k2601b.Resistivity'
'',
description = "Heater Resistance",
io = 'vsource_io',
)
Mod('hpow',
'frappy_psi.k2601b.Power'
'',
description = "Heater Power",
io = 'vsource_io',
)

29
cfg/sea/cp1000.addon.json Normal file
View File

@@ -0,0 +1,29 @@
{"cp2800": {"base": "/cp2800", "params": [
{"path": "", "type": "bool", "readonly": false, "cmd": "cp2800", "kids": 27},
{"path": "send", "type": "text", "readonly": false, "cmd": "cp2800 send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "comp_running_hrs", "type": "float"},
{"path": "cpu_t", "type": "float"},
{"path": "motor_current_a", "type": "float"},
{"path": "inp_water_t", "type": "float"},
{"path": "inp_water_t_min", "type": "float"},
{"path": "inp_water_t_max", "type": "float"},
{"path": "out_water_t", "type": "float"},
{"path": "out_water_t_min", "type": "float"},
{"path": "out_water_t_max", "type": "float"},
{"path": "helium_t", "type": "float"},
{"path": "helium_t_min", "type": "float"},
{"path": "helium_t_max", "type": "float"},
{"path": "oil_t", "type": "float"},
{"path": "oil_t_min", "type": "float"},
{"path": "oil_t_max", "type": "float"},
{"path": "high_side_p", "type": "float"},
{"path": "high_side_p_min", "type": "float"},
{"path": "high_side_p_max", "type": "float"},
{"path": "high_side_p_avg", "type": "float"},
{"path": "low_side_p", "type": "float"},
{"path": "low_side_p_min", "type": "float"},
{"path": "low_side_p_max", "type": "float"},
{"path": "low_side_p_avg", "type": "float"},
{"path": "high_side_delta_p_avg", "type": "float"},
{"path": "high_side_bounce", "type": "float"}]}}

14
cfg/sea/cp1000_cfg.py Normal file
View File

@@ -0,0 +1,14 @@
Node('cp1000.addon.sea.psi.ch',
'''dry system''',
)
Mod('sea_addons',
'frappy_psi.sea.SeaClient',
'addons sea connection for cp1000.addon',
config = 'cp1000.addon',
service = 'addons',
)
Mod('cp2800',
'frappy_psi.sea.SeaWritable', '',
io = 'sea_addons',
sea_object = 'cp2800',
)

213
cfg/sea/leiden.config.json Normal file
View File

@@ -0,0 +1,213 @@
{"tt": {"base": "/tt", "params": [
{"path": "", "type": "int", "kids": 18},
{"path": "send", "type": "text", "readonly": false, "cmd": "tt send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "autoscan", "type": "bool", "readonly": false, "cmd": "tt autoscan", "kids": 4},
{"path": "autoscan/synchronized", "type": "bool", "readonly": false, "cmd": "tt autoscan/synchronized"},
{"path": "autoscan/interval", "type": "text", "readonly": false, "cmd": "tt autoscan/interval"},
{"path": "autoscan/pause", "type": "text", "readonly": false, "cmd": "tt autoscan/pause"},
{"path": "autoscan/dwell", "type": "text", "readonly": false, "cmd": "tt autoscan/dwell"},
{"path": "T3K", "type": "float", "kids": 14},
{"path": "T3K/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt T3K/active"},
{"path": "T3K/autorange", "type": "bool", "readonly": false, "cmd": "tt T3K/autorange", "description": "autorange (common for all channels)"},
{"path": "T3K/range", "type": "text", "readonly": false, "cmd": "tt T3K/range", "description": "resistance range in Ohm"},
{"path": "T3K/range_num", "type": "int"},
{"path": "T3K/excitation", "type": "text", "readonly": false, "cmd": "tt T3K/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "T3K/excitation_num", "type": "int"},
{"path": "T3K/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "T3K/pause", "type": "int", "readonly": false, "cmd": "tt T3K/pause", "description": "pause time [sec] after channel change"},
{"path": "T3K/filter", "type": "int", "readonly": false, "cmd": "tt T3K/filter", "description": "filter average time [sec]"},
{"path": "T3K/dwell", "type": "int", "readonly": false, "cmd": "tt T3K/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "T3K/status", "type": "text"},
{"path": "T3K/curve", "type": "text", "readonly": false, "cmd": "tt T3K/curve", "kids": 1},
{"path": "T3K/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt T3K/curve/points", "visibility": 3},
{"path": "T3K/alarm", "type": "float", "readonly": false, "cmd": "tt T3K/alarm"},
{"path": "T3K/raw", "type": "float"},
{"path": "Tstill", "type": "float", "kids": 14},
{"path": "Tstill/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tstill/active"},
{"path": "Tstill/autorange", "type": "bool", "readonly": false, "cmd": "tt Tstill/autorange", "description": "autorange (common for all channels)"},
{"path": "Tstill/range", "type": "text", "readonly": false, "cmd": "tt Tstill/range", "description": "resistance range in Ohm"},
{"path": "Tstill/range_num", "type": "int"},
{"path": "Tstill/excitation", "type": "text", "readonly": false, "cmd": "tt Tstill/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tstill/excitation_num", "type": "int"},
{"path": "Tstill/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tstill/pause", "type": "int", "readonly": false, "cmd": "tt Tstill/pause", "description": "pause time [sec] after channel change"},
{"path": "Tstill/filter", "type": "int", "readonly": false, "cmd": "tt Tstill/filter", "description": "filter average time [sec]"},
{"path": "Tstill/dwell", "type": "int", "readonly": false, "cmd": "tt Tstill/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tstill/status", "type": "text"},
{"path": "Tstill/curve", "type": "text", "readonly": false, "cmd": "tt Tstill/curve", "kids": 1},
{"path": "Tstill/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tstill/curve/points", "visibility": 3},
{"path": "Tstill/alarm", "type": "float", "readonly": false, "cmd": "tt Tstill/alarm"},
{"path": "Tstill/raw", "type": "float"},
{"path": "T50mK", "type": "float", "kids": 14},
{"path": "T50mK/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt T50mK/active"},
{"path": "T50mK/autorange", "type": "bool", "readonly": false, "cmd": "tt T50mK/autorange", "description": "autorange (common for all channels)"},
{"path": "T50mK/range", "type": "text", "readonly": false, "cmd": "tt T50mK/range", "description": "resistance range in Ohm"},
{"path": "T50mK/range_num", "type": "int"},
{"path": "T50mK/excitation", "type": "text", "readonly": false, "cmd": "tt T50mK/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "T50mK/excitation_num", "type": "int"},
{"path": "T50mK/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "T50mK/pause", "type": "int", "readonly": false, "cmd": "tt T50mK/pause", "description": "pause time [sec] after channel change"},
{"path": "T50mK/filter", "type": "int", "readonly": false, "cmd": "tt T50mK/filter", "description": "filter average time [sec]"},
{"path": "T50mK/dwell", "type": "int", "readonly": false, "cmd": "tt T50mK/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "T50mK/status", "type": "text"},
{"path": "T50mK/curve", "type": "text", "readonly": false, "cmd": "tt T50mK/curve", "kids": 1},
{"path": "T50mK/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt T50mK/curve/points", "visibility": 3},
{"path": "T50mK/alarm", "type": "float", "readonly": false, "cmd": "tt T50mK/alarm"},
{"path": "T50mK/raw", "type": "float"},
{"path": "Tmxlow", "type": "float", "kids": 14},
{"path": "Tmxlow/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxlow/active"},
{"path": "Tmxlow/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxlow/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxlow/range", "type": "text", "readonly": false, "cmd": "tt Tmxlow/range", "description": "resistance range in Ohm"},
{"path": "Tmxlow/range_num", "type": "int"},
{"path": "Tmxlow/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxlow/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxlow/excitation_num", "type": "int"},
{"path": "Tmxlow/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxlow/pause", "type": "int", "readonly": false, "cmd": "tt Tmxlow/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxlow/filter", "type": "int", "readonly": false, "cmd": "tt Tmxlow/filter", "description": "filter average time [sec]"},
{"path": "Tmxlow/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxlow/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxlow/status", "type": "text"},
{"path": "Tmxlow/curve", "type": "text", "readonly": false, "cmd": "tt Tmxlow/curve", "kids": 1},
{"path": "Tmxlow/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxlow/curve/points", "visibility": 3},
{"path": "Tmxlow/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxlow/alarm"},
{"path": "Tmxlow/raw", "type": "float"},
{"path": "Tmxhigh", "type": "float", "kids": 14},
{"path": "Tmxhigh/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxhigh/active"},
{"path": "Tmxhigh/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxhigh/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxhigh/range", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/range", "description": "resistance range in Ohm"},
{"path": "Tmxhigh/range_num", "type": "int"},
{"path": "Tmxhigh/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxhigh/excitation_num", "type": "int"},
{"path": "Tmxhigh/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxhigh/pause", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxhigh/filter", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/filter", "description": "filter average time [sec]"},
{"path": "Tmxhigh/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxhigh/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxhigh/status", "type": "text"},
{"path": "Tmxhigh/curve", "type": "text", "readonly": false, "cmd": "tt Tmxhigh/curve", "kids": 1},
{"path": "Tmxhigh/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxhigh/curve/points", "visibility": 3},
{"path": "Tmxhigh/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxhigh/alarm"},
{"path": "Tmxhigh/raw", "type": "float"},
{"path": "Tmxcx", "type": "float", "kids": 14},
{"path": "Tmxcx/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tmxcx/active"},
{"path": "Tmxcx/autorange", "type": "bool", "readonly": false, "cmd": "tt Tmxcx/autorange", "description": "autorange (common for all channels)"},
{"path": "Tmxcx/range", "type": "text", "readonly": false, "cmd": "tt Tmxcx/range", "description": "resistance range in Ohm"},
{"path": "Tmxcx/range_num", "type": "int"},
{"path": "Tmxcx/excitation", "type": "text", "readonly": false, "cmd": "tt Tmxcx/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tmxcx/excitation_num", "type": "int"},
{"path": "Tmxcx/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tmxcx/pause", "type": "int", "readonly": false, "cmd": "tt Tmxcx/pause", "description": "pause time [sec] after channel change"},
{"path": "Tmxcx/filter", "type": "int", "readonly": false, "cmd": "tt Tmxcx/filter", "description": "filter average time [sec]"},
{"path": "Tmxcx/dwell", "type": "int", "readonly": false, "cmd": "tt Tmxcx/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tmxcx/status", "type": "text"},
{"path": "Tmxcx/curve", "type": "text", "readonly": false, "cmd": "tt Tmxcx/curve", "kids": 1},
{"path": "Tmxcx/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tmxcx/curve/points", "visibility": 3},
{"path": "Tmxcx/alarm", "type": "float", "readonly": false, "cmd": "tt Tmxcx/alarm"},
{"path": "Tmxcx/raw", "type": "float"},
{"path": "Tblueo", "type": "float", "kids": 14},
{"path": "Tblueo/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tblueo/active"},
{"path": "Tblueo/autorange", "type": "bool", "readonly": false, "cmd": "tt Tblueo/autorange", "description": "autorange (common for all channels)"},
{"path": "Tblueo/range", "type": "text", "readonly": false, "cmd": "tt Tblueo/range", "description": "resistance range in Ohm"},
{"path": "Tblueo/range_num", "type": "int"},
{"path": "Tblueo/excitation", "type": "text", "readonly": false, "cmd": "tt Tblueo/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tblueo/excitation_num", "type": "int"},
{"path": "Tblueo/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tblueo/pause", "type": "int", "readonly": false, "cmd": "tt Tblueo/pause", "description": "pause time [sec] after channel change"},
{"path": "Tblueo/filter", "type": "int", "readonly": false, "cmd": "tt Tblueo/filter", "description": "filter average time [sec]"},
{"path": "Tblueo/dwell", "type": "int", "readonly": false, "cmd": "tt Tblueo/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tblueo/status", "type": "text"},
{"path": "Tblueo/curve", "type": "text", "readonly": false, "cmd": "tt Tblueo/curve", "kids": 1},
{"path": "Tblueo/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tblueo/curve/points", "visibility": 3},
{"path": "Tblueo/alarm", "type": "float", "readonly": false, "cmd": "tt Tblueo/alarm"},
{"path": "Tblueo/raw", "type": "float"},
{"path": "Tpt50", "type": "float", "kids": 14},
{"path": "Tpt50/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt50/active"},
{"path": "Tpt50/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt50/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt50/range", "type": "text", "readonly": false, "cmd": "tt Tpt50/range", "description": "resistance range in Ohm"},
{"path": "Tpt50/range_num", "type": "int"},
{"path": "Tpt50/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt50/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt50/excitation_num", "type": "int"},
{"path": "Tpt50/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt50/pause", "type": "int", "readonly": false, "cmd": "tt Tpt50/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt50/filter", "type": "int", "readonly": false, "cmd": "tt Tpt50/filter", "description": "filter average time [sec]"},
{"path": "Tpt50/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt50/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt50/status", "type": "text"},
{"path": "Tpt50/curve", "type": "text", "readonly": false, "cmd": "tt Tpt50/curve", "kids": 1},
{"path": "Tpt50/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt50/curve/points", "visibility": 3},
{"path": "Tpt50/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt50/alarm"},
{"path": "Tpt50/raw", "type": "float"},
{"path": "Tpt3high", "type": "float", "kids": 14},
{"path": "Tpt3high/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt3high/active"},
{"path": "Tpt3high/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt3high/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt3high/range", "type": "text", "readonly": false, "cmd": "tt Tpt3high/range", "description": "resistance range in Ohm"},
{"path": "Tpt3high/range_num", "type": "int"},
{"path": "Tpt3high/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt3high/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt3high/excitation_num", "type": "int"},
{"path": "Tpt3high/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt3high/pause", "type": "int", "readonly": false, "cmd": "tt Tpt3high/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt3high/filter", "type": "int", "readonly": false, "cmd": "tt Tpt3high/filter", "description": "filter average time [sec]"},
{"path": "Tpt3high/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt3high/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt3high/status", "type": "text"},
{"path": "Tpt3high/curve", "type": "text", "readonly": false, "cmd": "tt Tpt3high/curve", "kids": 1},
{"path": "Tpt3high/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt3high/curve/points", "visibility": 3},
{"path": "Tpt3high/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt3high/alarm"},
{"path": "Tpt3high/raw", "type": "float"},
{"path": "Tpt3low", "type": "float", "kids": 14},
{"path": "Tpt3low/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tpt3low/active"},
{"path": "Tpt3low/autorange", "type": "bool", "readonly": false, "cmd": "tt Tpt3low/autorange", "description": "autorange (common for all channels)"},
{"path": "Tpt3low/range", "type": "text", "readonly": false, "cmd": "tt Tpt3low/range", "description": "resistance range in Ohm"},
{"path": "Tpt3low/range_num", "type": "int"},
{"path": "Tpt3low/excitation", "type": "text", "readonly": false, "cmd": "tt Tpt3low/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tpt3low/excitation_num", "type": "int"},
{"path": "Tpt3low/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tpt3low/pause", "type": "int", "readonly": false, "cmd": "tt Tpt3low/pause", "description": "pause time [sec] after channel change"},
{"path": "Tpt3low/filter", "type": "int", "readonly": false, "cmd": "tt Tpt3low/filter", "description": "filter average time [sec]"},
{"path": "Tpt3low/dwell", "type": "int", "readonly": false, "cmd": "tt Tpt3low/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tpt3low/status", "type": "text"},
{"path": "Tpt3low/curve", "type": "text", "readonly": false, "cmd": "tt Tpt3low/curve", "kids": 1},
{"path": "Tpt3low/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tpt3low/curve/points", "visibility": 3},
{"path": "Tpt3low/alarm", "type": "float", "readonly": false, "cmd": "tt Tpt3low/alarm"},
{"path": "Tpt3low/raw", "type": "float"},
{"path": "Twhite", "type": "float", "kids": 14},
{"path": "Twhite/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Twhite/active"},
{"path": "Twhite/autorange", "type": "bool", "readonly": false, "cmd": "tt Twhite/autorange", "description": "autorange (common for all channels)"},
{"path": "Twhite/range", "type": "text", "readonly": false, "cmd": "tt Twhite/range", "description": "resistance range in Ohm"},
{"path": "Twhite/range_num", "type": "int"},
{"path": "Twhite/excitation", "type": "text", "readonly": false, "cmd": "tt Twhite/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Twhite/excitation_num", "type": "int"},
{"path": "Twhite/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Twhite/pause", "type": "int", "readonly": false, "cmd": "tt Twhite/pause", "description": "pause time [sec] after channel change"},
{"path": "Twhite/filter", "type": "int", "readonly": false, "cmd": "tt Twhite/filter", "description": "filter average time [sec]"},
{"path": "Twhite/dwell", "type": "int", "readonly": false, "cmd": "tt Twhite/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Twhite/status", "type": "text"},
{"path": "Twhite/curve", "type": "text", "readonly": false, "cmd": "tt Twhite/curve", "kids": 1},
{"path": "Twhite/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Twhite/curve/points", "visibility": 3},
{"path": "Twhite/alarm", "type": "float", "readonly": false, "cmd": "tt Twhite/alarm"},
{"path": "Twhite/raw", "type": "float"},
{"path": "Tgreen", "type": "float", "kids": 14},
{"path": "Tgreen/active", "type": "enum", "enum": {"inactive": 0, "active": 1}, "readonly": false, "cmd": "tt Tgreen/active"},
{"path": "Tgreen/autorange", "type": "bool", "readonly": false, "cmd": "tt Tgreen/autorange", "description": "autorange (common for all channels)"},
{"path": "Tgreen/range", "type": "text", "readonly": false, "cmd": "tt Tgreen/range", "description": "resistance range in Ohm"},
{"path": "Tgreen/range_num", "type": "int"},
{"path": "Tgreen/excitation", "type": "text", "readonly": false, "cmd": "tt Tgreen/excitation", "description": "excitation with unit, i.e. 2uV or 3pA"},
{"path": "Tgreen/excitation_num", "type": "int"},
{"path": "Tgreen/excitation_mode", "type": "enum", "enum": {"voltage": 0, "current": 1, "off": 2}},
{"path": "Tgreen/pause", "type": "int", "readonly": false, "cmd": "tt Tgreen/pause", "description": "pause time [sec] after channel change"},
{"path": "Tgreen/filter", "type": "int", "readonly": false, "cmd": "tt Tgreen/filter", "description": "filter average time [sec]"},
{"path": "Tgreen/dwell", "type": "int", "readonly": false, "cmd": "tt Tgreen/dwell", "description": "dwell time [sec]. Total time per channel: pause + filter + dwell"},
{"path": "Tgreen/status", "type": "text"},
{"path": "Tgreen/curve", "type": "text", "readonly": false, "cmd": "tt Tgreen/curve", "kids": 1},
{"path": "Tgreen/curve/points", "type": "floatvarar", "readonly": false, "cmd": "tt Tgreen/curve/points", "visibility": 3},
{"path": "Tgreen/alarm", "type": "float", "readonly": false, "cmd": "tt Tgreen/alarm"},
{"path": "Tgreen/raw", "type": "float"},
{"path": "analog2", "type": "float", "readonly": false, "cmd": "tt analog2"},
{"path": "remote", "type": "bool"},
{"path": "display", "type": "text", "readonly": false, "cmd": "tt display"}]},
"cmn": {"base": "/cmn", "params": [
{"path": "", "type": "none", "kids": 6},
{"path": "send", "type": "text", "readonly": false, "cmd": "cmn send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "u1", "type": "float"},
{"path": "temp", "type": "float"},
{"path": "u2", "type": "float"},
{"path": "chan", "type": "enum", "enum": {"auto": 0, "chan1": 1, "chan2": 2}, "readonly": false, "cmd": "cmn chan"}]}}

View File

@@ -1,160 +0,0 @@
#!/usr/bin/env 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:
# Markus Zolliker <markus.zolliker@psi.ch>
# *****************************************************************************
"""Andeen Hagerling capacitance bridge
two modules: the capacitance itself and the loss angle
in the configuration file, only the capacitance module needs to be configured,
while the loss module will be created automatically.
the name of the loss module may be configured, or disabled by choosing
an empty name
"""
from frappy.core import FloatRange, HasIO, Parameter, Readable, StringIO, nopoll, \
Attached, Property, StringType, Writable
from frappy.dynamic import Pinata
class Ah2700IO(StringIO):
end_of_line = '\r\n'
timeout = 5
class Capacitance(HasIO, Pinata, Readable):
value = Parameter('capacitance', FloatRange(unit='pF'))
freq = Parameter('frequency', FloatRange(unit='Hz'), readonly=False, default=0)
voltage = Parameter('voltage', FloatRange(unit='V'), readonly=False, default=0)
loss_name = Property('''name of loss module (default: <name>_loss)
configure '' to disable the creation of the loss module
''',
StringType(), default='$_loss')
freq_name = Property('''name of freq module
default: not created
''',
StringType(), default='')
ioClass = Ah2700IO
loss = 0 # not a parameter
def scanModules(self):
if self.loss_name:
# if loss_name is not empty, we tell the framework to create
# a module for the loss with this name, and config below
yield self.loss_name.replace('$', self.name), {
'cls': Loss,
'description': f'loss value of {self.name}',
'cap': self.name}
if self.freq_name:
yield self.freq_name.replace('$', self.name), {
'cls': Freq,
'description': f'freq module of {self.name}',
'cap': self.name}
def parse_reply(self, reply):
if reply.startswith('SI'): # this is an echo
self.communicate('SERIAL ECHO OFF')
reply = self.communicate('SI')
if not reply.startswith('F='): # this is probably an error message like "LOSS TOO HIGH"
self.status = self.Status.ERROR, reply
return self.value
self.status = self.Status.IDLE, ''
# examples of replies:
# 'F= 1000.0 HZ C= 0.000001 PF L> 0.0 DS V= 15.0 V'
# 'F= 1000.0 HZ C= 0.0000059 PF L=-0.4 DS V= 15.0 V OVEN'
# 'LOSS TOO HIGH'
# make sure there is always a space after '=' and '>'
# split() ignores multiple white space
reply = reply.replace('=', '= ').replace('>', '> ').split()
_, freq, _, _, cap, _, _, loss, lossunit, _, volt = reply[:11]
self.freq = float(freq)
self.voltage = float(volt)
if lossunit == 'DS':
self.loss = float(loss)
else: # the unit was wrong, we want DS = tan(delta), not NS = nanoSiemens
reply = self.communicate('UN DS').split() # UN DS returns a reply similar to SI
try:
self.loss = reply[7]
except IndexError:
pass # don't worry, loss will be updated next time
return float(cap)
def read_value(self):
return self.parse_reply(self.communicate('SI')) # SI = single trigger
@nopoll
def read_freq(self):
self.read_value()
return self.freq
@nopoll
def read_voltage(self):
self.read_value()
return self.voltage
def write_freq(self, value):
self.value = self.parse_reply(self.communicate(f'FR {value:g};SI'))
return self.freq
def write_voltage(self, value):
self.value = self.parse_reply(self.communicate(f'V {value:g};SI'))
return self.voltage
class Loss(Readable):
cap = Attached()
value = Parameter('loss', FloatRange(unit='deg'), default=0)
def initModule(self):
super().initModule()
self.cap.registerCallbacks(self, ['status']) # auto update status
def update_value(self, _):
# value is always changed shortly after loss
self.value = self.cap.loss
@nopoll
def read_value(self):
self.cap.read_value()
return self.cap.loss
class Freq(Writable):
cap = Attached()
value = Parameter('', FloatRange(unit='Hz'), default=0)
def initModule(self):
super().initModule()
self.cap.registerCallbacks(self, ['status']) # auto update status
def update_value(self, _):
# value is always changed shortly after freq
self.value = self.cap.freq
@nopoll
def read_value(self):
self.cap.read_value()
return self.cap.freq
def write_target(self, target):
self.cap.write_freq(target)

View File

@@ -1,4 +1,3 @@
#!/usr/bin/env python
# ***************************************************************************** # *****************************************************************************
# This program is free software; you can redistribute it and/or modify it under # 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 # the terms of the GNU General Public License as published by the Free Software
@@ -19,73 +18,50 @@
# ***************************************************************************** # *****************************************************************************
"""Andeen Hagerling capacitance bridge """Andeen Hagerling capacitance bridge
two modules: the capacitance itself and the loss angle creates up to two additional modules for 'loss' and 'freq'
in the configuration file, only the capacitance module needs to be configured, in the configuration file, only the capacitance module needs to be configured,
while the loss module will be created automatically. while the loss module will be created automatically.
the name of the loss module may be configured, or disabled by choosing the name of the loss (and freq) module may be configured, or disabled by
an empty name choosing an empty name
""" """
import re import re
import time import time
import math import threading
from frappy.core import FloatRange, HasIO, Parameter, Readable, StringIO, \ from frappy.core import HasIO, Parameter, Readable, StringIO, \
Attached, Property, StringType, Command, Writable, IntRange, BUSY, IDLE, WARN, ERROR Attached, Property, Command, Writable, BUSY, IDLE, WARN
from frappy.datatypes import FloatRange, IntRange, StringType, TupleOf
from frappy.modules import Acquisition from frappy.modules import Acquisition
from frappy.dynamic import Pinata from frappy.dynamic import Pinata
from frappy.errors import ProgrammingError from frappy.errors import ProgrammingError, IsBusyError
from frappy.lib import clamp
class IO(StringIO): class IO(StringIO):
end_of_line = '\r\n' end_of_line = ('\r\n', '\r')
timeout = 1 # for writing, '\r\n' also accepted on AH2700
# default_settings = {'timeout': 0.1} # for reading, '\r\n' would be correct, but '\r' does also works
last_command = None # when stripping the reply
timeout = 5
# @Command(result=StringType())
# def readline(self):
# """async read"""
# with self._lock:
# reply = self._conn.readline()
# if reply:
# result = reply.decode(self.encoding)
# self.comLog('< %s', result)
# return result
# return ''
#
# @Command(StringType())
# def writeline(self, command):
# """no flush before"""
# cmd = command.encode(self.encoding)
# self.check_connection()
# try:
# self.comLog('> %s', command)
# self._conn.send(cmd + self._eol_write)
# self.last_command = command
# except ConnectionClosed:
# self.closeConnection()
# raise CommunicationFailedError('disconnected') from None
# except Exception as e:
# if self._conn is None:
# raise SilentError('disconnected') from None
# if repr(e) != self._last_error:
# self._last_error = repr(e)
# self.log.error(self._last_error)
# raise SilentError(repr(e)) from e
@Command(StringType(), result=StringType()) @Command(StringType(), result=StringType())
def communicate(self, command, noreply=False): def communicate(self, command, noreply=False):
"""communicate and save the last command""" """communicate and save the last command"""
# this is also called by writeline # this is also called by writeline
self.last_time = time.time() reply = super().communicate(command, noreply)
return super().communicate(command, noreply) return reply and reply.strip()
class AH2550(HasIO, Pinata, Acquisition): CONTINUOUS = 0
STARTING = 1
RUNNING = 2
FINISHED = 3
class AHBase(HasIO, Pinata, Acquisition):
value = Parameter('capacitance', FloatRange(unit='pF')) value = Parameter('capacitance', FloatRange(unit='pF'))
freq = Parameter('frequency', FloatRange(unit='Hz'), default=1000)
voltage = Parameter('upper voltage limit', voltage = Parameter('upper voltage limit',
FloatRange(0, 15, unit='V', fmtstr='%.1f'), FloatRange(0, 15, unit='V', fmtstr='%.1f'),
readonly=False, default=0) readonly=False, default=0)
@@ -93,127 +69,31 @@ class AH2550(HasIO, Pinata, Acquisition):
FloatRange(unit=''), default=0) FloatRange(unit=''), default=0)
averexp = Parameter('base 2 exponent of goal', averexp = Parameter('base 2 exponent of goal',
IntRange(0, 15), readonly=False, default=0) IntRange(0, 15), readonly=False, default=0)
goal = Parameter('number of samples to average', goal = Parameter('value for averexp for the next go()',
FloatRange(0, 50000, unit='samples'), IntRange(0, 15), readonly=False, default=0)
readonly=False, default=0)
meas_time = Parameter('measured measuring time', meas_time = Parameter('measured measuring time',
FloatRange(unit='s', fmtstr='%.1f'), default=0) FloatRange(unit='s', fmtstr='%.4g'), default=0)
loss_module = Property('''name of loss module (default: <name>_loss) calculated_time = Parameter('calculated measuring time',
FloatRange(unit='s', fmtstr='%.4g'), default=0)
loss_module = Property(
'''name of loss module (default: <name>_loss)
configure '' to disable the creation of the loss module configure '' to disable the creation of the loss module
''', ''', StringType(), default='')
StringType(), default='') pollinterval = Parameter(datatype=FloatRange(0.001, 1),
pollinterval = Parameter(export=False, value=0.1) export=False, value=0.001)
export = True # for a Pinata module, the default is False! export = True # for a Pinata module, the default is False!
ioClass = IO ioClass = IO
MODEL = 'AH2550' COMMANDS = ['AV', 'VO', 'SI', 'SH', 'FR']
PATTERN = [
r'AVERAGE *AVEREXP=(?P<averexp>[0-9]*)',
r'VOLTAGE HIGHEST *(?P<voltage>[0-9.E+-]+)',
]
MEAS_PAT = (
r'C= *(?P<cap>[0-9.E+-]+) *PF '
r'L= *(?P<loss>[0-9.E+-]+) *(?P<lossunit>[A-Z]*) '
f'V= *(?P<voltage>[0-9.E+-]+) *V *(?P<error>.*)$'
# <volt> must not collide with <voltage>
)
COMMANDS = ['AV', 'VO', 'SI' ,'SH']
_started = 0
_meas_state = 0
_todo = None
_error = '' _error = ''
_last_start = None
def initModule(self): _params = None
self.io.setProperty('identification', _mode = CONTINUOUS # or RUNNING or FINISHED
[('\r\nSERIAL ECHO OFF;SH MODEL', f'MODEL/OPTIONS *{self.MODEL}')]) _cont_deadline = 0 # when to switch back to continuous after finished
super().initModule() _averexp_deadline = 0 # to make sure averexp is polled periodically
self.log.info('INIT') # to be overridden:
pattern = self.PATTERN + [self.MEAS_PAT] PATTERN = None # a list of patterns to parse replies
self.pattern = {p[:2]: re.compile(p) for p in pattern} MEAS_PAT = None # the pattern to parse the measurement reply
if len(self.pattern) != len(pattern):
raise ProgrammingError('need more than two letters to distinguish patterns')
self.echo = re.compile('|'.join(self.COMMANDS))
# first commands:
# UN DS does also return the resukts of the last measurement (including the frequency for AH2700)
self._todo = {'averexp': 'SH AV', '<unit>': 'UN DS'}
def doPoll(self):
if self.io.last_time: # a command was sent
self._started = 0 # trigger start
reply = self.io.readline(0)
if reply:
# parse reply of (here unknown) command
self.io.last_time = 0
pattern = self.pattern.get(reply[:2])
if pattern:
match = pattern.match(reply)
if not match:
self.log.warning('unexpected reply syntax: %r', reply)
return
values = match.groupdict()
if len(values) == 1:
key, value = next(iter(values.items()))
getattr(self, f'update_{key}')(float(value))
return
self.update_meas(**values)
return
if self.echo.match(reply):
# this is probably an echo
# we may have lost connection, so query again averexp
self.writeline('\r\nSERIAL ECHO OFF;SH AV')
return
self.log.warning('unknown reply %r', reply)
return
now = time.time()
if now < self.io.last_time + 1:
# send no more commands before we have the reply of the previous
return
if self._todo:
# we have parameters to be changed
# this will interrupt the current measurement
command = self._todo.pop(next(iter(self._todo)))
self.io.writeline(command)
return
if self._started:
# we are measuring, the last command was SI
return
# nothing else to do - we start measuring
if self._meas_state == 1:
self._meas_state = 2
self.status = BUSY, 'measuring'
self.trigger_measurement()
def trigger_measurement(self):
self._started = time.time()
self.writeline('SI')
self.io.last_time = 0 # do not retrigger again
def update_meas(self, cap, loss, lossunit, voltage, error, freq=None):
"""update given arguments (these are strings!)"""
now = time.time()
self.meas_time = now - self._started
self.value = float(cap)
self.voltage = float(voltage)
self._error = error
if self._meas_state == 2:
self._meas_state = 0
if self._error:
self.status = WARN, self._error
else:
self.status = IDLE, ''
if lossunit != 'DS':
self.io.writeline('UN DS') # this will trigger a measuremement reply
return
self.loss = float(loss)
self.trigger_measurement()
def update_lossunit(self, unit):
if unit != 'DS':
self.log.warning('can not change unit to DS')
def update_averexp(self, averexp):
self.averexp = int(averexp)
self.goal = 2 ** self.averexp
def scanModules(self): def scanModules(self):
if self.loss_module: if self.loss_module:
@@ -224,24 +104,192 @@ class AH2550(HasIO, Pinata, Acquisition):
'description': f'loss value of {self.name}', 'description': f'loss value of {self.name}',
'cap': self.name} 'cap': self.name}
def initModule(self):
self.io.setProperty('identification',
[('\rSERIAL ECHO OFF;SH MODEL',
'ILLEGAL WORD: MODEL')])
super().initModule()
pattern = self.PATTERN + [self.MEAS_PAT]
self.pattern = {p[:2]: re.compile(p) for p in pattern}
if len(self.pattern) != len(pattern):
raise ProgrammingError('need more than two letters '
'to distinguish patterns')
self.echo = re.compile('|'.join(self.COMMANDS))
self._params = {}
self._lock = threading.RLock()
def initialReads(self):
# UN 2 does also return the results of the last measurement
# (including the frequency for AH2700)
self.io.writeline('SH FR;UN 2')
self.freq = self.interprete('freq')
self.verify_averexp()
self.goal = self.averexp
self.single_meas()
def interprete(self, wait_for=None, tmo=None):
"""
:param wait_for: name of parameter to wait for or None to wait
for measurement
:param tmo:
:return:
"""
if tmo is None:
tmo = self.io.timeout
reply = self.io.readline(tmo)
now = time.time()
while reply:
pattern = self.pattern.get(reply[:2])
if pattern:
match = pattern.match(reply)
if not match:
self.log.warning('unexpected reply syntax: %r', reply)
break
values = match.groupdict()
if len(values) == 1:
key, value = next(iter(values.items()))
self._params[key] = float(value)
else:
self._params['meas'] = values
elif self.echo.match(reply):
# this is probably an echo
# we may have lost connection, so query again averexp
self.writeline('\r\nSERIAL ECHO OFF;SH AV')
elif reply:
self.log.warning('unknown reply %r', reply)
if (wait_for or 'meas') in self._params:
break
reply = self.io.readline(tmo)
self.log.debug('doPoll %r params %r wait_for %r %d', reply,
list(self._params), wait_for, self._mode)
result = self._params.pop(wait_for, None)
if self._mode == FINISHED and now > self._cont_deadline:
self._mode = CONTINUOUS
self.status = IDLE, ''
self.single_meas()
return
if result is None and wait_for:
self.log.info(f'missing reply for {wait_for}')
return getattr(self, wait_for, None)
return result
def change(self, short, value, param):
if self._mode == RUNNING:
raise IsBusyError('can not change parameters while measuring')
with self._lock:
self.io.writeline(f'{short} {value};SH {short}')
result = self.interprete(param)
self.retrigger_meas()
return result
def retrigger_meas(self):
if self._mode == CONTINUOUS:
self.single_meas()
def single_meas(self):
self._last_start = time.time()
self.writeline('SI')
def doPoll(self):
# this polls measurement results
# we can not do polling of other parameters, as they would
# interrupt measurements. averexp needs a special treatment
self.interprete(tmo=1)
with self._lock:
for param in list(self._params):
value = self._params.pop(param, None)
if param == 'meas':
self.update_meas(**value)
self.retrigger_meas()
elif param == 'averexp':
self.update_averexp(value)
elif param == 'freq':
self.update_freq(value)
elif param == 'voltage':
self.voltage = value
def update_freq(self, value):
self.freq = value
self._calculate_time(self.averexp, value)
def update_averexp(self, value):
self.averexp = value
self._averexp_deadline = time.time() + 15
self._calculate_time(value, self.freq)
def update_meas(self, cap, loss, lossunit, voltage, error, freq=None):
"""update given arguments (these are strings!)"""
self._error = error
if self._error:
status = WARN, self._error
else:
status = IDLE, '' if self._mode == CONTINUOUS else 'finished'
if status != self.status:
self.status = status
now = time.time()
if self._mode == RUNNING:
self._cont_deadline = now + 5
self._mode = FINISHED
if freq:
self.freq = float(freq)
self._calculate_time(self.averexp, self.freq)
self.value = float(cap)
self.voltage = float(voltage)
if lossunit != self.UNIT:
if self._last_start == 0:
self.log.warning('change unit to %s failed', self.UNIT)
else:
self.io.writeline('UN 2')
# this will trigger a measurement reply
# skip calculation of meas_time while interpreting result
self._last_start = 0
self.interprete('meas')
self.retrigger_meas()
return
if self._last_start:
self.meas_time = now - self._last_start
self._last_start = 0
if now > self._averexp_deadline and self._mode == CONTINUOUS:
self.verify_averexp()
self.loss = float(loss)
def write_voltage(self, value): def write_voltage(self, value):
self._todo['voltage'] = f'VO {value:g};SH VO' return round(self.change('VO', f'{value:.1f}', 'voltage'), 1)
return round(value, 1)
def write_averexp(self, value): def write_averexp(self, value):
self._todo['averexp'] = f'AV {value};SH AV' self.update_averexp(self.change('AV', f'{value}', 'averexp'))
self.goal = 2 ** value
def write_goal(self, value): def verify_averexp(self):
self.averexp = clamp(0, 15, round(math.log2(value))) # we do not want to use read_averexp for this,
self._todo['averexp'] = f'AV {self.averexp};SH AV' # as it will stop the measurement when polled
return 2 ** self.averexp self.io.writeline('SH AV')
self.update_averexp(self.interprete('averexp'))
def _calculate_time(self, averexp, freq):
self.calculated_time = self.calculate_time(averexp, freq)
def go(self): def go(self):
"""start acquisition""" """start acquisition"""
prevmode = self._mode
self._mode = STARTING
if prevmode != FINISHED or time.time() > self._averexp_deadline:
# this also makes sure we catch a previous meas reply
self.verify_averexp()
if self.averexp != self.goal:
self.log.info('changed averexp')
self.write_averexp(self.goal)
self.status = BUSY, 'started' self.status = BUSY, 'started'
self._started = 0 # interrupt current measurement self.single_meas()
self._meas_state = 1 # retrigger a measurement self._mode = RUNNING
def stop(self):
"""stops measurement"""
if self._mode == RUNNING:
self.verify_averexp()
self.status = WARN, 'stopped'
self._mode = FINISHED
self._cont_deadline = time.time() + 5
class Loss(Readable): class Loss(Readable):
@@ -268,25 +316,71 @@ class Freq(Writable):
self.cap.addCallback('freq', self.update_freq) # auto update status self.cap.addCallback('freq', self.update_freq) # auto update status
def update_freq(self, freq): def update_freq(self, freq):
self.value = float(freq) self.value = freq
def write_target(self, target): def write_target(self, target):
self.cap.write_freq(target) self.cap.write_freq(target)
class AH2700(AH2550): class AH2550(AHBase):
MODEL = 'AH2700' PATTERN = [
freq = Parameter('frequency', r'AVERAGE_AVEREXP *(?P<averexp>[0-9]*)',
FloatRange(50, 20000, unit='Hz', fmtstr='%.1f'), r'VOLTAGE_HIGHEST *(?P<voltage>[0-9.E+-]+)',
readonly=False, default=50) r'FREQUENCY *(?P<freq>[0-9.E+-]+)',
]
MEAS_PAT = (
r'C= *(?P<cap>[0-9.E+-]+) *PF,'
r'L= *(?P<loss>[0-9.E+-]+) *(?P<lossunit>[A-Z]*),'
r'V= *(?P<voltage>[0-9.E+-]+) *V,A,*(?P<error>.*)$'
)
UNIT = 'DF'
# empirically determined - may vary with noise
# differs drastically from the table in the manual
MEAS_TIME_CONST = [0.2, 0.3, 0.4, 1.0, 1.3, 1.6, 2.2, 3.3,
5.5, 8.3, 14, 25, 47, 91, 180, 360]
def initModule(self):
self.io.setProperty('identification',
[('\rSERIAL ECHO OFF;SH MODEL',
'ILLEGAL WORD: MODEL')])
super().initModule()
def _calculate_time(self, averexp, freq):
self.calculated_time = self.calculate_time(averexp)
@Command(TupleOf(IntRange(0, 15)), result=FloatRange())
def calculate_time(self, averexp):
"""calculate estimated measuring time"""
return self.MEAS_TIME_CONST[int(averexp)]
class AH2700(AHBase):
freq = Parameter(datatype=FloatRange(50, 20000, unit='Hz', fmtstr='%.1f'),
readonly=False)
freq_module = Property('''name of freq module freq_module = Property('''name of freq module
default: not created default: not created
''', ''',
StringType(), default='') StringType(), default='')
MEAS_PAT = r'F= *(?P<freq>[0-9.E+-]+) *HZ ' + AH2550.MEAS_PAT PATTERN = [
PATTERN = AH2550.PATTERN + [r'FREQUENCY *(?P<freq>[0-9.E+-]+)'] r'AVERAGE *AVEREXP=(?P<averexp>[0-9]*)',
COMMANDS = AH2550.COMMANDS + ['FR'] r'VOLTAGE HIGHEST *(?P<voltage>[0-9.E+-]+)',
r'FREQUENCY *(?P<freq>[0-9.E+-]+)',
]
UNIT = 'DS'
MEAS_PAT = (
r'F= *(?P<freq>[0-9.E+-]+) *HZ '
r'C= *(?P<cap>[0-9.E+-]+) *PF '
r'L= *(?P<loss>[0-9.E+-]+) *(?P<lossunit>[A-Z]*) '
f'V= *(?P<voltage>[0-9.E+-]+) *V *(?P<error>.*)$'
)
def initModule(self):
super().initModule()
self.io.setProperty('identification',
[('\r\nSERIAL ECHO OFF;SH MODEL',
'MODEL/OPTIONS *AH2700')])
def scanModules(self): def scanModules(self):
yield from super().scanModules() yield from super().scanModules()
@@ -297,10 +391,39 @@ class AH2700(AH2550):
'cap': self.name} 'cap': self.name}
def write_freq(self, value): def write_freq(self, value):
self._todo['freq'] = f'FR {value:g};SH FR' self.change('FR', f'{value:g}', 'freq')
self.update_freq(value)
return round(value, 1) return round(value, 1)
def update_meas(self, freq, **kwds): MEAS_TIME_CONST = [
self.freq = float(freq) # (upper freq limit, meas time @ avrexp=7 - 0.8)
super().update_meas(**kwds) (75, 20),
(150, 10),
(270, 5.62),
(550, 2.34),
(1100, 2.73),
(4500, 1.02),
(20000, 0.51),
]
@Command(TupleOf(IntRange(0, 15), FloatRange(50, 20000)),
result=FloatRange())
def calculate_time(self, averexp, freq):
"""calculate estimated measuring time
from time efficiency considerations averexp > 7 is recommended
especially for freq < 550 no time may be saved with averexp <= 7
"""
for f, c in self.MEAS_TIME_CONST:
if f > freq:
const = c
break
else:
const = self.MEAS_TIME_CONST[-1][1]
if averexp >= 8:
result = 0.8 + const * (0.5 + 2 ** (averexp - 8))
elif freq < 550:
result = 0.8 + const
else:
result = 0.6 + 2 ** (averexp - 7) * const
return round(result, 1)