Skip to content


In Bliss, a counter is a python object that represents an experimental parameter which can be measured during a scan.

Counters are passed to the Scan object in order to select the experimental parameters to measure or in other words, the data that will be produced.


More about Counters and CounterControllers implementation can be found in the developers corner


  • A counter is identified by a name
  • A counter stores information about the shape (dimension) of the associated data (0D, 1D, 2D).
  • A counter stores information about the type of the associated data (float, int, …).
  • A counter is always attached to a CounterController which knows where and how to read the counter data: data = counter )
  • A conversion function can be attached to the counter.
  • A unit can be specified for the associated data.

For example, a counter can be:

  • a scalar parameter like a temperature (0D)
  • a spectrum of a multi-channel analyzer (1D)
  • an image produced by a camera (2D)
  • statistics of a ROI in an image (N x 0D)

Sampling counters

Sampling counters are designed for the measurement of instantaneous values.


A sample counter has a mode property to choose between different sampling behaviors.

The different available modes are:

  • SINGLE: performs a single measurement at the beginning of the counting time.
  • LAST: returns the last measurement at the end of the counting time.
  • MEAN: performs as many samples as possible and computes the averaged value.
  • STATS: in addition to MEAN, produces the usual statistics (mean, N, std, var, min, max, p2v)
  • SAMPLES: in addition to MEAN, produces individual samples as 1D array.
  • INTEGRATE: produces the MEAN multiplied by the counting time.

Example of usage:

In   [9]: diode
Out  [9]: 'diode` counter info:
            counter type = sampling
            sampling mode = MEAN
            fullname = simulation_diode_sampling_controller:diode
            unit = None

In  [10]: diode.mode
Out [10]: <SamplingMode.SINGLE: 4>

In  [11]: diode.mode='MEAN'
In  [12]: diode.mode
Out [12]: <SamplingMode.MEAN: 1>

In  [13]: ct(1.0, diode)
diode =   12.75 (       12.75/s)

In  [14]: diode.statistics
Out [14]: Stat.(mean=12.75, N=92, std=55.95, var=3130.40, min=-99.0, max=98.0)

Sampling counter statistics

Sampling counters read as many samples as possible from the connected hardware in the specified counting time and return, amongst others, an average value (default mode, see below for details). Additionally, some basic statistics of the sampling process are calculated on the fly, which are accessible after the count through the .statistics property.

DEMO [1]: diode.mode
 Out [1]: <SamplingMode.MEAN: 1>

DEMO [2]: ct(1,diode)

diode = 8.03225806451613 ( 8.03225806451613/s)

DEMO [3]: diode.statistics
 Out [3]: SamplingCounterStatistics( mean=8.032, N=93,
                                     std=55.96,  var=3132.16,
                                     min=-98.0,  max=100.0,
                                     p2v=198.0,  count_time=1,
                                     timestamp='2019-07-26 10:13:25')

Values available in SamplingCounterStatistics are

  • mean: Mean value \bar x = \frac {\sum_{j=1}^n x_j}{n}
  • var: Variance \sigma^2 = \displaystyle\frac {\sum_{i=1}^n (x_i - \bar x)^2}{n}
  • std: Standard deviation \sigma = \sqrt{\sigma^2}
  • min: Minimum value x_{min}
  • max: Maxium value x_{max}
  • p2v: Peak to valley x_{max}-x_{min}

To avoid storing individual sample values temporarily, the statistics are calculated in a rolling fashion using Welford’s online algorithm. Internally, the sum of squares of differences from the current mean M_{2,n} = \sum_{i=1}^n (x_i - \bar x_n)^2, is calculated iteratively via M_{2,n} = M_{2,n-1} + (x_n - \bar x_{n-1})(x_n - \bar x_n). Based on M_{2,n} the variance is derived as \sigma^2_n = \frac{M_{2,n}}{n}.

Sampling counter modes

At the end of the counting process, the sampling counter modes are used to specify which value(s) should be published (to hdf5 file and database).

The available modes can be found in bliss.common.measurement.SamplingMode:

DEMO [1]: from bliss.common.measurement import SamplingMode
DEMO [2]: list(SamplingMode)
 Out [3]: [<SamplingMode.MEAN: 1>,
           <SamplingMode.STATS: 2>,
           <SamplingMode.SAMPLES: 3>,
           <SamplingMode.SINGLE: 4>,
           <SamplingMode.LAST: 5>,
           <SamplingMode.INTEGRATE: 6>]
           <SamplingMode.INTEGRATE_STATS: 7>]


The default mode is MEAN which returns the mean (average) value of all samples, which have been read during the counting time.




in addition to SamplingMode.MEAN the nominal counting time is taken into account. This way a counter in the mode SamplingMode.INTEGRATE returns the equivalent of the sum of all samples normalized by the counting time. A use case for this mode is for example the reading of a diode, that should yield a value approximately proportional to the number of photons that hit the diode during the counting time.



