Scannng and data acquisition with pyepics using a postgresql database for configuration and communication of scanning commands and status.
Classes and Functions for simple step scanning for epics.
This does not used the Epics SScan Record, and the scan is intended to run as a python application, but many concepts from the Epics SScan Record are borrowed. Where appropriate, the difference will be noted here.
- A Step Scan consists of the following objects:
- a list of Positioners a list of Triggers a list of Counters
Each Positioner will have a list (or numpy array) of position values corresponding to the steps in the scan. As there is a fixed number of steps in the scan, the position list for each positioners must have the same length -- the number of points in the scan. Note that, unlike the SScan Record, the list of points (not start, stop, step, npts) must be given. Also note that the number of positioners or number of points is not limited.
A Trigger is simply an Epics PV that will start a particular detector, usually by having 1 written to its field. It is assumed that when the Epics ca.put() to the trigger completes, the Counters associated with the triggered detector will be ready to read.
A Counter is simple a PV whose value should be recorded at every step in the scan. Any PV can be a Counter, including waveform records. For many detector types, it is possible to build a specialized class that creates many counters.
Because Triggers and Counters are closely associated with detectors, a Detector is also defined, which simply contains a single Trigger and a list of Counters, and will cover most real use cases.
In addition to the core components (Positioners, Triggers, Counters, Detectors), a Step Scan contains the following objects:
- breakpoints a list of scan indices at which to pause and write data
- collected so far to disk.
- extra_pvs a list of PVs that are not recorded at each step in the
- scan, but recorded at the beginning of scan, and at each breakpoint, and to be recorded to disk file.
pre_scan() method to run prior to scan. post_scan() method to run after scan. at_break() method to run at each breakpoint.
Note that Postioners and Detectors may add their own pieces into extra_pvs, pre_scan(), post_scan(), and at_break().
With these concepts, a Step Scan ends up being a fairly simple loop, going roughly (that is, skipping error checking) as:
pos = <DEFINE POSITIONER LIST> det = <DEFINE DETECTOR LIST> run_pre_scan(pos, det) [p.move_to_start() for p in pos] record_extra_pvs(pos, det) for i in range(len(pos[0].array)):
[p.move_to_pos(i) for p in pos] while not all([p.done for p in pos]):
time.sleep(0.001)[trig.start() for trig in det.triggers] while not all([trig.done for trig in det.triggers]):
time.sleep(0.001)[det.read() for det in det.counters]
- if i in breakpoints:
- write_data(pos, det) record_exrta_pvs(pos, det) run_at_break(pos, det)
write_data(pos, det) run_post_scan(pos, det)
Note that multi-dimensional mesh scans over a rectangular grid is not explicitly supported, but these can be easily emulated with the more flexible mechanism of unlimited list of positions and breakpoints. Non-mesh scans are also possible.
A step scan can have an Epics SScan Record or StepScan database associated with it. It will use these for PVs to post data at each point of the scan.