Skip to content

Acquisition chain


Used by a Scan object, the AcquisitionChain object is like a tree describing the relations between masters and slaves in term of triggering. To define such a relation, sub-objects inherited from AcquisitionMaster or AcquisitionSlave are inserted into the chain.


The tree is built using add() method with the master and a slave passed as arguments.


  • An AcquisitionChain can only be run once by a scan.
  • It’s sometimes needed to control the state of devices like shutter, multiplexer, or detector cover. Those devices should be controlled with Preset objects.

Add acquisition objects example

2 points loopscan with 1 counter:

from bliss.scanning import chain
from bliss.scanning.acquisition.timer import SoftwareTimerMaster
from bliss.scanning.acquisition.counter import SamplingCounterAcquisitionSlave
acq_chain = chain.AcquisitionChain()
timer = SoftwareTimerMaster(1, npoints=2)
counter_device = SamplingCounterAcquisitionSlave(diode, count_time=1, npoints=2)
acq_chain.add(timer, counter_device)

Out [4]:
acquisition chain
└── timer
    └── simulation_diode_sampling_controller

To execute the loopscan:

from bliss.scanning.scan import Scan
s = Scan(acq_chain,name='simple loop',scan_info={},save=False)

Out [12]: {'elapsed_time': array([0. , 1.0063]),
           'diode': array([11.59349, -6.2747])}

To transform the loopscan into an ascan:

from bliss.scanning.acquisition.motor import LinearStepTriggerMaster
motor_master = LinearStepTriggerMaster(2,robz,0,1)

Out [12]:
acquisition chain
└── axis
    └── timer
        └── simulation_diode_sampling_controller

Calling sequence

When the Scan iterates over the AcquisitionChain, AcquisitionMaster and AcquisitionSlave are called in a defined sequence.

For each iteration, the acquisition chain calls are:

  • wait_ready which should return when the device is ready to have an other trigger.
  • prepare device preparation.
  • start starts acquisition on device

All the calling functions of masters and slaves during a scan can be displayed using the bliss.scans logger.

DEMO [1]: s = loopscan(2,1,diode,diode2,run=False)
DEMO [2]: debugon('bliss.scans')
DEMO [3]:
DEBUG 29,288 Scan: Start timer.wait_ready
DEBUG 29,289 Scan: End timer.wait_ready Took 0.000449s
DEBUG 29,289 Scan: Start diode.wait_ready
DEBUG 29,289 Scan: Start simulation_diode_controller.wait_ready
DEBUG 29,290 Scan: End diode.wait_ready Took 0.000545s
DEBUG 29,290 Scan: End simulation_diode_controller.wait_ready Took 0.000495s

Scan 11 Wed Mar 06 24 2019 /tmp/scans/test_session/data.h5
                                 test_session user = seb
loopscan 2 1

           #         dt[s]         diode        diode2
DEBUG 29,314 Scan: Start simulation_diode_controller.prepare
DEBUG 29,314 Scan: End simulation_diode_controller.prepare Took 0.000120s
DEBUG 29,314 Scan: Start diode.prepare
DEBUG 29,314 Scan: End diode.prepare Took 0.000040s
DEBUG 29,314 Scan: Start timer.prepare
DEBUG 29,315 Scan: End timer.prepare Took 0.000033s
DEBUG 29,315 Scan: Start simulation_diode_controller.start
DEBUG 29,315 Scan: End simulation_diode_controller.start Took 0.000536s
DEBUG 29,315 Scan: Start diode.start
DEBUG 29,316 Scan: End diode.start Took 0.000395s
DEBUG 29,316 Scan: Start timer.start
DEBUG 29,316 Scan: Start timer.trigger_slaves
DEBUG 29,316 Scan: End timer.trigger_slaves Took 0.000090s
DEBUG 29,317 Scan: Start diode.trigger
DEBUG 29,317 Scan: End diode.trigger Took 0.000062s
DEBUG 29,317 Scan: Start simulation_diode_controller.trigger
DEBUG 29,317 Scan: End simulation_diode_controller.trigger Took 0.000037s
DEBUG 29,318 Scan: Start timer.wait_slaves
DEBUG 29,318 Scan: End timer.wait_slaves Took 0.000249s
DEBUG 30,319 Scan: End timer.start Took 1.002655s
DEBUG 30,320 Scan: Start timer.wait_ready
DEBUG 30,321 Scan: End timer.wait_ready Took 0.000566s
DEBUG 30,322 Scan: Start diode.wait_ready
DEBUG 30,322 Scan: Start simulation_diode_controller.wait_ready
DEBUG 30,323 Scan: End diode.wait_ready Took 0.001143s
DEBUG 30,323 Scan: End simulation_diode_controller.wait_ready Took 0.001143s
DEBUG 30,324 Scan: Start timer.prepare
DEBUG 30,325 Scan: End timer.prepare Took 0.000302s
DEBUG 30,326 Scan: Start timer.start
DEBUG 30,327 Scan: Start timer.trigger_slaves
DEBUG 30,327 Scan: End timer.trigger_slaves Took 0.000420s
DEBUG 30,328 Scan: Start diode.trigger
DEBUG 30,329 Scan: End diode.trigger Took 0.000344s
DEBUG 30,329 Scan: Start simulation_diode_controller.trigger
DEBUG 30,329 Scan: End simulation_diode_controller.trigger Took 0.000291s
DEBUG 30,332 Scan: Start timer.wait_slaves
DEBUG 30,334 Scan: End timer.wait_slaves Took 0.001394s
          0             0       3.52747      -2.57778
DEBUG 31,329 Scan: End timer.start Took 1.002674s
DEBUG 31,331 Scan: Start timer.wait_ready
DEBUG 31,331 Scan: End timer.wait_ready Took 0.000565s
DEBUG 31,332 Scan: Start diode.wait_ready
DEBUG 31,332 Scan: Start simulation_diode_controller.wait_ready
DEBUG 31,339 Scan: End simulation_diode_controller.wait_ready Took 0.006308s
DEBUG 31,339 Scan: End diode.wait_ready Took 0.007317s
DEBUG 31,340 Scan: Start timer.wait_slaves
DEBUG 31,340 Scan: End timer.wait_slaves Took 0.000504s
          1       1.01025      -1.74157       10.4382
DEBUG 31,349 Scan: Start timer.stop
DEBUG 31,350 Scan: End timer.stop Took 0.000574s
DEBUG 31,351 Scan: Start diode.stop
DEBUG 31,352 Scan: End diode.stop Took 0.000701s
DEBUG 31,352 Scan: Start simulation_diode_controller.stop
DEBUG 31,352 Scan: End simulation_diode_controller.stop Took 0.000295s
DEBUG 31,353 Scan: Start timer.wait_slaves
DEBUG 31,354 Scan: End timer.wait_slaves Took 0.000611s

Took 0:00:06.935785

Display the chain tree

A print() on the tree property of the acquisition chain object, displays the acquisition tree.


DEMO [1]: s = ascan(robz, 0, 1, 10, 0.1, diode2, diode3, run=False)
DEMO [2]: print(s.acq_chain.tree)
acquisition chain
└── axis
    └── timer
        └── simulation_diode_sampling_controller

Going further

To build more advanced acquisition chain, see the custom scans section.