publish all the values as calculated for the sampling counter statistics (see above) into the hdf5 file and the redis database.


equivalent to SamplingMode.STATS, but for counters that should behave as described in SamplingMode.INTEGRATE yielding statistics in additional channels.


A counter in this mode publishes only the first sample read from the device, discarding any further samples. If possible (i.e. there is no counter in any other mode on the same AquisitionDevice) only one sample will be read.



A counter in this mode publishes only the last sample discarding any further samples.



SAMPLES mode is different from other modes in the sense that in addition to SamplingMode.MEAN, it generates an additional 1d dataset containing the individual samples in a counting period and also publishes it. It can e.g. be used to do some more complex statistical analysis of the measured values or, as basis for any CalcCounter, that can be used to extract derived quantities from the original dataset. Following is an example for a CalcCounter, that returns the median:

from bliss.common.measurement import CalcCounter
from bliss.scanning.acquisition.calc import CalcHook
import numpy
class Median(CalcHook):
    def compute(self,sender,data_dict):
        if "_samples" in
            return {"median":numpy.median(data_dict[])}
medi = CalcCounter('median',Median(),diode9)
diode9.mode = "SAMPLES"

DEMO [2]: ct(.1,medi)
         Out [2]: Scan(number=224, name=ct, path=<no saving>)

                  dt[s] =          0.0 (         0.0/s)
                  diode9 = 19.333333333333332 ( 193.33333333333331/s)
                  median =         -7.0 (       -70.0/s)

Integrating counters

Integrating counters are designed for time integrated measurements and do not offer any special counting modes. They are bound to a “Time Master” controller which propagates its counting time to the integrating counters that depend on it.


DEMO [49]: lima1
 Out [49]: Simulator - Generator (Simulator) - Lima Simulator

        bin = [1 1]
        flip = [False False]
        height = 1024
        roi = <0,0> <1024 x 1024>
        rotation = rotation_enum.NONE
        sizes = [   0    4 1024 1024]
        type = Bpp32
        width = 1024

        expo_time = 0.1
        mode = mode_enum.SINGLE
        nb_frames = 1
        status = Ready
        status_fault_error = No error
        trigger_mode = trigger_mode_enum.INTERNAL_TRIGGER_MULTI

        ROI Counters: default
        Name  ROI (<X, Y> <W x H>)
        ----  ------------------
        r1    <0, 0> <100 x 200>

        BPM Counters:
        acq_time, intensity, x, y, fwhm_x, fwhm_y

Calculation counters

A CalcCounterController takes multiple counters as inputs and produces multiple calculated counters as outputs.

One calculated counter is defined by a name and a function that computes a new value from the values of the input counters.

The calculation counters can be used in a scan as standard counters and the dependencies on the input counters will be automatically managed.


- plugin: bliss
  module: expression_based_calc
  class: ExpressionCalcCounterController
  name: calc_diodes

      - counter: $diode1
        tags: d1

      - counter: $diode2
        tags: d2

      - counter: $diode3
        tags: d3

      - counter: $diode4
        tags: d4

       m : 0.5
       n : 0.8

      - name: intensity
        expression:  (d1 + d2 + d3 + d4 )

      - name: cen_x
        expression:  m*(d2-d1) + m*(d4-d3)

      - name: cen_y
        expression:  m*(d3-d1) + m*(d4-d2)
More info about Calculation Counters here.

Conversion function

In order to transform the value of a single counter, a ligther procedure than calculation counters can be used. A conversion function can be dynamicaly added to a counter.

Example usable in a session or in its setup:

# Invert sign of a keithley counter:
kdiag.conversion_function = lambda x : -x

# Multiply cc12 counter by 3:
cc12.conversion_function = lambda x : 3*x

Counters list


The lscnt() command displays all the counters that are currently available in the Bliss session.

It shows the counters names, their associated controllers and the shape of the associated data.

The fullname attribute of a counter is the concatenation of the counter name and its controller name.

Measurement groups

As it is not convenient to count on all counters of the session during a scan, Bliss provides the MeasurementGroup object.

A measurement group is a sub-set of counters which can be defined through the configuration files.

Several measurement groups can be defined and one can be chosen as the default.

Counters can be added/removed or enabled/disabled on the fly while in a Bliss session.

IN  [52]: MG1
OUT [52]: MeasurementGroup: MG1 (state='default')
   - Existing states : 'default'

   Enabled                       Disabled
   ----------------------------  -------------------------------------------
   simu_diode_controller:diode   heater:heater_counter
   simu_diode_controller:diode2  sample_regulation:sample_regulation_setpoint
   simu_diode_controller:diode3  thermo_sample:thermo_sample_counter

IN [53]: ct(1.0)

Tue Feb 18 17:04:36 2020

 diode =       1.6125 (      1.6125/s)
diode2 =       7.5125 (      7.5125/s)
diode3 =          8.5 (         8.5/s)

Out [53]: Scan(number=6, name=ct, path=)

More info about Measurement groups here.