Get a frame stream from a merlin detector

This notebook shows how you can access the stream of frames from a Merlin Medipix detector, without using the high-level features of LiberTEM, like user-defined functions (UDFs). You get access to the data in a chunked fashion, that is, you’ll always get a small frame stack to work on.

Usage with the simulator

If you want to use this with the simulated data source, run something like this in the background:

libertem-live-mib-sim ~/Data/default.hdr --cached=MEM --wait-trigger

The --wait-trigger option is important for this notebook to function correctly since that allows to drain the data socket before an acquisition like it is necessary for a real-world Merlin detector.

A suitable MIB dataset can be downloaded at https://zenodo.org/record/5113449.

On Linux, MEMFD is also supported as a cache. Use NONE to deactivate the cache.

[1]:
# set this to the host/port where the merlin data server is listening:
MERLIN_DATA_SOCKET = ('127.0.0.1', 6342)
MERLIN_CONTROL_SOCKET = ('127.0.0.1', 6341)
[2]:
%matplotlib nbagg
[3]:
import time
import logging
import numpy as np
import matplotlib.pyplot as plt
[4]:
from libertem_live.detectors.merlin import MerlinDataSource
from libertem_live.detectors.merlin import MerlinControl
[5]:
data_source = MerlinDataSource(host=MERLIN_DATA_SOCKET[0], port=MERLIN_DATA_SOCKET[1], pool_size=3)
control = MerlinControl(host=MERLIN_CONTROL_SOCKET[0], port=MERLIN_CONTROL_SOCKET[1])
[6]:
result = np.zeros((256, 256), dtype=np.float32)
result_nav = np.zeros(128 * 128, dtype=np.float32)
[7]:
# connect the control and data sockets, and close after the block automatically:
with control, data_source:
    # Drain data from previous acquisitions out of the data socket,
    # which could for example happen if a connection to the data socket was cancelled.
    # Draining might not be necessary with current Merlin software anymore.
    data_source.socket.drain()

    # Tell the detector to send us some data:
    # (in a real setup, you probably want to properly configure triggering,
    # or even start the microscope scan here)
    control.cmd('STARTACQUISITION')
    control.cmd('SOFTTRIGGER')

    index = 0

    for frames in data_source.stream(
        # How many frames do we expect in total?
        # If you put a too large number here, the loop will possibly block;
        # a too small number means you won't get the data for the whole scan
        # (should match the `result_nav` shape above)
        num_frames=result_nav.size,

        # Get frames in chunks of N, which can have an effect on performance
        # (too small or too large chunks are slower than the optimum, which depends
        # on the CPU you are using):
        chunk_size=16,
    ):
        result += frames.buf.sum(axis=0)
        real_size = frames.buf.shape[0]
        result_nav[index:index+real_size] = frames.buf.sum(axis=(1, 2))
        index += real_size
[8]:
fig, axes = plt.subplots(1, 2)
axes[0].imshow(np.log1p(result))
axes[1].imshow(np.log1p(result_nav.reshape((128, 128))))
[8]:
<matplotlib.image.AxesImage at 0x7f0682aecd30>
[ ]: