1- # DAE
1+ # DAE (Data Acquisition Electronics)
22
3- ## DaeBase (base class)
3+ The ` SimpleDae ` class is designed to be a configurable DAE object, which will cover the
4+ majority of DAE use-cases within bluesky.
5+
6+ This class uses several objects to configure its behaviour:
7+ - The ` Controller ` is responsible for beginning and ending acquisitions.
8+ - The ` Waiter ` is responsible for waiting for an acquisition to be "complete".
9+ - The ` Reducer ` is responsible for publishing data from an acquisition that has
10+ just been completed.
11+
12+ This means that ` SimpleDae ` is generic enough to cope with most typical DAE use-casess, for
13+ example running using either one DAE run per scan point, or one DAE period per scan point.
14+
15+ For complex use-cases, particularly those where the DAE may need to start and stop multiple
16+ acquisitions per scan point (e.g. polarization measurements), ` SimpleDae ` is unlikely to be
17+ suitable; instead the ` Dae ` class should be subclassed directly to allow for finer control.
18+
19+ ## Example configurations
20+
21+ ### Run-per-point
22+
23+ ``` python
24+ from ibex_bluesky_core.devices import get_pv_prefix
25+ from ibex_bluesky_core.devices.simpledae import SimpleDae
26+ from ibex_bluesky_core.devices.simpledae.controllers import RunPerPointController
27+ from ibex_bluesky_core.devices.simpledae.waiters import GoodFramesWaiter
28+ from ibex_bluesky_core.devices.simpledae.reducers import GoodFramesNormalizer
29+
30+
31+ prefix = get_pv_prefix()
32+ # One DAE run for each scan point, save the runs after each point.
33+ controller = RunPerPointController(save_run = True )
34+ # Wait for 500 good frames on each run
35+ waiter = GoodFramesWaiter(500 )
36+ # Sum spectra 1..99 inclusive, then normalize by total good frames
37+ reducer = GoodFramesNormalizer(
38+ prefix = prefix,
39+ detector_spectra = [i for i in range (1 , 100 )],
40+ )
41+
42+ dae = SimpleDae(
43+ prefix = prefix,
44+ controller = controller,
45+ waiter = waiter,
46+ reducer = reducer,
47+ )
48+
49+ # Can give signals user-friendly names if desired
50+ controller.run_number.set_name(" run number" )
51+ reducer.intensity.set_name(" normalized counts" )
52+ ```
53+
54+ ### Period-per-point
55+
56+ ``` python
57+ from ibex_bluesky_core.devices import get_pv_prefix
58+ from ibex_bluesky_core.devices.simpledae import SimpleDae
59+ from ibex_bluesky_core.devices.simpledae.controllers import PeriodPerPointController
60+ from ibex_bluesky_core.devices.simpledae.waiters import PeriodGoodFramesWaiter
61+ from ibex_bluesky_core.devices.simpledae.reducers import PeriodGoodFramesNormalizer
62+
63+
64+ prefix = get_pv_prefix()
65+ # One DAE period for each scan point, save the runs after the scan.
66+ controller = PeriodPerPointController(save_run = True )
67+ # Wait for 500 period good frames on each point
68+ waiter = PeriodGoodFramesWaiter(500 )
69+ # Sum spectra 1..99 inclusive, then normalize by period good frames
70+ reducer = PeriodGoodFramesNormalizer(
71+ prefix = prefix,
72+ detector_spectra = [i for i in range (1 , 100 )],
73+ )
74+
75+ dae = SimpleDae(
76+ prefix = prefix,
77+ controller = controller,
78+ waiter = waiter,
79+ reducer = reducer,
80+ )
81+ ```
82+
83+ ``` {note}
84+ You will also need to set up the DAE in advance with enough periods. This can be done from a
85+ plan using `yield from bps.mv(dae.number_of_periods, num_points)` before starting the scan.
86+ ```
87+
88+ ## Mapping to bluesky device model
89+
90+ ### Start of scan (` stage ` )
91+
92+ ` SimpleDae ` will call ` controller.setup() ` to allow any pre-scan setup to be done.
93+
94+ For example, this is where the period-per-point controller object will begin a DAE run.
95+
96+ ### Each scan point (` trigger ` )
97+
98+ ` SimpleDae ` will call:
99+ - ` controller.start_counting() ` to begin counting for a single scan point.
100+ - ` waiter.wait() ` to wait for that acquisition to complete
101+ - ` controller.stop_counting() ` to finish counting for a single scan point.
102+ - ` reducer.reduce_data() ` to do any necessary post-processing on
103+ the raw DAE data (e.g. normalization)
104+
105+ ### Each scan point (` read ` )
106+
107+ Any signals marked as "interesting" by the controller, reducer or waiter will be published
108+ in the top-level documents published when ` read() ` ing the ` SimpleDae ` object.
109+
110+ These may correspond to EPICS signals directly from the DAE (e.g. good frames), or may be
111+ soft signals derived at runtime (e.g. normalized intensity).
112+
113+ This means that the ` SimpleDae ` object is suitable for use as a detector in most bluesky
114+ plans, and will make an appropriate set of data available in the emitted documents.
115+
116+ ### End of scan (` unstage ` )
117+
118+ ` SimpleDae ` will call ` controller.teardown() ` to allow any post-scan teardown to be done.
119+
120+ For example, this is where the period-per-point controller object will end a DAE run.
121+
122+ ## Controllers
123+
124+ The ` Controller ` class is responsible for starting and stopping acquisitions, in a generic
125+ way.
126+
127+ ### RunPerPointController
128+
129+ This controller starts and stops a new DAE run for each scan point. It can be configured to
130+ either end runs or abort them on completion.
131+
132+ This controller causes the following signals to be published by ` SimpleDae ` :
133+
134+ - ` controller.run_number ` - The run number into which data was collected. Only published
135+ if runs are being saved.
136+
137+ ### PeriodPerPointController
138+
139+ This controller begins a single DAE run at the start of a scan, and then counts into a new
140+ DAE period for each individual scan point.
141+
142+ The DAE must be configured with enough periods in advance. This is possible to do from a
143+ plan as follows:
144+
145+ ``` python
146+ import bluesky.plan_stubs as bps
147+ import bluesky.plans as bp
148+ from ibex_bluesky_core.devices.simpledae import SimpleDae
149+ from ibex_bluesky_core.devices.block import BlockRw
150+
151+
152+ def plan ():
153+ dae: SimpleDae = ...
154+ block: BlockRw = ...
155+ num_points = 20
156+ yield from bps.mv(dae.number_of_periods, num_points)
157+ yield from bp.scan([dae], block, 0 , 10 , num = num_points)
158+ ```
159+
160+ The controller causes the following signals to be published by ` SimpleDae ` :
161+
162+ - ` simpledae.period_num ` - the period number into which this scan point was counted.
163+
164+ ## Reducers
165+
166+ A ` Reducer ` for a ` SimpleDae ` is responsible for publishing any data derived from the raw
167+ DAE signals. For example, normalizing intensities are implemented as a reducer.
168+
169+ A reducer may produce any number of reduced signals.
170+
171+ ### GoodFramesNormalizer
172+
173+ This normalizer sums a set of user-defined detector spectra, and then divides by the number
174+ of good frames.
175+
176+ Published signals:
177+ - ` simpledae.good_frames ` - the number of good frames reported by the DAE
178+ - ` reducer.det_counts ` - summed detector counts for all of the user-provided spectra
179+ - ` reducer.intensity ` - normalized intensity (` det_counts / good_frames ` )
180+
181+ ### PeriodGoodFramesNormalizer
182+
183+ Equivalent to the ` GoodFramesNormalizer ` above, but uses good frames only from the current
184+ period. This should be used if a controller which counts into multiple periods is being used.
185+
186+ Published signals:
187+ - ` simpledae.period.good_frames ` - the number of good frames reported by the DAE
188+ - ` reducer.det_counts ` - summed detector counts for all of the user-provided spectra
189+ - ` reducer.intensity ` - normalized intensity (` det_counts / good_frames ` )
190+
191+ ### DetectorMonitorNormalizer
192+
193+ This normalizer sums a set of user-defined detector spectra, and then divides by the sum
194+ of a set of user-defined monitor spectra.
195+
196+ Published signals:
197+ - ` reducer.det_counts ` - summed detector counts for the user-provided detector spectra
198+ - ` reducer.mon_counts ` - summed monitor counts for the user-provided monitor spectra
199+ - ` reducer.intensity ` - normalized intensity (` det_counts / mon_counts ` )
200+
201+ ## Waiters
202+
203+ A ` waiter ` defines an arbitrary strategy for how long to count at each point.
204+
205+ Some waiters may be very simple, such as waiting for a fixed amount of time or for a number
206+ of good frames or microamp-hours. However, it is also possible to define much more
207+ sophisticated waiters, for example waiting until sufficient statistics have been collected.
208+
209+ ### GoodUahWaiter
210+
211+ Waits for a user-specified number of microamp-hours.
212+
213+ Published signals:
214+ - ` simpledae.good_uah ` - actual good uAh for this run.
215+
216+ ### GoodFramesWaiter
217+
218+ Waits for a user-specified number of good frames (in total for the entire run)
219+
220+ Published signals:
221+ - ` simpledae.good_frames ` - actual good frames for this run.
222+
223+ ### GoodFramesWaiter
224+
225+ Waits for a user-specified number of good frames (in the current period)
226+
227+ Published signals:
228+ - ` simpledae.period.good_frames ` - actual period good frames for this run.
229+
230+ ### MEventsWaiter
231+
232+ Waits for a user-specified number of millions of events
233+
234+ Published signals:
235+ - ` simpledae.m_events ` - actual period good frames for this run.
236+
237+ ### TimeWaiter
238+
239+ Waits for a user-specified time duration, irrespective of DAE state.
240+
241+ Does not publish any additional signals.
242+
243+ ---
244+
245+ ## ` Dae ` (base class, advanced)
4246
5247` Dae ` is the principal class in ibex_bluesky_core which exposes configuration settings
6- and controls from the ISIS data acquisition electronics (DAE).
248+ and controls from the ISIS data acquisition electronics (DAE). ` SimpleDae ` derives from
249+ DAE, so all of the signals available on ` Dae ` are also available on ` SimpleDae ` .
7250
8251``` {note}
9252 The `Dae` class is not intended to be used directly in scans - it is a low-level class
@@ -16,11 +259,11 @@ and controls from the ISIS data acquisition electronics (DAE).
16259 that it will usually be better to implement functionality at the device level rather
17260 than the plan level.
18261
19- For other use-cases, a user-facing DAE class is likely to be more appropriate to use
20- as a detector in a scan - this class cannot be used by itself.
262+ For other use-cases, a user-facing DAE class such as `SimpleDae` is likely to be more
263+ appropriate to use as a detector in a scan - this class cannot be used by itself.
21264```
22265
23- ## Top-level signals
266+ ### Top-level signals
24267
25268Some DAE parameters, particularly metadata parameters, are exposed as simple signals,
26269for example ` dae.title ` or ` dae.good_uah ` .
@@ -36,13 +279,13 @@ def plan(dae: Dae):
36279 yield from bps.mv(dae.title, " new title" )
37280```
38281
39- ## Period-specific signals
282+ ### Period-specific signals
40283
41284For signals which apply to the current period, see ` dae.period ` , which contains signals
42285such as ` dae.period.good_uah ` (the number of good uamp-hours collected in the current period).
43286
44287
45- ## Controlling the DAE directly
288+ ### Controlling the DAE directly
46289
47290It is possible to control the DAE directly using the signals provided by ` dae.controls ` .
48291
@@ -51,7 +294,7 @@ used by plans directly.
51294
52295For example, beginning a run is possible via ` dae.controls.begin_run.trigger() ` .
53296
54- ### Advanced options
297+ ### Additional begin_run flags
55298
56299Options on ` begin ` (for example, beginning a run in paused mode) can be specified
57300using the ` dae.controls.begin_run_ex ` signal.
@@ -60,7 +303,7 @@ Unlike the standard `begin_run` signal, this needs to be `set()` rather than sim
60303` trigger() ` ed, the value on set is a combination of flags from ` BeginRunExBits ` .
61304
62305
63- ## DAE Settings
306+ ### DAE Settings
64307
65308Many signals on the DAE are only available as composite signals - this includes most DAE
66309configuration parameters which are available under the "experiment setup" tab in IBEX, for
@@ -95,7 +338,7 @@ def plan(dae: Dae):
95338```
96339
97340
98- ## DAE Spectra
341+ ### DAE Spectra
99342
100343Raw spectra are provided by the ` DaeSpectra ` class. Not all spectra are automatically available
101344on the base DAE object - user classes will define the specific set of spectra which they are
0 commit comments