NSLS-II / wishlist

an issue tracker for the big picture
1 stars 0 forks source link

[ENH] Add Capability to areaDetector to create an image flyer #77

Closed stuwilkins closed 8 years ago

stuwilkins commented 8 years ago

HI!

So at CSX we would like to include the beam camera as a "monitor" which is async with the detector. By modifying areaDetector, a flyer can be created using the buffer capability of the Stats plugin. For the areaDetector changes see:

Pull Request at CSX to ADCore

This enables a flyer to be constructed like:

class AreaDetectorTimeseriesCollector:
    def __init__(self, name, pv_basename, num_points = 1000000):
        self._name = name
        self._pv_basename = pv_basename
        self._num_points = num_points

        self._pv_tscontrol = epics.PV("{}TSControl".format(pv_basename))
        self._pv_num_points = epics.PV("{}TSNumPoints".format(pv_basename))
        self._pv_cur_point = epics.PV("{}TSCurrentPoint".format(pv_basename))
        self._pv_wfrm = epics.PV("{}TSTotal".format(pv_basename), auto_monitor=False)
        self._pv_wfrm_ts = epics.PV("{}TSTimestamp".format(pv_basename), auto_monitor=False)
        self._cb = None
        self.done = True

    def _get_wfrms(self):
        n = self._pv_cur_point.get()
        if n:
            return (self._pv_wfrm.get(count=n), self._pv_wfrm_ts.get(count=n))
        else:
            return (np.array([]), np.array([]))

    def kickoff(self):
        self._pv_num_points.put(self._num_points, wait=True)
        self._pv_tscontrol.put(0, wait=True) # Erase buffer and start collection
        return self

    @property
    def finished_cb(self):
        return self._cb

    @finished_cb.setter
    def finished_cb(self, cb):
        if self._cb is not None:
            raise RuntimeError("Cannot change the callback")
        if self.done:
            cb()
        else:
            self._cb = cb

    def _finish(self):
        self.ready = True
        if self._cb is not None:
            self._cb()
            self._cb = None

    def collect(self):
        payload_val, payload_time = self._get_wfrm()
        if payload_val.size == 0:
            # We have no data, yeild {}
            ev = {'data': {self._name: 0.0},
                  'timestamps': {self._name: 0.0},
                  'time': ttime.time()}
            yield ev
        for v,t in zip(payload_val, payload_time):
            ev = {'data': {self._name: v},
                  'timestamps': {self._name: t},
                  'time': ttime.time()}
            yield ev

    def stop(self):
        self._pv_tscontrol.put(2, wait=True) # Stop Collection

    def describe(self):
        return [{self._name: {'source': self._pv_basename, 'dtype': 'number', 'shape': None}}]

diag5_flyer = AreaDetectorTimeseriesCollector('diag5_flyer', 'XF:23ID1-BI{Diag:5-Cam:1}Stats1:')

Which will work with bluesky to create a timestamped series of events from any of the stats plugin values (Total Counts, Sigma etc.)

Needs help adding a "done" monitor and some testing, could also be useful for measuring stats anyway without having to do them in a "step scan" way.

This takes the timestamp from the areaDetector driver which should be correct....... It is on my FastCCD :-)

Thoughts / Comments? @tacaswell @klauer @danielballan

stuwilkins commented 8 years ago

Attn @cmazzoli @ambarb I think this solves the diag6 issue and gets it as the monitor.

tacaswell commented 8 years ago

Closing this as it is on open PR in ophyd, it can be milestoned and tracked there.