Continuous Scan Tutorial¶
This tutorial shows how to use Bliss in 3 steps:
- configuring data acquisition
- configuring data storage
- starting a continuous scan
First step: data acquisition configuration¶
A continuous scan in Bliss is the combination of acquisition objects, being either master or slaves, and data recorder objects.
Master devices are enabled with synchronization superpowers ; their responsibility is to start or trigger slaves to perform data acquisition at the right moment.
The link between masters and slaves can be either hardware, or emulated with software.
A scan can have multiple masters, and masters can also be slaves for other masters. There is no limit to imagination.
The set of masters and slaves acquisition objects is called an acquisition chain, it is the first thing to configure when defining a new Bliss scan:
from bliss.scanning.chain import AcquisitionChain chain = AcquisitionChain()
Then, masters and slaves objects have to be created and added to the acquisition chain.
Master object creation¶
The SoftwarePositionTriggerMaster
class is shipped with Bliss. It
takes an Emotion Axis
object, and turns it into a Bliss master,
capable of triggering slaves at evenly spaced points between a start and
an end position.
from bliss.scanning.acquisition.motor import SoftwarePositionTriggerMaster emotion_master = SoftwarePositionTriggerMaster(m0, 5, 10, 10, time=5)
In the example above the SoftwarePositionTriggerMaster
is configured
to move m0
from 5 to 10, with 10 points. The optional time
keyword
argument specifies the time in seconds to go between the start and end
positions (acquisition time). Motor speed is changed accordingly.
Acceleration time is taken into account to ensure constant speed during
acquisition. An extra step is also added after end position in order to
guarantee the last point is exposed identically as the previous ones.
Considering an m0
Emotion axis with acceleration set to 100 mm.s-2,
and time for moving from 5 to 10 set to 5 seconds, velocity will be set
to 1 mm.s-1 and the effective move will be from 4.99 to 10.56.
Slave object creation¶
Bliss comes with the LimaAcquisitionMaster
class, encapsulating a
Tango Lima device for use within an acquisition chain:
params = { "acq_nb_frames": 10, "acq_expo_time": 0.3, "acq_trigger_mode": "INTERNAL_TRIGGER_MULTI" } lima_acq_dev = LimaAcquisitionMaster(lima_dev, **params)
Acquisition is configured to take 10 frames of 300 milliseconds exposure time, each frame capture being triggered by software.
Adding master and slave to acquisition chain¶
The AcquisitionChain.add()
method is used to associate a master with
slaves or with another master, and to add a node in the chain.
chain.add(emotion_master, lima_acq_dev)
Internally, the AcquisitionChain.add()
method builds a tree
representing the different acquisition devices. Master acquisition
devices are represented as nodes in a tree:
. chain(AcquisitionChain) └── emotion_master(SoftwarePositionTriggerMaster) └── lima_dev(LimaAcquisitionMaster)
Second step: data storage configuration¶
Once a continuous scan is started, data is produced from the masters and
slaves devices in the acquisition chain. A Scan
object has to be
created, in order to specify how data is saved and made accessible for
other programs like Online Data Analysis.
A scan is identified by its name ; the full name is made of a prefix,
plus a run number. The default prefix is scan, so the first scan is
called scan_1, the second scan is called scan_2 and so on. The
first argument to Scan
is the scan name prefix.
In the same way the AcquisitionChain
can be represented as a tree, the
Scan
saves data in a tree-like structure within the Redis cache. A
scan node contains meta-data (scan_info
), plus a data
member. If
data is too big, only a reference to the data is saved. For example, in
the case of images, the file name is stored instead of the image bytes.
Scan
objects can be placed inside a Container
, in order to match
data acquisition with data analysis logic. A Container
is only
identified by its name. Typically, a container will have a sample name,
an each scan on this sample can be stored inside the container.
Container
objects can be nested without limitation.
??? no more data_manager…
from bliss.common.data_manager import Container, Scan sample = Container('my_sample`') scan = Scan(name='scan', chain=chain, parent=sample)
Launching a scan is done by calling run method :
scan.run()