mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2025-04-20 02:40:03 +02:00

* removed online flag, removed rxronline flag, added useReceier flag that is set only when rxr hostname is set, removed setonline, setreceiveronline flag, removed ret for ok or fail, using exceptions for this, changed cannot bind socket printout * fixed python
1494 lines
36 KiB
Python
Executable File
1494 lines
36 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Python - sls
|
|
=============
|
|
|
|
"""
|
|
import os
|
|
from collections.abc import Iterable
|
|
from collections import namedtuple
|
|
|
|
from _sls_detector import DetectorApi
|
|
from .detector_property import DetectorProperty
|
|
from .errors import DetectorError, DetectorValueError
|
|
from .registers import Register
|
|
from .utils import element_if_equal
|
|
|
|
import numpy as np
|
|
|
|
class Detector:
|
|
"""
|
|
Base class used as interface with the slsDetectorSoftware. To control a specific detector use the
|
|
derived classes such as Eiger and Jungfrau. Functions as an interface to the C++ API and provides a
|
|
more Pythonic interface
|
|
"""
|
|
|
|
_speed_names = {0: 'Full Speed', 1: 'Half Speed', 2: 'Quarter Speed', 3: 'Super Slow Speed'}
|
|
_speed_int = {'Full Speed': 0, 'Half Speed': 1, 'Quarter Speed': 2, 'Super Slow Speed': 3}
|
|
_settings = []
|
|
|
|
def __init__(self, multi_id=0):
|
|
self._api = DetectorApi(multi_id)
|
|
self._register = Register(self)
|
|
|
|
self._flippeddatax = DetectorProperty(self._api.getFlippedDataX,
|
|
self._api.setFlippedDataX,
|
|
self._api.getNumberOfDetectors,
|
|
'flippeddatax')
|
|
self._flippeddatay = DetectorProperty(self._api.getFlippedDataY,
|
|
self._api.setFlippedDataY,
|
|
self._api.getNumberOfDetectors,
|
|
'flippeddatay')
|
|
|
|
def __len__(self):
|
|
return self._api.getNumberOfDetectors()
|
|
|
|
def __repr__(self):
|
|
return '{}(id = {})'.format(self.__class__.__name__,
|
|
self._api.getMultiDetectorId())
|
|
|
|
|
|
def acq(self):
|
|
"""
|
|
Blocking command to launch the programmed measurement. Number of frames specified by frames, cycles etc.
|
|
"""
|
|
self._api.acq()
|
|
|
|
|
|
@property
|
|
def busy(self):
|
|
"""
|
|
Checks the detector is acquiring. Can also be set but should only be used if the acquire fails and
|
|
leaves the detector with busy == True
|
|
|
|
.. note ::
|
|
|
|
Only works when the measurement is launched using acquire, not with status start!
|
|
|
|
Returns
|
|
--------
|
|
bool
|
|
:py:obj:`True` if the detector is acquiring otherwise :py:obj:`False`
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
d.busy
|
|
>> True
|
|
|
|
#If the detector is stuck reset by:
|
|
d.busy = False
|
|
|
|
|
|
"""
|
|
return self._api.getAcquiringFlag()
|
|
|
|
@busy.setter
|
|
def busy(self, value):
|
|
self._api.setAcquiringFlag(value)
|
|
|
|
|
|
@property
|
|
def client_version(self):
|
|
"""
|
|
:py:obj:`str` The date of commit for the client API version
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
d.client_version
|
|
>> '20180327'
|
|
|
|
"""
|
|
v = hex(self._api.getClientVersion())
|
|
return v[2:]
|
|
|
|
@property
|
|
def detectornumber(self):
|
|
"""
|
|
Get all detector numbers as a list. For Eiger the detector numbers
|
|
correspond to the beb numbers.
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
#for beb083 and beb098
|
|
detector.detector_number
|
|
>> [83, 98]
|
|
|
|
"""
|
|
return self._api.getDetectorNumber()
|
|
|
|
@property
|
|
def detector_type(self):
|
|
"""
|
|
Return either a string or list of strings with the detector type.
|
|
|
|
* Eiger
|
|
* Jungfrau
|
|
* etc.
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
detector.detector_type
|
|
>> 'Eiger'
|
|
|
|
detector.detector_type
|
|
>> ['Eiger', 'Jungfrau']
|
|
|
|
"""
|
|
return element_if_equal(self._api.getDetectorType())
|
|
|
|
@property
|
|
def dynamic_range(self):
|
|
"""
|
|
:obj:`int`: Dynamic range of the detector.
|
|
|
|
+----+-------------+------------------------------+
|
|
| dr | max counts | comments |
|
|
+====+=============+==============================+
|
|
| 4 | 15 | |
|
|
+----+-------------+------------------------------+
|
|
| 8 | 255 | |
|
|
+----+-------------+------------------------------+
|
|
|16 | 4095 | 12 bit internally |
|
|
+----+-------------+------------------------------+
|
|
|32 | 4294967295 | Autosumming of 12 bit frames |
|
|
+----+-------------+------------------------------+
|
|
|
|
Raises
|
|
-------
|
|
ValueError
|
|
If the dynamic range is not available in the detector
|
|
|
|
|
|
"""
|
|
return self._api.getDynamicRange()
|
|
|
|
@dynamic_range.setter
|
|
def dynamic_range(self, dr):
|
|
if dr in self._detector_dynamic_range:
|
|
self._api.setDynamicRange(dr)
|
|
return
|
|
else:
|
|
raise DetectorValueError('Cannot set dynamic range to: {:d} availble options: '.format(dr),
|
|
self._detector_dynamic_range)
|
|
|
|
|
|
@property
|
|
def exposure_time(self):
|
|
"""
|
|
:obj:`double` Exposure time in [s] of a single frame.
|
|
"""
|
|
return self._api.getExposureTime() / 1e9
|
|
|
|
@exposure_time.setter
|
|
def exposure_time(self, t):
|
|
ns_time = int(t * 1e9)
|
|
if ns_time <= 0:
|
|
raise DetectorValueError('Exposure time must be larger than 0')
|
|
self._api.setExposureTime(ns_time)
|
|
|
|
@property
|
|
def file_index(self):
|
|
"""
|
|
:obj:`int` Index for frames and file names
|
|
|
|
Raises
|
|
-------
|
|
ValueError
|
|
If the user tries to set an index less than zero
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
detector.file_index
|
|
>> 0
|
|
|
|
detector.file_index = 10
|
|
detector.file_index
|
|
>> 10
|
|
|
|
"""
|
|
return self._api.getFileIndex()
|
|
|
|
@file_index.setter
|
|
def file_index(self, i):
|
|
if i < 0:
|
|
raise ValueError('Index needs to be positive')
|
|
self._api.setFileIndex(i)
|
|
|
|
@property
|
|
def file_name(self):
|
|
"""
|
|
:obj:`str`: Base file name for writing images
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
detector.file_name
|
|
>> 'run'
|
|
|
|
detector.file_name = 'myrun'
|
|
|
|
#For a single acquisition the detector now writes
|
|
# myrun_master_0.raw
|
|
# myrun_d0_0.raw
|
|
# myrun_d1_0.raw
|
|
# myrun_d2_0.raw
|
|
# myrun_d3_0.raw
|
|
|
|
"""
|
|
return self._api.getFileName()
|
|
|
|
@file_name.setter
|
|
def file_name(self, fname):
|
|
self._api.setFileName(fname)
|
|
|
|
@property
|
|
def file_path(self):
|
|
"""
|
|
:obj:`str`: Path where images are written
|
|
|
|
Raises
|
|
-------
|
|
FileNotFoundError
|
|
If path does not exists
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
detector.file_path
|
|
>> '/path/to/files'
|
|
|
|
detector.file_path = '/new/path/to/other/files'
|
|
|
|
"""
|
|
fp = self._api.getFilePath()
|
|
if fp == '':
|
|
return [self._api.getFilePath(i) for i in range(len(self))]
|
|
else:
|
|
return fp
|
|
|
|
@file_path.setter
|
|
def file_path(self, path):
|
|
if os.path.exists(path) is True:
|
|
self._api.setFilePath(path)
|
|
else:
|
|
raise FileNotFoundError('File path does not exists')
|
|
|
|
@property
|
|
def file_write(self):
|
|
"""
|
|
:obj:`bool` If True write files to disk
|
|
"""
|
|
return self._api.getFileWrite()
|
|
|
|
@file_write.setter
|
|
def file_write(self, fwrite):
|
|
self._api.setFileWrite(fwrite)
|
|
|
|
|
|
@property
|
|
def file_overwrite(self):
|
|
"""
|
|
:obj:`bool` If true overwrite files on disk
|
|
"""
|
|
return self._api.getFileOverWrite()
|
|
|
|
@file_overwrite.setter
|
|
def file_overwrite(self, value):
|
|
self._api.setFileOverWrite(value)
|
|
|
|
@property
|
|
def file_padding(self):
|
|
"""
|
|
Pad files in the receiver
|
|
:obj:`bool` If true pads partial frames
|
|
"""
|
|
return self._api.getReceiverPartialFramesPadding()
|
|
|
|
@file_padding.setter
|
|
def file_padding(self, value):
|
|
self._api.getReceiverPartialFramesPadding(value)
|
|
|
|
@property
|
|
def firmware_version(self):
|
|
"""
|
|
:py:obj:`int` Firmware version of the detector
|
|
"""
|
|
return self._api.getFirmwareVersion()
|
|
|
|
@property
|
|
def flags(self):
|
|
"""Read and set flags. Accepts both single flag as
|
|
string or list of flags.
|
|
|
|
Raises
|
|
--------
|
|
RuntimeError
|
|
If flag not recognized
|
|
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
#Eiger
|
|
detector.flags
|
|
>> ['storeinram', 'parallel']
|
|
|
|
detector.flags = 'nonparallel'
|
|
detector.flags
|
|
>> ['storeinram', 'nonparallel']
|
|
|
|
detector.flags = ['continous', 'parallel']
|
|
|
|
|
|
"""
|
|
return self._api.getReadoutFlags()
|
|
|
|
@flags.setter
|
|
def flags(self, flags):
|
|
if isinstance(flags, str):
|
|
self._api.setReadoutFlag(flags)
|
|
elif isinstance(flags, Iterable):
|
|
for f in flags:
|
|
self._api.setReadoutFlag(f)
|
|
|
|
@property
|
|
def frames_caught(self):
|
|
"""
|
|
Number of frames caught by the receiver. Can be used to check for
|
|
package loss.
|
|
"""
|
|
return self._api.getFramesCaughtByReceiver()
|
|
|
|
|
|
@property
|
|
def frame_discard_policy(self):
|
|
"""
|
|
Decides what the receiver does when packet loss occurs.
|
|
nodiscard - keep all frames
|
|
discardempty - discard only empty frames
|
|
discardpartial - discard partial and empty frames
|
|
"""
|
|
return self._api.getReceiverFrameDiscardPolicy()
|
|
|
|
@frame_discard_policy.setter
|
|
def frame_discard_policy(self, policy):
|
|
self._api.setReceiverFramesDiscardPolicy(policy)
|
|
|
|
|
|
@property
|
|
def api_compatibility(self):
|
|
Compatibility = namedtuple('Compatibility', ['client_detector', 'client_receiver'])
|
|
c = Compatibility(self._api.isClientAndDetectorCompatible(), self._api.isClientAndReceiverCompatible())
|
|
return c
|
|
|
|
@property
|
|
def frame_padding(self):
|
|
"""
|
|
Padd partial frames in the receiver
|
|
"""
|
|
return self._api.getPartialFramesPadding()
|
|
|
|
@frame_padding.setter
|
|
def frame_padding(self, padding):
|
|
self._api.setPartialFramesPadding(padding)
|
|
|
|
def free_shared_memory(self):
|
|
"""
|
|
Free the shared memory that contains the detector settings
|
|
and reinitialized with 0 detectors so that you can keep
|
|
using the same object.
|
|
|
|
"""
|
|
self._api.freeSharedMemory()
|
|
self.__init__(self._api.getMultiDetectorId())
|
|
|
|
@property
|
|
def flipped_data_x(self):
|
|
"""Flips data on x axis. Set for eiger bottom modules"""
|
|
return self._flippeddatax
|
|
|
|
@property
|
|
def flipped_data_y(self):
|
|
"""Flips data on y axis."""
|
|
return self._flippeddatax
|
|
|
|
@property
|
|
def high_voltage(self):
|
|
"""
|
|
High voltage applied to the sensor
|
|
"""
|
|
return self._api.getDac('vhighvoltage', -1)
|
|
|
|
@high_voltage.setter
|
|
def high_voltage(self, voltage):
|
|
voltage = int(voltage)
|
|
if voltage < 0 or voltage > 200:
|
|
raise DetectorValueError('High voltage {:d}V is out of range. Should be between 0-200V'.format(voltage))
|
|
self._api.setDac('vhighvoltage', -1, voltage)
|
|
|
|
|
|
@property
|
|
def hostname(self):
|
|
"""
|
|
:obj:`list` of :obj:`str`: hostnames of all connected detectors
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
detector.hostname
|
|
>> ['beb059', 'beb058']
|
|
|
|
"""
|
|
_hm = self._api.getHostname()
|
|
if _hm == '':
|
|
return []
|
|
return _hm.strip('+').split('+')
|
|
|
|
|
|
@hostname.setter
|
|
def hostname(self, hn):
|
|
if isinstance(hn, str):
|
|
self._api.setHostname(hn)
|
|
else:
|
|
name = ''.join([''.join((h, '+')) for h in hn])
|
|
self._api.setHostname(name)
|
|
|
|
@property
|
|
def image_size(self):
|
|
"""
|
|
:py:obj:`collections.namedtuple` with the image size of the detector
|
|
Also works setting using a normal tuple
|
|
|
|
.. note ::
|
|
|
|
Follows the normal convention in Python of (rows, cols)
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
d.image_size = (512, 1024)
|
|
|
|
d.image_size
|
|
>> ImageSize(rows=512, cols=1024)
|
|
|
|
d.image_size.rows
|
|
>> 512
|
|
|
|
d.image_size.cols
|
|
>> 1024
|
|
|
|
"""
|
|
size = namedtuple('ImageSize', ['rows', 'cols'])
|
|
return size(*self._api.getImageSize())
|
|
|
|
@image_size.setter
|
|
def image_size(self, size):
|
|
self._api.setImageSize(*size)
|
|
|
|
|
|
def load_config(self, fname):
|
|
"""
|
|
Load detector configuration from a configuration file
|
|
|
|
Raises
|
|
--------
|
|
FileNotFoundError
|
|
If the file does not exists
|
|
|
|
"""
|
|
if os.path.isfile(fname):
|
|
self._api.readConfigurationFile(fname)
|
|
else:
|
|
raise FileNotFoundError('Cannot find configuration file')
|
|
|
|
|
|
def load_parameters(self, fname):
|
|
"""
|
|
Setup detector by executing commands in a parameters file
|
|
|
|
|
|
.. note ::
|
|
|
|
If you are relying mainly on the Python API it is probably
|
|
better to track the settings from Python. This function uses
|
|
parameters stored in a text file and the command line commands.
|
|
|
|
Raises
|
|
--------
|
|
FileNotFoundError
|
|
If the file does not exists
|
|
|
|
"""
|
|
if os.path.isfile(fname):
|
|
self._api.readParametersFile(fname)
|
|
else:
|
|
raise FileNotFoundError('Cannot find parameters file')
|
|
|
|
|
|
def load_trimbits(self, fname, idet=-1):
|
|
"""
|
|
Load trimbit file or files. Either called with detector number or -1
|
|
to try to load detector specific trimbit files
|
|
|
|
Parameters
|
|
-----------
|
|
fname:
|
|
:py:obj:`str` Filename (including path) to the trimbit files
|
|
|
|
idet
|
|
:py:obj:`int` Detector to load trimbits to, -1 for all
|
|
|
|
|
|
::
|
|
|
|
#Assuming 500k consisting of beb049 and beb048
|
|
# 0 is beb049
|
|
# 1 is beb048
|
|
|
|
#Load name.sn049 to beb049 and name.sn048 to beb048
|
|
detector.load_trimbits('/path/to/dir/name')
|
|
|
|
#Load one file to a specific detector
|
|
detector.load_trimbits('/path/to/dir/name.sn049', 0)
|
|
|
|
"""
|
|
self._api.loadTrimbitFile(fname, idet)
|
|
|
|
|
|
@property
|
|
def lock(self):
|
|
"""Lock the detector to this client
|
|
|
|
::
|
|
|
|
detector.lock = True
|
|
|
|
"""
|
|
return self._api.getServerLock()
|
|
|
|
@lock.setter
|
|
def lock(self, value):
|
|
self._api.setServerLock(value)
|
|
|
|
@property
|
|
def lock_receiver(self):
|
|
"""Lock the receivers to this client
|
|
|
|
::
|
|
|
|
detector.lock_receiver = True
|
|
|
|
"""
|
|
|
|
return self._api.getReceiverLock()
|
|
|
|
@lock_receiver.setter
|
|
def lock_receiver(self, value):
|
|
self._api.setReceiverLock(value)
|
|
|
|
@property
|
|
def module_geometry(self):
|
|
"""
|
|
:obj:`namedtuple` Geometry(horizontal=nx, vertical=ny)
|
|
of the detector modules.
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
detector.module_geometry
|
|
>> Geometry(horizontal=1, vertical=2)
|
|
|
|
detector.module_geometry.vertical
|
|
>> 2
|
|
|
|
detector.module_geometry[0]
|
|
>> 1
|
|
|
|
"""
|
|
_t = self._api.getDetectorGeometry()
|
|
Geometry = namedtuple('Geometry', ['horizontal', 'vertical'])
|
|
return Geometry(horizontal=_t[0], vertical=_t[1])
|
|
|
|
@property
|
|
def n_frames(self):
|
|
"""
|
|
:obj:`int` Number of frames per acquisition
|
|
"""
|
|
return self._api.getNumberOfFrames()
|
|
|
|
@n_frames.setter
|
|
def n_frames(self, n):
|
|
if n >= 1:
|
|
self._api.setNumberOfFrames(n)
|
|
else:
|
|
raise DetectorValueError('Invalid value for n_frames: {:d}. Number of'\
|
|
' frames should be an integer greater than 0'.format(n))
|
|
|
|
@property
|
|
def frames_per_file(self):
|
|
return self._api.getFramesPerFile()
|
|
|
|
@frames_per_file.setter
|
|
def frames_per_file(self, n):
|
|
self._api.setFramesPerFile(n)
|
|
|
|
@property
|
|
def n_cycles(self):
|
|
"""Number of cycles for the measurement (exp*n_frames)*n_cycles"""
|
|
return self._api.getCycles()
|
|
|
|
@n_cycles.setter
|
|
def n_cycles(self, n_cycles):
|
|
if n_cycles > 0:
|
|
self._api.setCycles(n_cycles)
|
|
else:
|
|
raise DetectorValueError('Number of cycles must be positive')
|
|
|
|
@property
|
|
def n_measurements(self):
|
|
"""
|
|
Number of times to repeat the programmed measurement.
|
|
This is the outer most part. Real time operation is not
|
|
guaranteed since this is software controlled.
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
detector.n_frames = 1
|
|
detector.n_cycles = 1
|
|
detector.n_measurements = 3
|
|
|
|
detector.acq() # 1 frame 3 times
|
|
|
|
detector.n_frames = 5
|
|
detector.n_cycles = 3
|
|
detector.n_measurements = 2
|
|
|
|
detector.acq() # 5x3 frames 2 times total 30 frames
|
|
|
|
"""
|
|
return self._api.getNumberOfMeasurements()
|
|
|
|
@n_measurements.setter
|
|
def n_measurements(self, value):
|
|
if value > 0:
|
|
self._api.setNumberOfMeasurements(value)
|
|
else:
|
|
raise DetectorValueError('Number of measurements must be positive')
|
|
|
|
@property
|
|
def n_modules(self):
|
|
"""
|
|
:obj:`int` Number of (half)modules in the detector
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
detector.n_modules
|
|
>> 2
|
|
|
|
"""
|
|
return self._api.getNumberOfDetectors()
|
|
|
|
|
|
@property
|
|
def last_client_ip(self):
|
|
"""Returns the ip address of the last client
|
|
that accessed the detector
|
|
|
|
Returns
|
|
-------
|
|
|
|
:obj:`str` last client ip
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
detector.last_client_ip
|
|
>> '129.129.202.117'
|
|
|
|
"""
|
|
return self._api.getLastClientIP()
|
|
|
|
@property
|
|
def receiver_last_client_ip(self):
|
|
"""Returns the ip of the client last talking to the receiver"""
|
|
return self._api.getReceiverLastClientIP()
|
|
|
|
@property
|
|
def receiver_online(self):
|
|
"""
|
|
Online flag for the receiver. Is set together with detector.online when creating the detector object
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
d.receiver_online
|
|
>> True
|
|
|
|
d.receiver_online = False
|
|
|
|
"""
|
|
return self._api.getReceiverOnline()
|
|
|
|
|
|
@property
|
|
def receiver_version(self):
|
|
"""
|
|
:py:obj:`str` Receiver version as a string. [yearmonthday]
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
d.receiver_version
|
|
>> '20180327'
|
|
|
|
"""
|
|
v = hex(self._api.getReceiverVersion())
|
|
return v[2:]
|
|
|
|
#When returning instance error hadling needs to be done in the
|
|
#class that is returned
|
|
@property
|
|
def register(self):
|
|
"""Directly manipulate registers on the readout board
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
d.register[0x5d] = 0xf00
|
|
|
|
"""
|
|
return self._register
|
|
|
|
def reset_frames_caught(self):
|
|
"""
|
|
Reset the number of frames caught by the receiver.
|
|
|
|
.. note ::
|
|
|
|
Automatically done when using d.acq()
|
|
|
|
"""
|
|
self._api.resetFramesCaught()
|
|
|
|
@property
|
|
def period(self):
|
|
"""
|
|
:obj:`double` Period between start of frames. Set to 0 for the detector
|
|
to choose the shortest possible
|
|
"""
|
|
_t = self._api.getPeriod()
|
|
return _t / 1e9
|
|
|
|
@period.setter
|
|
def period(self, t):
|
|
ns_time = int(t * 1e9)
|
|
if ns_time < 0:
|
|
raise ValueError('Period must be 0 or larger')
|
|
self._api.setPeriod(ns_time)
|
|
|
|
@property
|
|
def rate_correction(self):
|
|
"""
|
|
:obj:`list` of :obj:`double` Rate correction for all modules.
|
|
Set to 0 for **disabled**
|
|
|
|
.. todo ::
|
|
|
|
Should support individual assignments
|
|
|
|
Raises
|
|
-------
|
|
ValueError
|
|
If the passed list is not of the same length as the number of
|
|
detectors
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
detector.rate_correction
|
|
>> [125.0, 155.0]
|
|
|
|
detector.rate_correction = [125, 155]
|
|
|
|
|
|
"""
|
|
return self._api.getRateCorrection()
|
|
|
|
@rate_correction.setter
|
|
def rate_correction(self, tau_list):
|
|
if len(tau_list) != self.n_modules:
|
|
raise ValueError('List of tau needs the same length')
|
|
self._api.setRateCorrection(tau_list)
|
|
|
|
|
|
@property
|
|
def readout_clock(self):
|
|
"""
|
|
Speed of the readout clock relative to the full speed
|
|
|
|
* Full Speed
|
|
* Half Speed
|
|
* Quarter Speed
|
|
* Super Slow Speed
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
d.readout_clock
|
|
>> 'Half Speed'
|
|
|
|
d.readout_clock = 'Full Speed'
|
|
|
|
|
|
"""
|
|
speed = self._api.getReadoutClockSpeed()
|
|
return self._speed_names[speed]
|
|
|
|
@readout_clock.setter
|
|
def readout_clock(self, value):
|
|
speed = self._speed_int[value]
|
|
self._api.setReadoutClockSpeed(speed)
|
|
|
|
@property
|
|
def receiver_frame_index(self):
|
|
return self._api.getReceiverCurrentFrameIndex()
|
|
|
|
@property
|
|
def rx_datastream(self):
|
|
"""
|
|
Zmq datastream from receiver. :py:obj:`True` if enabled and :py:obj:`False`
|
|
otherwise
|
|
|
|
::
|
|
|
|
#Enable data streaming from receiver
|
|
detector.rx_datastream = True
|
|
|
|
#Check data streaming
|
|
detector.rx_datastream
|
|
>> True
|
|
|
|
"""
|
|
return self._api.getRxDataStreamStatus()
|
|
|
|
@rx_datastream.setter
|
|
def rx_datastream(self, status):
|
|
self._api.setRxDataStreamStatus(status)
|
|
|
|
|
|
@property
|
|
def rx_hostname(self):
|
|
"""
|
|
Receiver hostname
|
|
TODO! setting of individual hostnames, now done with API call
|
|
"""
|
|
return self._api.getReceiverHostname()
|
|
|
|
|
|
@rx_hostname.setter
|
|
def rx_hostname(self, name):
|
|
self._api.setReceiverHostname(name)
|
|
|
|
|
|
@property
|
|
def rx_udpip(self):
|
|
"""
|
|
Receiver UDP ip
|
|
"""
|
|
return self._api.getReceiverUDPIP(-1)
|
|
|
|
|
|
@rx_udpip.setter
|
|
def rx_udpip(self, ip):
|
|
if isinstance(ip, list):
|
|
for i, addr in enumerate(ip):
|
|
self._api.setReceiverUDPIP(addr, i)
|
|
else:
|
|
self._api.setReceiverUDPIP(ip, -1)
|
|
|
|
|
|
@property
|
|
def rx_udpmac(self):
|
|
return self._api.getReceiverUDPMAC(-1)
|
|
|
|
@rx_udpmac.setter
|
|
def rx_udpmac(self, mac):
|
|
if isinstance(mac, list):
|
|
for i, m in enumerate(mac):
|
|
self._api.setReceiverUDPMAC(m, i)
|
|
else:
|
|
self._api.setReceiverUDPMAC(mac, -1)
|
|
|
|
@property
|
|
def rx_tcpport(self):
|
|
return self._api.getReceiverPort()
|
|
|
|
@rx_tcpport.setter
|
|
def rx_tcpport(self, ports):
|
|
if len(ports) != len(self):
|
|
raise ValueError('Number of ports: {} not equal to number of '
|
|
'detectors: {}'.format(len(ports), len(self)))
|
|
else:
|
|
for i, p in enumerate(ports):
|
|
self._api.setReceiverPort(i, p)
|
|
|
|
@property
|
|
def rx_zmqip(self):
|
|
"""
|
|
ip where the receiver streams data
|
|
"""
|
|
ip = self._api.getNetworkParameter('rx_zmqip')
|
|
return element_if_equal(ip)
|
|
|
|
@rx_zmqip.setter
|
|
def rx_zmqip(self, ip):
|
|
self._api.setNetworkParameter('rx_zmqip', ip, -1)
|
|
|
|
|
|
|
|
@property
|
|
def syncclk(self):
|
|
return self._api.getSyncClkSpeed(-1)
|
|
|
|
@property
|
|
def detectormac(self):
|
|
"""
|
|
Read detector mac address
|
|
"""
|
|
mac = self._api.getNetworkParameter('detectormac')
|
|
return element_if_equal(mac)
|
|
|
|
@property
|
|
def detectorip(self):
|
|
"""
|
|
Read detector ip address
|
|
"""
|
|
return self._api.getDetectorIp(-1)
|
|
|
|
|
|
# @detectorip.setter
|
|
# def detectorip(self, ip):
|
|
|
|
|
|
@property
|
|
def client_zmqip(self):
|
|
"""
|
|
Ip address where the client listens to zmq stream
|
|
"""
|
|
ip = self._api.getNetworkParameter('client_zmqip')
|
|
return element_if_equal(ip)
|
|
|
|
@client_zmqip.setter
|
|
|
|
def client_zmqip(self, ip):
|
|
self._api.setNetworkParameter('client_zmqip', ip, -1)
|
|
|
|
|
|
|
|
@property
|
|
def rx_fifodepth(self):
|
|
"""
|
|
Fifo depth of receiver in number of frames
|
|
"""
|
|
return self._api.getReceiverFifoDepth()
|
|
|
|
@rx_fifodepth.setter
|
|
def rx_fifodepth(self, n_frames):
|
|
self._api.setReceiverFifoDepth(n_frames)
|
|
|
|
|
|
@property
|
|
def rx_udpsocksize(self):
|
|
"""
|
|
UDP buffer size
|
|
"""
|
|
buffer_size = [int(s) for s in self._api.getNetworkParameter('rx_udpsocksize')]
|
|
return element_if_equal(buffer_size)
|
|
|
|
@property
|
|
def rx_jsonaddheader(self):
|
|
"""
|
|
UDP buffer size
|
|
"""
|
|
header = self._api.getNetworkParameter('rx_jsonaddheader')
|
|
return element_if_equal(header)
|
|
|
|
@rx_jsonaddheader.setter
|
|
def rx_jsonaddheader(self, header):
|
|
self._api.setNetworkParameter('rx_jsonaddheader', header, -1)
|
|
|
|
|
|
|
|
@rx_udpsocksize.setter
|
|
def rx_udpsocksize(self, buffer_size):
|
|
self._api.setNetworkParameter('rx_udpsocksize', str(buffer_size), -1)
|
|
|
|
|
|
@property
|
|
def rx_realudpsocksize(self):
|
|
"""
|
|
UDP buffer size
|
|
"""
|
|
buffer_size = [int(s) for s in self._api.getNetworkParameter('rx_realudpsocksize')]
|
|
return element_if_equal(buffer_size)
|
|
|
|
|
|
@property
|
|
def rx_zmqport(self):
|
|
"""
|
|
Return the receiver zmq ports.
|
|
|
|
::
|
|
|
|
detector.rx_zmqport
|
|
>> [30001, 30002]
|
|
|
|
"""
|
|
_s = self._api.getNetworkParameter('rx_zmqport')
|
|
if _s == '':
|
|
return []
|
|
else:
|
|
return [int(_p) for _p in _s]
|
|
|
|
@rx_zmqport.setter
|
|
def rx_zmqport(self, port):
|
|
if isinstance(port, Iterable):
|
|
for i, p in enumerate(port):
|
|
self._api.setNetworkParameter('rx_zmqport', str(p), i)
|
|
else:
|
|
self._api.setNetworkParameter('rx_zmqport', str(port), -1)
|
|
|
|
# Add back when versioning is defined
|
|
# @property
|
|
# def software_version(self):
|
|
# return self._api.getSoftwareVersion();
|
|
|
|
|
|
@property
|
|
def user(self):
|
|
return self._api.getUserDetails()
|
|
|
|
@property
|
|
def server_version(self):
|
|
"""
|
|
:py:obj:`int` On-board server version of the detector
|
|
"""
|
|
return hex(self._api.getServerVersion())
|
|
|
|
@property
|
|
def settings(self):
|
|
"""
|
|
Detector settings used to control for example calibration or gain
|
|
switching. For EIGER almost always standard standard.
|
|
|
|
.. warning ::
|
|
|
|
For Eiger setting settings should be followed by setting the threshold
|
|
otherwise reading of the settings will overwrite the set value
|
|
|
|
|
|
"""
|
|
return self._api.getSettings()
|
|
|
|
@settings.setter
|
|
def settings(self, s):
|
|
if s in self._settings:
|
|
self._api.setSettings(s)
|
|
else:
|
|
raise DetectorValueError('Settings: {:s}, not defined for {:s}. '
|
|
'Valid options are: [{:s}]'.format(s, self.detector_type, ', '.join(self._settings)))
|
|
|
|
|
|
@property
|
|
def settings_path(self):
|
|
"""
|
|
The path where the slsDetectorSoftware looks for settings/trimbit files
|
|
"""
|
|
return self._api.getSettingsDir()
|
|
|
|
@settings_path.setter
|
|
def settings_path(self, path):
|
|
if os.path.isdir(path):
|
|
self._api.setSettingsDir(path)
|
|
else:
|
|
raise FileNotFoundError('Settings path does not exist')
|
|
|
|
@property
|
|
def status(self):
|
|
"""
|
|
:py:obj:`str` Status of the detector: idle, running,
|
|
|
|
.. todo ::
|
|
|
|
Check possible values
|
|
|
|
"""
|
|
return self._api.getRunStatus()
|
|
|
|
def start_detector(self):
|
|
"""
|
|
Non blocking command to star acquisition. Needs to be used in combination
|
|
with receiver start.
|
|
"""
|
|
self._api.startAcquisition()
|
|
|
|
def stop_detector(self):
|
|
"""
|
|
Stop acquisition early or if the detector hangs
|
|
"""
|
|
self._api.stopAcquisition()
|
|
|
|
|
|
def start_receiver(self):
|
|
self._api.startReceiver()
|
|
|
|
def stop_receiver(self):
|
|
self._api.stopReceiver()
|
|
|
|
@property
|
|
def threaded(self):
|
|
"""
|
|
Enable parallel execution of commands to the different detector modules
|
|
|
|
Examples
|
|
----------
|
|
|
|
::
|
|
|
|
d.threaded
|
|
>> True
|
|
|
|
d.threaded = False
|
|
|
|
"""
|
|
return self._api.getThreadedProcessing()
|
|
|
|
@threaded.setter
|
|
def threaded(self, value):
|
|
self._api.setThreadedProcessing(value)
|
|
|
|
@property
|
|
def threshold(self):
|
|
"""
|
|
Detector threshold in eV
|
|
"""
|
|
return self._api.getThresholdEnergy()
|
|
|
|
@threshold.setter
|
|
def threshold(self, eV):
|
|
self._api.setThresholdEnergy(eV)
|
|
|
|
@property
|
|
def timing_mode(self):
|
|
"""
|
|
:py:obj:`str` Timing mode of the detector
|
|
|
|
* **auto** Something
|
|
* **trigger** Something else
|
|
|
|
|
|
"""
|
|
return self._api.getTimingMode()
|
|
|
|
@timing_mode.setter
|
|
def timing_mode(self, mode):
|
|
self._api.setTimingMode(mode)
|
|
|
|
|
|
@property
|
|
def trimmed_energies(self):
|
|
"""
|
|
EIGER: the energies at which the detector was trimmed. This also sets
|
|
the range for which the calibration of the detector is valid.
|
|
|
|
|
|
::
|
|
|
|
detector.trimmed_energies = [5400, 6400, 8000]
|
|
|
|
detector.trimmed_energies
|
|
>> [5400, 6400, 8000]
|
|
|
|
"""
|
|
|
|
return self._api.getTrimEnergies()
|
|
|
|
@trimmed_energies.setter
|
|
def trimmed_energies(self, energy_list):
|
|
self._api.setTrimEnergies(energy_list)
|
|
|
|
@property
|
|
def vthreshold(self):
|
|
"""
|
|
Threshold in DAC units for the detector. Sets the individual vcmp of
|
|
all chips in the detector.
|
|
"""
|
|
return self._api.getDac('vthreshold', -1)
|
|
|
|
@vthreshold.setter
|
|
def vthreshold(self, th):
|
|
self._api.setDac('vthreshold', -1, th)
|
|
|
|
@property
|
|
def trimbits(self):
|
|
"""
|
|
Set or read trimbits of the detector.
|
|
|
|
Examples
|
|
---------
|
|
|
|
::
|
|
|
|
#Set all to 32
|
|
d.trimbits = 32
|
|
|
|
d.trimbits
|
|
>> 32
|
|
|
|
#if undefined or different
|
|
d.trimbits
|
|
>> -1
|
|
|
|
"""
|
|
return self._api.getAllTrimbits()
|
|
|
|
@trimbits.setter
|
|
def trimbits(self, value):
|
|
if self._trimbit_limits.min <= value <= self._trimbit_limits.max:
|
|
self._api.setAllTrimbits(value)
|
|
else:
|
|
raise DetectorValueError('Trimbit setting {:d} is outside of range:'\
|
|
'{:d}-{:d}'.format(value, self._trimbit_limits.min, self._trimbit_limits.max))
|
|
|
|
@property
|
|
def client_zmqport(self):
|
|
"""zmq port of the client"""
|
|
_s = self._api.getNetworkParameter('client_zmqport')
|
|
if _s == '':
|
|
return []
|
|
return [int(_p)+i for _p in _s for i in range(2)]
|
|
|
|
|
|
def _provoke_error(self):
|
|
self._api.setErrorMask(1)
|
|
|
|
|
|
def config_network(self):
|
|
"""
|
|
Configures the detector source and destination MAC addresses, IP addresses
|
|
and UDP ports, and computes the IP header checksum for such parameters
|
|
"""
|
|
self._api.configureNetworkParameters()
|
|
|
|
|
|
#TODO! can we make this one function?
|
|
@property
|
|
def patnloop0(self):
|
|
return self._api.getPatternLoops(0, -1)[2]
|
|
|
|
@patnloop0.setter
|
|
def patnloop0(self, n):
|
|
self._api.setPatternLoops(0, -1, -1, n, -1)
|
|
|
|
@property
|
|
def patnloop1(self):
|
|
return self._api.getPatternLoops(1, -1)[2]
|
|
|
|
@patnloop1.setter
|
|
def patnloop1(self, n):
|
|
self._api.setPatternLoops(1, -1, -1, n, -1)
|
|
|
|
@property
|
|
def patnloop2(self):
|
|
return self._api.getPatternLoops(2, -1)[2]
|
|
|
|
@patnloop2.setter
|
|
def patnloop2(self, n):
|
|
self._api.setPatternLoops(2, -1, -1, n, -1)
|
|
|
|
@property
|
|
def patloop0(self):
|
|
return self._api.getPatternLoops(0)[0:2]
|
|
|
|
@patloop0.setter
|
|
def patloop0(self, value):
|
|
start, stop = value
|
|
self._api.setPatternLoops(0, start, stop, -1)
|
|
|
|
@property
|
|
def patloop1(self):
|
|
return self._api.getPatternLoops(1)[0:2]
|
|
|
|
@patloop1.setter
|
|
def patloop1(self, value):
|
|
start, stop = value
|
|
self._api.setPatternLoops(1, start, stop, -1)
|
|
|
|
@property
|
|
def patloop2(self):
|
|
return self._api.getPatternLoops(2)[0:2]
|
|
|
|
@patloop2.setter
|
|
def patloop2(self, value):
|
|
start, stop = value
|
|
self._api.setPatternLoops(2, start, stop, -1)
|
|
|
|
def setPatternWord(self, addr, word, det_id = -1):
|
|
self._api.setPatternWord(addr, word, det_id)
|
|
|
|
def setPatternLoops(self, level, start, stop, n, det_id=-1):
|
|
self._api.setPatternLoops(level, start, stop, n, det_id)
|
|
|
|
def getPatternLoops(self, level):
|
|
return self._api.getPatternLoops(level)
|
|
|
|
def getPatternWaitAddr(self, level):
|
|
return self._api.getPatternWaitAddr(level)
|
|
|
|
def setPatternWaitAddr(self, level, addr):
|
|
self._api.setPatternWaitAddr(level, addr)
|
|
|
|
@property
|
|
def patwait0(self):
|
|
return self._api.getPatternWaitAddr(0)
|
|
|
|
@patwait0.setter
|
|
def patwait0(self, addr):
|
|
self._api.setPatternWaitAddr(0, addr)
|
|
|
|
@property
|
|
def patwait1(self):
|
|
return self._api.getPatternWaitAddr(1)
|
|
|
|
@patwait1.setter
|
|
def patwait1(self, addr):
|
|
self._api.setPatternWaitAddr(1, addr)
|
|
|
|
@property
|
|
def patwait2(self):
|
|
return self._api.getPatternWaitAddr(0)
|
|
|
|
@patwait2.setter
|
|
def patwait2(self, addr):
|
|
self._api.setPatternWaitAddr(2, addr)
|
|
|
|
|
|
def setPatternWaitTime(self, level, duration):
|
|
self._api.setPatternWaitTime(level, duration)
|
|
|
|
def getPatternWaitTime(self, level):
|
|
return self._api.getPatternWaitTime(level)
|
|
|
|
@property
|
|
def patwaittime0(self):
|
|
return self._api.getPatternWaitTime(0)
|
|
|
|
@patwaittime0.setter
|
|
def patwaittime0(self, duration):
|
|
self._api.setPatternWaitTime(0, duration)
|
|
|
|
@property
|
|
def patwaittime1(self):
|
|
return self._api.getPatternWaitTime(1)
|
|
|
|
@patwaittime1.setter
|
|
def patwaittime1(self, duration):
|
|
self._api.setPatternWaitTime(1, duration)
|
|
|
|
@property
|
|
def patwaittime2(self):
|
|
return self._api.getPatternWaitTime(2)
|
|
|
|
@patwaittime2.setter
|
|
def patwaittime2(self, duration):
|
|
self._api.setPatternWaitTime(2, duration)
|
|
|
|
@property
|
|
def patioctrl(self):
|
|
return self._api.setPatternIOControl(np.uint64(-1))
|
|
|
|
@patioctrl.setter
|
|
def patioctrl(self, word):
|
|
self._api.setPatternIOControl(np.uint64(word))
|
|
|
|
@property
|
|
def patlimits(self):
|
|
return self._api.getPatternLoops(-1,-1)[0:2]
|
|
|
|
@patlimits.setter
|
|
def patlimits(self, value):
|
|
start, stop = value
|
|
self._api.setPatternLoops(-1, start, stop, -1)
|
|
|
|
@property
|
|
def patword(self):
|
|
print('Can\'t read')
|
|
|
|
@patword.setter
|
|
def patword(self, value):
|
|
addr, word = value
|
|
self._api.setPatternWord(addr, word)
|
|
|
|
@property
|
|
def patclkctrl(self):
|
|
return self._api.setPatternClockControl(np.uint64(-1))
|
|
|
|
@patclkctrl.setter
|
|
def patclkctrl(self, value):
|
|
self._api.setPatternClockControl(value)
|
|
|
|
|
|
def free_shared_memory(multi_id=0):
|
|
"""
|
|
Function to free the shared memory but do not initialize with new
|
|
0 size detector
|
|
"""
|
|
api = DetectorApi(multi_id)
|
|
api.freeSharedMemory()
|