diff --git a/cfg/addons/ah2700_cfg.py b/cfg/addons/ah2700_cfg.py index 7f13473f..5a6eecda 100755 --- a/cfg/addons/ah2700_cfg.py +++ b/cfg/addons/ah2700_cfg.py @@ -3,20 +3,16 @@ Node('ah2700.frappy.psi.ch', ) Mod('cap_io', - 'frappy_psi.ah2700.Ah2700IO', - '', - uri='linse-976d-ts:3006', + 'frappy_psi.ahcapbridge.IO', '', + uri='linse-leiden-ts:3002' ) Mod('cap', - 'frappy_psi.ah2700.Capacitance', + 'frappy_psi.ahcapbridge.AH2700', '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', -) + diff --git a/cfg/addons/ahtwo_cfg.py b/cfg/addons/ahtwo_cfg.py new file mode 100644 index 00000000..834280ad --- /dev/null +++ b/cfg/addons/ahtwo_cfg.py @@ -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', +#) + diff --git a/cfg/ah2700test_cfg.py b/cfg/ah2700test_cfg.py index c83b8585..c81cea90 100644 --- a/cfg/ah2700test_cfg.py +++ b/cfg/ah2700test_cfg.py @@ -3,8 +3,15 @@ Node('AH2700Test.psi.ch', 'tcp://5000', ) -Mod('cap', - 'frappy_psi.ah2700.Capacitance', - 'capacitance', - uri='ldmse3-ts:3015', +Mod('io', + 'frappy_psi.ahcapbridge.IO', '', + uri='linse-leiden-ts:3002' +) + +Mod('cap', + 'frappy_psi.ahcapbridge.AH2700', + 'capacitance', + io='io', + loss_module = 'loss', + freq_module = 'freq', ) diff --git a/cfg/main/leiden370_cfg.py b/cfg/main/leiden370_cfg.py deleted file mode 100644 index 48b9f48a..00000000 --- a/cfg/main/leiden370_cfg.py +++ /dev/null @@ -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, - ) - diff --git a/cfg/main/leiden_cfg.py b/cfg/main/leiden_cfg.py new file mode 100644 index 00000000..69288cd3 --- /dev/null +++ b/cfg/main/leiden_cfg.py @@ -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', +) diff --git a/cfg/sea/cp1000.addon.json b/cfg/sea/cp1000.addon.json new file mode 100644 index 00000000..c0ffd1dc --- /dev/null +++ b/cfg/sea/cp1000.addon.json @@ -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"}]}} diff --git a/cfg/sea/cp1000_cfg.py b/cfg/sea/cp1000_cfg.py new file mode 100644 index 00000000..610331c1 --- /dev/null +++ b/cfg/sea/cp1000_cfg.py @@ -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', +) diff --git a/cfg/sea/leiden.config.json b/cfg/sea/leiden.config.json new file mode 100644 index 00000000..729e6525 --- /dev/null +++ b/cfg/sea/leiden.config.json @@ -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"}]}} diff --git a/frappy_psi/ah2700.py b/frappy_psi/ah2700.py deleted file mode 100644 index 53edbd05..00000000 --- a/frappy_psi/ah2700.py +++ /dev/null @@ -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 -# ***************************************************************************** -"""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: _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) - diff --git a/frappy_psi/ahcapbridge.py b/frappy_psi/ahcapbridge.py index 3d1ad7d4..4009ba48 100644 --- a/frappy_psi/ahcapbridge.py +++ b/frappy_psi/ahcapbridge.py @@ -1,4 +1,3 @@ -#!/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 @@ -19,73 +18,50 @@ # ***************************************************************************** """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, while the loss module will be created automatically. -the name of the loss module may be configured, or disabled by choosing -an empty name +the name of the loss (and freq) module may be configured, or disabled by +choosing an empty name """ import re import time -import math -from frappy.core import FloatRange, HasIO, Parameter, Readable, StringIO, \ - Attached, Property, StringType, Command, Writable, IntRange, BUSY, IDLE, WARN, ERROR +import threading +from frappy.core import HasIO, Parameter, Readable, StringIO, \ + Attached, Property, Command, Writable, BUSY, IDLE, WARN +from frappy.datatypes import FloatRange, IntRange, StringType, TupleOf from frappy.modules import Acquisition from frappy.dynamic import Pinata -from frappy.errors import ProgrammingError -from frappy.lib import clamp +from frappy.errors import ProgrammingError, IsBusyError class IO(StringIO): - end_of_line = '\r\n' - timeout = 1 - # default_settings = {'timeout': 0.1} - last_command = None - - # @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 + end_of_line = ('\r\n', '\r') + # for writing, '\r\n' also accepted on AH2700 + # for reading, '\r\n' would be correct, but '\r' does also works + # when stripping the reply + timeout = 5 @Command(StringType(), result=StringType()) def communicate(self, command, noreply=False): """communicate and save the last command""" # this is also called by writeline - self.last_time = time.time() - return super().communicate(command, noreply) + reply = 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')) + freq = Parameter('frequency', FloatRange(unit='Hz'), default=1000) voltage = Parameter('upper voltage limit', FloatRange(0, 15, unit='V', fmtstr='%.1f'), readonly=False, default=0) @@ -93,127 +69,31 @@ class AH2550(HasIO, Pinata, Acquisition): FloatRange(unit=''), default=0) averexp = Parameter('base 2 exponent of goal', IntRange(0, 15), readonly=False, default=0) - goal = Parameter('number of samples to average', - FloatRange(0, 50000, unit='samples'), - readonly=False, default=0) + goal = Parameter('value for averexp for the next go()', + IntRange(0, 15), readonly=False, default=0) meas_time = Parameter('measured measuring time', - FloatRange(unit='s', fmtstr='%.1f'), default=0) - loss_module = Property('''name of loss module (default: _loss) + FloatRange(unit='s', fmtstr='%.4g'), default=0) + calculated_time = Parameter('calculated measuring time', + FloatRange(unit='s', fmtstr='%.4g'), default=0) + loss_module = Property( + '''name of loss module (default: _loss) - configure '' to disable the creation of the loss module - ''', - StringType(), default='') - pollinterval = Parameter(export=False, value=0.1) + configure '' to disable the creation of the loss module + ''', StringType(), default='') + pollinterval = Parameter(datatype=FloatRange(0.001, 1), + export=False, value=0.001) export = True # for a Pinata module, the default is False! ioClass = IO - MODEL = 'AH2550' - PATTERN = [ - r'AVERAGE *AVEREXP=(?P[0-9]*)', - r'VOLTAGE HIGHEST *(?P[0-9.E+-]+)', - ] - MEAS_PAT = ( - r'C= *(?P[0-9.E+-]+) *PF ' - r'L= *(?P[0-9.E+-]+) *(?P[A-Z]*) ' - f'V= *(?P[0-9.E+-]+) *V *(?P.*)$' - # must not collide with - ) - COMMANDS = ['AV', 'VO', 'SI' ,'SH'] - _started = 0 - _meas_state = 0 - _todo = None + COMMANDS = ['AV', 'VO', 'SI', 'SH', 'FR'] _error = '' - - def initModule(self): - self.io.setProperty('identification', - [('\r\nSERIAL ECHO OFF;SH MODEL', f'MODEL/OPTIONS *{self.MODEL}')]) - super().initModule() - self.log.info('INIT') - 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)) - # first commands: - # UN DS does also return the resukts of the last measurement (including the frequency for AH2700) - self._todo = {'averexp': 'SH AV', '': '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 + _last_start = None + _params = None + _mode = CONTINUOUS # or RUNNING or FINISHED + _cont_deadline = 0 # when to switch back to continuous after finished + _averexp_deadline = 0 # to make sure averexp is polled periodically + # to be overridden: + PATTERN = None # a list of patterns to parse replies + MEAS_PAT = None # the pattern to parse the measurement reply def scanModules(self): if self.loss_module: @@ -224,24 +104,192 @@ class AH2550(HasIO, Pinata, Acquisition): 'description': f'loss value of {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): - self._todo['voltage'] = f'VO {value:g};SH VO' - return round(value, 1) + return round(self.change('VO', f'{value:.1f}', 'voltage'), 1) def write_averexp(self, value): - self._todo['averexp'] = f'AV {value};SH AV' - self.goal = 2 ** value + self.update_averexp(self.change('AV', f'{value}', 'averexp')) - def write_goal(self, value): - self.averexp = clamp(0, 15, round(math.log2(value))) - self._todo['averexp'] = f'AV {self.averexp};SH AV' - return 2 ** self.averexp + def verify_averexp(self): + # we do not want to use read_averexp for this, + # as it will stop the measurement when polled + 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): """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._started = 0 # interrupt current measurement - self._meas_state = 1 # retrigger a measurement + self.single_meas() + 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): @@ -268,25 +316,71 @@ class Freq(Writable): self.cap.addCallback('freq', self.update_freq) # auto update status def update_freq(self, freq): - self.value = float(freq) + self.value = freq def write_target(self, target): self.cap.write_freq(target) -class AH2700(AH2550): - MODEL = 'AH2700' - freq = Parameter('frequency', - FloatRange(50, 20000, unit='Hz', fmtstr='%.1f'), - readonly=False, default=50) +class AH2550(AHBase): + PATTERN = [ + r'AVERAGE_AVEREXP *(?P[0-9]*)', + r'VOLTAGE_HIGHEST *(?P[0-9.E+-]+)', + r'FREQUENCY *(?P[0-9.E+-]+)', + ] + MEAS_PAT = ( + r'C= *(?P[0-9.E+-]+) *PF,' + r'L= *(?P[0-9.E+-]+) *(?P[A-Z]*),' + r'V= *(?P[0-9.E+-]+) *V,A,*(?P.*)$' + ) + 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 default: not created ''', StringType(), default='') - MEAS_PAT = r'F= *(?P[0-9.E+-]+) *HZ ' + AH2550.MEAS_PAT - PATTERN = AH2550.PATTERN + [r'FREQUENCY *(?P[0-9.E+-]+)'] - COMMANDS = AH2550.COMMANDS + ['FR'] + PATTERN = [ + r'AVERAGE *AVEREXP=(?P[0-9]*)', + r'VOLTAGE HIGHEST *(?P[0-9.E+-]+)', + r'FREQUENCY *(?P[0-9.E+-]+)', + ] + UNIT = 'DS' + MEAS_PAT = ( + r'F= *(?P[0-9.E+-]+) *HZ ' + r'C= *(?P[0-9.E+-]+) *PF ' + r'L= *(?P[0-9.E+-]+) *(?P[A-Z]*) ' + f'V= *(?P[0-9.E+-]+) *V *(?P.*)$' + ) + + def initModule(self): + super().initModule() + self.io.setProperty('identification', + [('\r\nSERIAL ECHO OFF;SH MODEL', + 'MODEL/OPTIONS *AH2700')]) def scanModules(self): yield from super().scanModules() @@ -297,10 +391,39 @@ class AH2700(AH2550): 'cap': self.name} 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) - def update_meas(self, freq, **kwds): - self.freq = float(freq) - super().update_meas(**kwds) + MEAS_TIME_CONST = [ + # (upper freq limit, meas time @ avrexp=7 - 0.8) + (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)