From 65ad13d1142925465ac5f7d135b2e8f824d5425e Mon Sep 17 00:00:00 2001 From: Edward Wall Date: Tue, 17 Jun 2025 10:12:52 +0200 Subject: [PATCH] adds gating stuff, writes all thresholds simultaneously, updates readme with example --- README.md | 100 +++++++++++++++++++++++++++++++++++++++++ db/channels.db | 9 ++++ db/daq.proto | 2 +- db/daq_2nd_gen.db | 33 ++++++++++++++ db/daq_4ch.db | 13 ++++++ db/daq_8ch.db | 17 +++++++ db/daq_common.db | 5 +-- db/gating_channels.db | 102 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 db/gating_channels.db diff --git a/README.md b/README.md index c22f0aa..5a76d62 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ runScript "$(sinqDAQ_DIR)daq_2nd_gen.cmd" "NAME=DAQ, DAQ_IP=TestInst-DAQ1, DAQ_P | \$(INSTR)\$(NAME):ELAPSED-TIME | Time DAQ has been measuring for | | \$(INSTR)\$(NAME):M_ | Current count on channel. (1-10 depending on DAQ system) | | \$(INSTR)\$(NAME):CHANNELS | Number of available channels (4, 8 or 10) | +| \$(INSTR)\$(NAME):GATE-_ | Configuration for Gating in newer hardware | ## Generating Test Signals @@ -71,6 +72,105 @@ runScript "$(sinqDAQ_DIR)daq_2nd_gen.cmd" "NAME=DAQ, DAQ_IP=TestInst-DAQ1, DAQ_P See the file [daq\_2nd\_gen\_test.db](./db/daq_2nd_gen_test.db) +## Nicos Interface + +A set of Nicos devices have been developed which allow control of the Detector +Hardware via this Epics Driver. The corresponding code can be found in +[sinqdaq.py](https://gitea.psi.ch/lin-instrument-computers/Nicos/src/branch/release-3.12/nicos_sinq/devices/epics/sinqdaq.py). + +## Full Example + +Include the following snippet in your IOC + +``` +# st.cmd at SINQTEST + +epicsEnvSet("STREAM_PROTOCOL_PATH","./db") +epicsEnvSet("INSTR","SQ:SINQTEST:") + +require sinqDAQ +runScript "$(sinqDAQ_DIR)daq_2nd_gen.cmd" "NAME=DAQ, DAQ_IP=TestInst-DAQ1, DAQ_PORT=2000" +``` + +What follows is an example Nicos setup file. The "channels" are created in the loop +at the bottom. + +``` +# DAQDetector.py +description = 'Devices for the detectors' + +countprefix = 'SQ:SINQTEST:DAQ' + +devices = dict( + ElapsedTime = device( + 'nicos_sinq.devices.epics.sinqdaq.DAQTime', + daqpvprefix = countprefix, + ), + DAQPreset = device( + 'nicos_sinq.devices.epics.sinqdaq.DAQPreset', + description = '2nd Generation Data Acquisition', + daqpvprefix = countprefix, + channels = [], + time_channel = ['ElapsedTime'], + ), + DAQV2 = device( + 'nicos_sinq.devices.epics.sinqdaq.SinqDetector', + description = 'Detector Interface', + timers = ['ElapsedTime'], + counters = [], + monitors = ['DAQPreset'], + images = [], + others = [], + liveinterval = 2, + saveintervals = [2] + ), + ThresholdChannel = device( + 'nicos_sinq.devices.epics.sinqdaq.DAQMinThresholdChannel', + daqpvprefix = countprefix, + channels = [], + visibility = {'metadata', 'namespace'}, + ), + Threshold = device( + 'nicos_sinq.devices.epics.sinqdaq.DAQMinThreshold', + daqpvprefix = countprefix, + min_rate_channel = 'ThresholdChannel', + visibility = {'metadata', 'namespace'}, + ), + Gate1 = device( + 'nicos_sinq.devices.epics.sinqdaq.DAQGate', + daqpvprefix = countprefix, + channel = 1, + visibility = {'metadata', 'namespace'}, + ), + Gate2 = device( + 'nicos_sinq.devices.epics.sinqdaq.DAQGate', + daqpvprefix = countprefix, + channel = 2, + visibility = {'metadata', 'namespace'}, + ), + TestGen = device('nicos_sinq.devices.epics.sinqdaq.DAQTestGen', + daqpvprefix = countprefix, + visibility = {'metadata', 'namespace'}, + ), +) + +for i in range(10): + devices[f'monitor{i+1}'] = device( + 'nicos_sinq.devices.epics.sinqdaq.DAQChannel', + description = f'Monitor {i + 1}', + daqpvprefix = countprefix, + channel = i + 1, + type = 'monitor', + ) + devices['DAQPreset'][1]['channels'].append(f'monitor{i+1}') + devices['ThresholdChannel'][1]['channels'].append(f'monitor{i+1}') + devices['DAQV2'][1]['monitors'].append(f'monitor{i+1}') + +startupcode = ''' +SetDetectors(DAQV2) +''' +``` + ## Simulation Simulation of the Hardware can be toggled on as follows: diff --git a/db/channels.db b/db/channels.db index c136ec8..48dae2a 100644 --- a/db/channels.db +++ b/db/channels.db @@ -48,6 +48,15 @@ record(longout, "$(INSTR)$(NAME):C$(CHANNEL)") field(FLNK, "$(INSTR)$(NAME):T$(CHANNEL)") } +record(ao,"$(INSTR)$(NAME):THRESH$(CHANNEL)") +{ + field(DESC, "Sets minimum rate for counting to proceed") + field(OMSL, "supervisory") + field(OROC, "0") + field(OUT, "@$(PROTO) setMinRate($(INSTR)$(NAME):, $(CHANNEL)) $(ASYN_PORT)") + field(DTYP, "stream") +} + ################################################################################ # Read all monitors values diff --git a/db/daq.proto b/db/daq.proto index 4b4e2fb..6d5fbec 100644 --- a/db/daq.proto +++ b/db/daq.proto @@ -124,7 +124,7 @@ startWithTimePreset { } setMinRate{ - out "DL %(\$1THRESHOLD-MONITOR_RBV)d %.3f"; + out "DL \$2 %.3f"; in; @mismatch{in "%(\$1MsgTxt)s";} } diff --git a/db/daq_2nd_gen.db b/db/daq_2nd_gen.db index 2c06596..f5f2af9 100644 --- a/db/daq_2nd_gen.db +++ b/db/daq_2nd_gen.db @@ -41,5 +41,38 @@ record(seq, "$(INSTR)$(NAME):CORRECT-MONITOR-CHANNEL") ################################################################################ # Count Commands +# The hardware stores a separate threshold for each channel, which is somewhat +# unintuitive for the user, as only one can actually be made use of at a time. +# So, we just write the threshold value to all channels when it is changed. +record(dfanout,"$(INSTR)$(NAME):THRESHOLD-F") +{ + field(OMSL, "supervisory") + field(SELM, "All") + field(OUTA, "$(INSTR)$(NAME):THRESHOLD-F1 PP") + field(OUTB, "$(INSTR)$(NAME):THRESHOLD-F2 PP") +} + +record(dfanout,"$(INSTR)$(NAME):THRESHOLD-F1") +{ + field(OMSL, "supervisory") + field(SELM, "All") + field(OUTA, "$(INSTR)$(NAME):THRESH1 PP") + field(OUTB, "$(INSTR)$(NAME):THRESH2 PP") + field(OUTC, "$(INSTR)$(NAME):THRESH3 PP") + field(OUTD, "$(INSTR)$(NAME):THRESH4 PP") + field(OUTE, "$(INSTR)$(NAME):THRESH5 PP") + field(OUTF, "$(INSTR)$(NAME):THRESH6 PP") + field(OUTG, "$(INSTR)$(NAME):THRESH7 PP") + field(OUTH, "$(INSTR)$(NAME):THRESH8 PP") +} + +record(dfanout,"$(INSTR)$(NAME):THRESHOLD-F2") +{ + field(OMSL, "supervisory") + field(SELM, "All") + field(OUTA, "$(INSTR)$(NAME):THRESH9 PP") + field(OUTB, "$(INSTR)$(NAME):THRESH10 PP") +} + ################################################################################ # Read all monitors values diff --git a/db/daq_4ch.db b/db/daq_4ch.db index 1e2c1ac..193a01a 100644 --- a/db/daq_4ch.db +++ b/db/daq_4ch.db @@ -28,5 +28,18 @@ record(longin, "$(INSTR)$(NAME):MONITOR-CHANNEL_RBV") ################################################################################ # Count Commands +# The hardware stores a separate threshold for each channel, which is somewhat +# unintuitive for the user, as only one can actually be made use of at a time. +# So, we just write the threshold value to all channels when it is changed. +record(dfanout,"$(INSTR)$(NAME):THRESHOLD-F") +{ + field(OMSL, "supervisory") + field(SELM, "All") + field(OUTA, "$(INSTR)$(NAME):THRESH1 PP") + field(OUTB, "$(INSTR)$(NAME):THRESH2 PP") + field(OUTC, "$(INSTR)$(NAME):THRESH3 PP") + field(OUTD, "$(INSTR)$(NAME):THRESH4 PP") +} + ################################################################################ # Read all monitors values diff --git a/db/daq_8ch.db b/db/daq_8ch.db index d1893b0..c770a7d 100644 --- a/db/daq_8ch.db +++ b/db/daq_8ch.db @@ -28,5 +28,22 @@ record(longin, "$(INSTR)$(NAME):MONITOR-CHANNEL_RBV") ################################################################################ # Count Commands +# The hardware stores a separate threshold for each channel, which is somewhat +# unintuitive for the user, as only one can actually be made use of at a time. +# So, we just write the threshold value to all channels when it is changed. +record(dfanout,"$(INSTR)$(NAME):THRESHOLD-F") +{ + field(OMSL, "supervisory") + field(SELM, "All") + field(OUTA, "$(INSTR)$(NAME):THRESH1 PP") + field(OUTB, "$(INSTR)$(NAME):THRESH2 PP") + field(OUTC, "$(INSTR)$(NAME):THRESH3 PP") + field(OUTD, "$(INSTR)$(NAME):THRESH4 PP") + field(OUTE, "$(INSTR)$(NAME):THRESH5 PP") + field(OUTF, "$(INSTR)$(NAME):THRESH6 PP") + field(OUTG, "$(INSTR)$(NAME):THRESH7 PP") + field(OUTH, "$(INSTR)$(NAME):THRESH8 PP") +} + ################################################################################ # Read all monitors values diff --git a/db/daq_common.db b/db/daq_common.db index f898bf1..fa86c65 100644 --- a/db/daq_common.db +++ b/db/daq_common.db @@ -178,15 +178,14 @@ record(longout, "$(INSTR)$(NAME):STOP") record(ao,"$(INSTR)$(NAME):THRESHOLD") { field(DESC, "Minimum rate for counting to proceed") - field(VAL, "0") # Rate + field(VAL, "1") # Default Rate # Could perhaps still be improved. # It seems to only accept whole counts? field(DRVL, "1") # Minimum Rate field(DRVH, "100000") # Maximum Rate field(OMSL, "supervisory") field(OROC, "0") - field(OUT, "@$(PROTO) setMinRate($(INSTR)$(NAME):) $(ASYN_PORT)") - field(DTYP, "stream") + field(OUT, "$(INSTR)$(NAME):THRESHOLD-F PP") } record(ai,"$(INSTR)$(NAME):THRESHOLD_RBV") diff --git a/db/gating_channels.db b/db/gating_channels.db new file mode 100644 index 0000000..248e609 --- /dev/null +++ b/db/gating_channels.db @@ -0,0 +1,102 @@ +# EPICS Database for streamdevice specific to gating channels +# +# Macros +# INSTR - Prefix +# NAME - the device name, e.g. EL737 +# PROTO - Stream device protocol file +# ASYN_PORT - Low level Asyn IP Port to DAQ +# CHANNEL - the number associated with the measurment channel + +################################################################################ + +# Records for configuring gating settings +record(mbbo, "$(INSTR)$(NAME):GATE-$(CHANNEL)") +{ + field(DESC, "Sets the current gate state") + field(DTYP, "Soft Channel") + field(OUT, "$(INSTR)$(NAME):GATE-$(CHANNEL)-SET PP") + field(ZRST, "Disabled") + field(ONST, "Trigger Low") + field(TWST, "Trigger High") +} + +record(dfanout, "$(INSTR)$(NAME):GATE-$(CHANNEL)-SET") +{ + field(OUTA, "$(INSTR)$(NAME):GATE-$(CHANNEL)-SEL.SELN PP") + field(OUTB, "$(INSTR)$(NAME):GATE-$(CHANNEL)-SEL2.SELN PP") +} + +record(seq, "$(INSTR)$(NAME):GATE-$(CHANNEL)-SEL") +{ + field(SELM, "Specified") + field(DO0, 0) + field(LNK0, "$(INSTR)$(NAME):GATE-$(CHANNEL)-ENABLE PP") + field(DO1, 1) + field(LNK1, "$(INSTR)$(NAME):GATE-$(CHANNEL)-ENABLE PP") + field(DO2, 1) + field(LNK2, "$(INSTR)$(NAME):GATE-$(CHANNEL)-ENABLE PP") +} + +record(bo, "$(INSTR)$(NAME):GATE-$(CHANNEL)-ENABLE") +{ + field(DESC, "Enable Gating Channel") + field(ZNAM, "Disabled") + field(ONAM, "Enabled") + field(DTYP, "stream") + field(OUT, "@$(PROTO) setGateStatus($(INSTR)$(NAME):, $(CHANNEL)) $(ASYN_PORT)") +} + +record(seq, "$(INSTR)$(NAME):GATE-$(CHANNEL)-SEL2") +{ + field(SELM, "Specified") + field(DO1, 0) + field(LNK1, "$(INSTR)$(NAME):GATE-$(CHANNEL)-TRIG PP") + field(DO2, 1) + field(LNK2, "$(INSTR)$(NAME):GATE-$(CHANNEL)-TRIG PP") +} + +record(bo, "$(INSTR)$(NAME):GATE-$(CHANNEL)-TRIG") +{ + field(DESC, "Set Count when first Gate high/low") + field(ZNAM, "Low") + field(ONAM, "High") + field(DTYP, "stream") + field(OUT, "@$(PROTO) setGateStatus($(INSTR)$(NAME):, $(CHANNEL)) $(ASYN_PORT)") +} + +# Records for reading configured gating settings +record(mbbi, "$(INSTR)$(NAME):GATE-$(CHANNEL)_RBV") +{ + field(DESC, "The current gate state") + field(ZRST, "Disabled") + field(ONST, "Trigger Low") + field(TWST, "Trigger High") +} + +record(calcout, "$(INSTR)$(NAME):GATE-$(CHANNEL)-SEL_RBV") +{ + field(INPA, "$(INSTR)$(NAME):GATE-$(CHANNEL)-ENABLE_RBV NPP") + field(INPB, "$(INSTR)$(NAME):GATE-$(CHANNEL)-TRIG_RBV NPP") + field(CALC, "A=0?0:B=0?1:2") + field(OOPT, "On Change") + field(OUT, "$(INSTR)$(NAME):GATE-$(CHANNEL)_RBV PP") + field(FLNK, "$(INSTR)$(NAME):GATE-$(CHANNEL)_RBV") +} + +record(bi, "$(INSTR)$(NAME):GATE-$(CHANNEL)-ENABLE_RBV") +{ + field(DESC, "Is Gating Channel Enabled") + field(ZNAM, "Disabled") + field(ONAM, "Enabled") + field(DTYP, "stream") + field(INP, "@$(PROTO) getGateStatus($(INSTR)$(NAME):, $(CHANNEL)) $(ASYN_PORT)") + field(SCAN, "2 second") + field(FLNK, "$(INSTR)$(NAME):GATE-$(CHANNEL)-SEL_RBV") +} + +record(bi, "$(INSTR)$(NAME):GATE-$(CHANNEL)-TRIG_RBV") +{ + field(DESC, "Count when first Gate high/low") + field(ZNAM, "Low") + field(ONAM, "High") +}