mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2026-06-09 15:23:04 +02:00
python
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
from .detector import Detector, DetectorError, free_shared_memory
|
||||
from .eiger import Eiger
|
||||
from .jungfrau import Jungfrau
|
||||
from .jungfrau_ctb import JungfrauCTB
|
||||
from _sls_detector import DetectorApi
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,40 @@
|
||||
from functools import partial
|
||||
class Adc:
|
||||
def __init__(self, name, detector):
|
||||
self.name = name
|
||||
self._detector = detector
|
||||
self.get_nmod = self._detector._api.getNumberOfDetectors
|
||||
# Bind functions to get and set the dac
|
||||
self.get = partial(self._detector._api.getAdc, self.name)
|
||||
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""
|
||||
Get dacs either by slice, key or list
|
||||
"""
|
||||
if key == slice(None, None, None):
|
||||
return [self.get(i) / 1000 for i in range(self.get_nmod())]
|
||||
elif isinstance(key, Iterable):
|
||||
return [self.get(k) / 1000 for k in key]
|
||||
else:
|
||||
return self.get(key) / 1000
|
||||
|
||||
def __repr__(self):
|
||||
"""String representation for a single adc in all modules"""
|
||||
degree_sign = u'\N{DEGREE SIGN}'
|
||||
r_str = ['{:14s}: '.format(self.name)]
|
||||
r_str += ['{:6.2f}{:s}C, '.format(self.get(i)/1000, degree_sign) for i in range(self.get_nmod())]
|
||||
return ''.join(r_str).strip(', ')
|
||||
|
||||
|
||||
|
||||
class DetectorAdcs:
|
||||
"""
|
||||
Interface to the ADCs on the readout board
|
||||
"""
|
||||
def __iter__(self):
|
||||
for attr, value in self.__dict__.items():
|
||||
yield value
|
||||
|
||||
def __repr__(self):
|
||||
return '\n'.join([str(t) for t in self])
|
||||
@@ -0,0 +1,125 @@
|
||||
from .detector_property import DetectorProperty
|
||||
from functools import partial
|
||||
import numpy as np
|
||||
|
||||
class Dac(DetectorProperty):
|
||||
"""
|
||||
This class represents a dac on the detector. One instance handles all
|
||||
dacs with the same name for a multi detector instance.
|
||||
|
||||
.. note ::
|
||||
|
||||
This class is used to build up DetectorDacs and is in general
|
||||
not directly accessed to the user.
|
||||
|
||||
|
||||
"""
|
||||
def __init__(self, name, low, high, default, detector):
|
||||
|
||||
super().__init__(partial(detector._api.getDac, name),
|
||||
partial(detector._api.setDac, name),
|
||||
detector._api.getNumberOfDetectors,
|
||||
name)
|
||||
|
||||
self.min_value = low
|
||||
self.max_value = high
|
||||
self.default = default
|
||||
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
"""String representation for a single dac in all modules"""
|
||||
r_str = ['{:10s}: '.format(self.__name__)]
|
||||
r_str += ['{:5d}, '.format(self.get(i)) for i in range(self.get_nmod())]
|
||||
return ''.join(r_str).strip(', ')
|
||||
|
||||
|
||||
class DetectorDacs:
|
||||
_dacs = [('vsvp', 0, 4000, 0),
|
||||
('vtr', 0, 4000, 2500),
|
||||
('vrf', 0, 4000, 3300),
|
||||
('vrs', 0, 4000, 1400),
|
||||
('vsvn', 0, 4000, 4000),
|
||||
('vtgstv', 0, 4000, 2556),
|
||||
('vcmp_ll', 0, 4000, 1500),
|
||||
('vcmp_lr', 0, 4000, 1500),
|
||||
('vcall', 0, 4000, 4000),
|
||||
('vcmp_rl', 0, 4000, 1500),
|
||||
('rxb_rb', 0, 4000, 1100),
|
||||
('rxb_lb', 0, 4000, 1100),
|
||||
('vcmp_rr', 0, 4000, 1500),
|
||||
('vcp', 0, 4000, 200),
|
||||
('vcn', 0, 4000, 2000),
|
||||
('vis', 0, 4000, 1550),
|
||||
('iodelay', 0, 4000, 660)]
|
||||
_dacnames = [_d[0] for _d in _dacs]
|
||||
|
||||
def __init__(self, detector):
|
||||
# We need to at least initially know which detector we are connected to
|
||||
self._detector = detector
|
||||
|
||||
# Index to support iteration
|
||||
self._current = 0
|
||||
|
||||
# Populate the dacs
|
||||
for _d in self._dacs:
|
||||
setattr(self, '_'+_d[0], Dac(*_d, detector))
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self.__getattribute__('_' + name)
|
||||
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in self._dacnames:
|
||||
return self.__getattribute__('_' + name).__setitem__(slice(None, None, None), value)
|
||||
else:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __next__(self):
|
||||
if self._current >= len(self._dacs):
|
||||
self._current = 0
|
||||
raise StopIteration
|
||||
else:
|
||||
self._current += 1
|
||||
return self.__getattr__(self._dacnames[self._current-1])
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
r_str = ['========== DACS =========']
|
||||
r_str += [repr(dac) for dac in self]
|
||||
return '\n'.join(r_str)
|
||||
|
||||
def get_asarray(self):
|
||||
"""
|
||||
Read the dacs into a numpy array with dimensions [ndacs, nmodules]
|
||||
"""
|
||||
dac_array = np.zeros((len(self._dacs), self._detector.n_modules))
|
||||
for i, _d in enumerate(self):
|
||||
dac_array[i,:] = _d[:]
|
||||
return dac_array
|
||||
|
||||
def set_from_array(self, dac_array):
|
||||
"""
|
||||
Set the dacs from an numpy array with dac values. [ndacs, nmodules]
|
||||
"""
|
||||
dac_array = dac_array.astype(np.int)
|
||||
for i, _d in enumerate(self):
|
||||
_d[:] = dac_array[i]
|
||||
|
||||
def set_default(self):
|
||||
"""
|
||||
Set all dacs to their default values
|
||||
"""
|
||||
for _d in self:
|
||||
_d[:] = _d.default
|
||||
|
||||
def update_nmod(self):
|
||||
"""
|
||||
Update the cached value of nmod, needs to be run after adding or
|
||||
removing detectors
|
||||
"""
|
||||
for _d in self:
|
||||
_d._n_modules = self._detector.n_modules
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Function decorators for the sls_detector.
|
||||
"""
|
||||
from .errors import DetectorError
|
||||
import functools
|
||||
|
||||
|
||||
def error_handling(func):
|
||||
"""
|
||||
Check for errors registered by the slsDetectorSoftware
|
||||
"""
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
|
||||
# remove any previous errors
|
||||
self._api.clearErrorMask()
|
||||
|
||||
# call function
|
||||
result = func(self, *args, **kwargs)
|
||||
|
||||
# check for new errors
|
||||
m = self.error_mask
|
||||
if m != 0:
|
||||
msg = self.error_message
|
||||
self._api.clearErrorMask()
|
||||
raise DetectorError(msg)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def property_error_handling(func):
|
||||
"""
|
||||
Check for errors registered by the slsDetectorSoftware
|
||||
"""
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
# remove any previous errors
|
||||
self._detector._api.clearErrorMask()
|
||||
|
||||
# call function
|
||||
result = func(self, *args, **kwargs)
|
||||
|
||||
# check for new errors
|
||||
m = self._detector.error_mask
|
||||
if m != 0:
|
||||
msg = self._detector.error_message
|
||||
self._detector._api.clearErrorMask()
|
||||
raise DetectorError(msg)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
from collections.abc import Iterable
|
||||
import numpy as np
|
||||
|
||||
class DetectorProperty:
|
||||
"""
|
||||
Base class for a detector property that should be accessed by name and index
|
||||
"""
|
||||
def __init__(self, get_func, set_func, nmod_func, name):
|
||||
self.get = get_func
|
||||
self.set = set_func
|
||||
self.get_nmod = nmod_func
|
||||
self.__name__ = name
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == slice(None, None, None):
|
||||
return [self.get(i) for i in range(self.get_nmod())]
|
||||
elif isinstance(key, Iterable):
|
||||
return [self.get(k) for k in key]
|
||||
else:
|
||||
return self.get(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
#operate on all values
|
||||
if key == slice(None, None, None):
|
||||
if isinstance(value, (np.integer, int)):
|
||||
for i in range(self.get_nmod()):
|
||||
self.set(i, value)
|
||||
elif isinstance(value, Iterable):
|
||||
for i in range(self.get_nmod()):
|
||||
self.set(i, value[i])
|
||||
else:
|
||||
raise ValueError('Value should be int or np.integer not', type(value))
|
||||
|
||||
#Iterate over some
|
||||
elif isinstance(key, Iterable):
|
||||
if isinstance(value, Iterable):
|
||||
for k,v in zip(key, value):
|
||||
self.set(k,v)
|
||||
|
||||
elif isinstance(value, int):
|
||||
for k in key:
|
||||
self.set(k, value)
|
||||
|
||||
#Set single value
|
||||
elif isinstance(key, int):
|
||||
self.set(key, value)
|
||||
|
||||
def __repr__(self):
|
||||
s = ', '.join(str(v) for v in self[:])
|
||||
return '{}: [{}]'.format(self.__name__, s)
|
||||
@@ -0,0 +1,618 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Wed Dec 6 11:51:18 2017
|
||||
|
||||
@author: l_frojdh
|
||||
"""
|
||||
|
||||
import socket
|
||||
from collections.abc import Iterable
|
||||
from collections import namedtuple
|
||||
from functools import partial
|
||||
|
||||
from .adcs import Adc, DetectorAdcs
|
||||
from .dacs import DetectorDacs
|
||||
from .decorators import error_handling
|
||||
from .detector import Detector
|
||||
from .detector_property import DetectorProperty
|
||||
from .utils import element_if_equal
|
||||
from sls_detector.errors import DetectorValueError, DetectorError
|
||||
|
||||
class EigerVcmp:
|
||||
"""
|
||||
Convenience class to be able to loop over vcmp for Eiger
|
||||
|
||||
|
||||
.. todo::
|
||||
|
||||
Support single assignment and perhaps unify with Dac class
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, detector):
|
||||
_names = ['vcmp_ll',
|
||||
'vcmp_lr',
|
||||
'vcmp_rl',
|
||||
'vcmp_rr']
|
||||
self.set = []
|
||||
self.get = []
|
||||
for i in range(detector.n_modules):
|
||||
if i % 2 == 0:
|
||||
name = _names
|
||||
else:
|
||||
name = _names[::-1]
|
||||
for n in name:
|
||||
self.set.append(partial(detector._api.setDac, n, i))
|
||||
self.get.append(partial(detector._api.getDac, n, i))
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == slice(None, None, None):
|
||||
return [_d() for _d in self.get]
|
||||
return self.get[key]()
|
||||
|
||||
def __setitem__(self, i, value):
|
||||
self.set[i](value)
|
||||
|
||||
def __repr__(self):
|
||||
return 'vcmp: '+ str(self[:])
|
||||
|
||||
|
||||
class EigerDacs(DetectorDacs):
|
||||
_dacs = [('vsvp', 0, 4000, 0),
|
||||
('vtr', 0, 4000, 2500),
|
||||
('vrf', 0, 4000, 3300),
|
||||
('vrs', 0, 4000, 1400),
|
||||
('vsvn', 0, 4000, 4000),
|
||||
('vtgstv', 0, 4000, 2556),
|
||||
('vcmp_ll', 0, 4000, 1500),
|
||||
('vcmp_lr', 0, 4000, 1500),
|
||||
('vcall', 0, 4000, 4000),
|
||||
('vcmp_rl', 0, 4000, 1500),
|
||||
('rxb_rb', 0, 4000, 1100),
|
||||
('rxb_lb', 0, 4000, 1100),
|
||||
('vcmp_rr', 0, 4000, 1500),
|
||||
('vcp', 0, 4000, 200),
|
||||
('vcn', 0, 4000, 2000),
|
||||
('vis', 0, 4000, 1550),
|
||||
('iodelay', 0, 4000, 660)]
|
||||
_dacnames = [_d[0] for _d in _dacs]
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
class DetectorDelays:
|
||||
_delaynames = ['frame', 'left', 'right']
|
||||
|
||||
def __init__(self, detector):
|
||||
# We need to at least initially know which detector we are connected to
|
||||
self._detector = detector
|
||||
|
||||
setattr(self, '_frame', DetectorProperty(detector._api.getDelayFrame,
|
||||
detector._api.setDelayFrame,
|
||||
detector._api.getNumberOfDetectors,
|
||||
'frame'))
|
||||
|
||||
setattr(self, '_left', DetectorProperty(detector._api.getDelayLeft,
|
||||
detector._api.setDelayLeft,
|
||||
detector._api.getNumberOfDetectors,
|
||||
'left'))
|
||||
|
||||
setattr(self, '_right', DetectorProperty(detector._api.getDelayRight,
|
||||
detector._api.setDelayRight,
|
||||
detector._api.getNumberOfDetectors,
|
||||
'right'))
|
||||
# Index to support iteration
|
||||
self._current = 0
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self.__getattribute__('_' + name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in self._delaynames:
|
||||
return self.__getattribute__('_' + name).__setitem__(slice(None, None, None), value)
|
||||
else:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __next__(self):
|
||||
if self._current >= len(self._delaynames):
|
||||
self._current = 0
|
||||
raise StopIteration
|
||||
else:
|
||||
self._current += 1
|
||||
return self.__getattr__(self._delaynames[self._current-1])
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
hn = self._detector.hostname
|
||||
r_str = ['Transmission delay [ns]\n'
|
||||
'{:11s}{:>8s}{:>8s}{:>8s}'.format('', 'left', 'right', 'frame')]
|
||||
for i in range(self._detector.n_modules):
|
||||
r_str.append('{:2d}:{:8s}{:>8d}{:>8d}{:>8d}'.format(i, hn[i], self.left[i], self.right[i], self.frame[i]))
|
||||
return '\n'.join(r_str)
|
||||
|
||||
|
||||
class Eiger(Detector):
|
||||
"""
|
||||
Subclassing Detector to set up correct dacs and detector specific
|
||||
functions.
|
||||
"""
|
||||
_detector_dynamic_range = [4, 8, 16, 32]
|
||||
|
||||
|
||||
_settings = ['standard', 'highgain', 'lowgain', 'veryhighgain', 'verylowgain']
|
||||
"""available settings for Eiger, note almost always standard"""
|
||||
|
||||
def __init__(self, id=0):
|
||||
super().__init__(id)
|
||||
|
||||
self._active = DetectorProperty(self._api.getActive,
|
||||
self._api.setActive,
|
||||
self._api.getNumberOfDetectors,
|
||||
'active')
|
||||
|
||||
self._vcmp = EigerVcmp(self)
|
||||
self._dacs = EigerDacs(self)
|
||||
self._trimbit_limits = namedtuple('trimbit_limits', ['min', 'max'])(0, 63)
|
||||
self._delay = DetectorDelays(self)
|
||||
|
||||
# Eiger specific adcs
|
||||
self._temp = DetectorAdcs()
|
||||
self._temp.fpga = Adc('temp_fpga', self)
|
||||
self._temp.fpgaext = Adc('temp_fpgaext', self)
|
||||
self._temp.t10ge = Adc('temp_10ge', self)
|
||||
self._temp.dcdc = Adc('temp_dcdc', self)
|
||||
self._temp.sodl = Adc('temp_sodl', self)
|
||||
self._temp.sodr = Adc('temp_sodr', self)
|
||||
self._temp.fpgafl = Adc('temp_fpgafl', self)
|
||||
self._temp.fpgafr = Adc('temp_fpgafr', self)
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def active(self):
|
||||
"""
|
||||
Is the detector active? Can be used to enable or disable a detector
|
||||
module
|
||||
|
||||
Examples
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
d.active
|
||||
>> active: [True, True]
|
||||
|
||||
d.active[1] = False
|
||||
>> active: [True, False]
|
||||
"""
|
||||
return self._active
|
||||
|
||||
@active.setter
|
||||
@error_handling
|
||||
def active(self, value):
|
||||
self._active[:] = value
|
||||
|
||||
@property
|
||||
def measured_period(self):
|
||||
return self._api.getMeasuredPeriod()
|
||||
|
||||
@property
|
||||
def measured_subperiod(self):
|
||||
return self._api.getMeasuredSubPeriod()
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def add_gappixels(self):
|
||||
"""Enable or disable the (virual) pixels between ASICs
|
||||
|
||||
Examples
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
d.add_gappixels = True
|
||||
|
||||
d.add_gappixels
|
||||
>> True
|
||||
|
||||
"""
|
||||
return self._api.getGapPixels()
|
||||
|
||||
@add_gappixels.setter
|
||||
@error_handling
|
||||
def add_gappixels(self, value):
|
||||
self._api.setGapPixels(value)
|
||||
|
||||
@property
|
||||
def dacs(self):
|
||||
"""
|
||||
|
||||
An instance of DetectorDacs used for accessing the dacs of a single
|
||||
or multi detector.
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
d = Eiger()
|
||||
|
||||
#Set all vrf to 1500
|
||||
d.dacs.vrf = 1500
|
||||
|
||||
#Check vrf
|
||||
d.dacs.vrf
|
||||
>> vrf : 1500, 1500
|
||||
|
||||
#Set a single vtr
|
||||
d.dacs.vtr[0] = 1800
|
||||
|
||||
#Set vrf with multiple values
|
||||
d.dacs.vrf = [3500,3700]
|
||||
d.dacs.vrf
|
||||
>> vrf : 3500, 3700
|
||||
|
||||
#read into a variable
|
||||
var = d.dacs.vrf[:]
|
||||
|
||||
#set multiple with multiple values, mostly used for large systems
|
||||
d.dacs.vcall[0,1] = [3500,3600]
|
||||
d.dacs.vcall
|
||||
>> vcall : 3500, 3600
|
||||
|
||||
d.dacs
|
||||
>>
|
||||
========== DACS =========
|
||||
vsvp : 0, 0
|
||||
vtr : 4000, 4000
|
||||
vrf : 1900, 1900
|
||||
vrs : 1400, 1400
|
||||
vsvn : 4000, 4000
|
||||
vtgstv : 2556, 2556
|
||||
vcmp_ll : 1500, 1500
|
||||
vcmp_lr : 1500, 1500
|
||||
vcall : 4000, 4000
|
||||
vcmp_rl : 1500, 1500
|
||||
rxb_rb : 1100, 1100
|
||||
rxb_lb : 1100, 1100
|
||||
vcmp_rr : 1500, 1500
|
||||
vcp : 1500, 1500
|
||||
vcn : 2000, 2000
|
||||
vis : 1550, 1550
|
||||
iodelay : 660, 660
|
||||
|
||||
"""
|
||||
return self._dacs
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def tx_delay(self):
|
||||
"""
|
||||
Transmission delay of the modules to allow running the detector
|
||||
in a network not supporting the full speed of the detector.
|
||||
|
||||
|
||||
::
|
||||
|
||||
d.tx_delay
|
||||
>>
|
||||
Transmission delay [ns]
|
||||
left right frame
|
||||
0:beb048 0 15000 0
|
||||
1:beb049 100 190000 100
|
||||
|
||||
d.tx_delay.left = [2000,5000]
|
||||
"""
|
||||
return self._delay
|
||||
|
||||
def default_settings(self):
|
||||
"""
|
||||
reset the detector to some type of standard settings
|
||||
mostly used when testing
|
||||
"""
|
||||
self.n_frames = 1
|
||||
self.exposure_time = 1
|
||||
self.period = 0
|
||||
self.n_cycles = 1
|
||||
self.n_measurements = 1
|
||||
self.dynamic_range = 16
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def eiger_matrix_reset(self):
|
||||
"""
|
||||
Matrix reset bit for Eiger.
|
||||
|
||||
:py:obj:`True` : Normal operation, the matrix is reset before each acq.
|
||||
:py:obj:`False` : Matrix reset disabled. Used to not reset before
|
||||
reading out analog test pulses.
|
||||
"""
|
||||
return self._api.getCounterBit()
|
||||
|
||||
@eiger_matrix_reset.setter
|
||||
@error_handling
|
||||
def eiger_matrix_reset(self, value):
|
||||
self._api.setCounterBit(value)
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def flowcontrol_10g(self):
|
||||
"""
|
||||
:py:obj:`True` - Flow control enabled :py:obj:`False` flow control disabled.
|
||||
Sets for all moduels, if for some reason access to a single module is needed
|
||||
this can be done trough the C++ API.
|
||||
|
||||
"""
|
||||
fc = self._api.getNetworkParameter('flow_control_10g')
|
||||
return element_if_equal([bool(int(e)) for e in fc])
|
||||
|
||||
@flowcontrol_10g.setter
|
||||
@error_handling
|
||||
def flowcontrol_10g(self, value):
|
||||
if value is True:
|
||||
v = '1'
|
||||
else:
|
||||
v = '0'
|
||||
self._api.setNetworkParameter('flow_control_10g', v, -1)
|
||||
|
||||
@error_handling
|
||||
def pulse_all_pixels(self, n):
|
||||
"""
|
||||
Pulse each pixel of the chip **n** times using the analog test pulses.
|
||||
The pulse height is set using d.dacs.vcall with 4000 being 0 and 0 being
|
||||
the highest pulse.
|
||||
|
||||
::
|
||||
|
||||
#Pulse all pixels ten times
|
||||
d.pulse_all_pixels(10)
|
||||
|
||||
#Avoid resetting before acq
|
||||
d.eiger_matrix_reset = False
|
||||
|
||||
d.acq() #take frame
|
||||
|
||||
#Restore normal behaviour
|
||||
d.eiger_matrix_reset = True
|
||||
|
||||
|
||||
"""
|
||||
self._api.pulseAllPixels(n)
|
||||
|
||||
@error_handling
|
||||
def pulse_diagonal(self, n):
|
||||
"""
|
||||
Pulse pixels in super colums in a diagonal fashion. Used for calibration
|
||||
of vcall. Saves time compared to pulsing all pixels.
|
||||
"""
|
||||
self._api.pulseDiagonal(n)
|
||||
|
||||
@error_handling
|
||||
def pulse_chip(self, n):
|
||||
"""
|
||||
Advance the counter by toggling enable. Gives 2*n+2 int the counter
|
||||
|
||||
"""
|
||||
n = int(n)
|
||||
if n >= -1:
|
||||
self._api.pulseChip(n)
|
||||
else:
|
||||
raise ValueError('n must be equal or larger than -1')
|
||||
|
||||
@property
|
||||
def vcmp(self):
|
||||
"""
|
||||
Convenience function to get and set the individual vcmp of chips
|
||||
Used mainly in the calibration code.
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
#Reading
|
||||
d.vcmp[:]
|
||||
>> [500, 500, 500, 500, 500, 500, 500, 500]
|
||||
|
||||
#Setting
|
||||
d.vcmp = [500, 500, 500, 500, 500, 500, 500, 500]
|
||||
|
||||
|
||||
"""
|
||||
|
||||
return self._vcmp
|
||||
|
||||
@vcmp.setter
|
||||
@error_handling
|
||||
def vcmp(self, values):
|
||||
if len(values) == len(self._vcmp.set):
|
||||
for i, v in enumerate(values):
|
||||
self._vcmp.set[i](v)
|
||||
else:
|
||||
raise ValueError('vcmp only compatible with setting all')
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def rx_udpport(self):
|
||||
"""
|
||||
UDP port for the receiver. Each module has two ports referred to
|
||||
as rx_udpport and rx_udpport2 in the command line interface
|
||||
here they are grouped for each detector
|
||||
|
||||
::
|
||||
|
||||
[0:rx_udpport, 0:rx_udpport2, 1:rx_udpport ...]
|
||||
|
||||
Examples
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
d.rx_udpport
|
||||
>> [50010, 50011, 50004, 50005]
|
||||
|
||||
d.rx_udpport = [50010, 50011, 50012, 50013]
|
||||
|
||||
"""
|
||||
p0 = self._api.getNetworkParameter('rx_udpport')
|
||||
p1 = self._api.getNetworkParameter('rx_udpport2')
|
||||
return [int(val) for pair in zip(p0, p1) for val in pair]
|
||||
|
||||
@rx_udpport.setter
|
||||
@error_handling
|
||||
def rx_udpport(self, ports):
|
||||
"""Requires iterating over elements two and two for setting ports"""
|
||||
a = iter(ports)
|
||||
for i, p in enumerate(zip(a, a)):
|
||||
self._api.setNetworkParameter('rx_udpport', str(p[0]), i)
|
||||
self._api.setNetworkParameter('rx_udpport2', str(p[1]), i)
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def rx_zmqport(self):
|
||||
"""
|
||||
Return the receiver zmq ports. Note that Eiger has two ports per receiver!
|
||||
|
||||
::
|
||||
|
||||
detector.rx_zmqport
|
||||
>> [30001, 30002, 30003, 30004]
|
||||
|
||||
|
||||
"""
|
||||
_s = self._api.getNetworkParameter('rx_zmqport')
|
||||
if _s == '':
|
||||
return []
|
||||
else:
|
||||
return [int(_p) + i for _p in _s for i in range(2)]
|
||||
|
||||
@rx_zmqport.setter
|
||||
@error_handling
|
||||
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)
|
||||
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def sub_exposure_time(self):
|
||||
"""
|
||||
Sub frame exposure time in *seconds* for Eiger in 32bit autosumming mode
|
||||
|
||||
::
|
||||
|
||||
d.sub_exposure_time
|
||||
>> 0.0023
|
||||
|
||||
d.sub_exposure_time = 0.002
|
||||
|
||||
"""
|
||||
return self._api.getSubExposureTime() / 1e9
|
||||
|
||||
|
||||
@sub_exposure_time.setter
|
||||
@error_handling
|
||||
def sub_exposure_time(self, t):
|
||||
#TODO! checking here or in the detector?
|
||||
ns_time = int(t * 1e9)
|
||||
if ns_time > 0:
|
||||
self._api.setSubExposureTime(ns_time)
|
||||
else:
|
||||
raise DetectorValueError('Sub exposure time must be larger than 0')
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def sub_deadtime(self):
|
||||
"""
|
||||
Deadtime between subexposures. Used to mimize noise by delaying the start of the next
|
||||
subexposure.
|
||||
"""
|
||||
return self._api.getSubExposureDeadTime() / 1e9
|
||||
|
||||
|
||||
@sub_deadtime.setter
|
||||
@error_handling
|
||||
def sub_deadtime(self, t):
|
||||
ns_time = int(t * 1e9)
|
||||
if ns_time >= 0:
|
||||
self._api.setSubExposureDeadTime(ns_time)
|
||||
else:
|
||||
raise ValueError('Sub deadtime time must be larger or equal to 0')
|
||||
|
||||
@property
|
||||
def temp(self):
|
||||
"""
|
||||
An instance of DetectorAdcs used to read the temperature
|
||||
of different components
|
||||
|
||||
Examples
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
detector.temp
|
||||
>>
|
||||
temp_fpga : 36.90°C, 45.60°C
|
||||
temp_fpgaext : 31.50°C, 32.50°C
|
||||
temp_10ge : 0.00°C, 0.00°C
|
||||
temp_dcdc : 36.00°C, 36.00°C
|
||||
temp_sodl : 33.00°C, 34.50°C
|
||||
temp_sodr : 33.50°C, 34.00°C
|
||||
temp_fpgafl : 33.81°C, 30.93°C
|
||||
temp_fpgafr : 27.88°C, 29.15°C
|
||||
|
||||
a = detector.temp.fpga[:]
|
||||
a
|
||||
>> [36.568, 45.542]
|
||||
|
||||
|
||||
"""
|
||||
return self._temp
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def tengiga(self):
|
||||
"""Enable 10Gbit/s data output
|
||||
|
||||
Examples
|
||||
----------
|
||||
|
||||
::
|
||||
|
||||
d.tengiga
|
||||
>> False
|
||||
|
||||
d.tengiga = True
|
||||
|
||||
"""
|
||||
return self._api.getTenGigabitEthernet()
|
||||
|
||||
@tengiga.setter
|
||||
@error_handling
|
||||
def tengiga(self, value):
|
||||
self._api.setTenGigabitEthernet(value)
|
||||
|
||||
def set_delays(self, delta):
|
||||
self.tx_delay.left = [delta*(i*2) for i in range(self.n_modules)]
|
||||
self.tx_delay.right = [delta*(i*2+1) for i in range(self.n_modules)]
|
||||
|
||||
|
||||
def setup500k(self, hostnames):
|
||||
"""
|
||||
Setup the Eiger detector to run on the local machine
|
||||
"""
|
||||
|
||||
self.hostname = hostnames
|
||||
self.file_write = False
|
||||
self.image_size = (512, 1024)
|
||||
self.rx_tcpport = [1954, 1955]
|
||||
self.rx_udpport = [50010, 50011, 50004, 50005]
|
||||
self.rx_hostname = socket.gethostname().split('.')[0]
|
||||
self.rx_datastream = False
|
||||
self.file_write = False
|
||||
self.online = True
|
||||
self.receiver_online = True
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Thu Dec 14 17:13:55 2017
|
||||
|
||||
@author: l_frojdh
|
||||
"""
|
||||
|
||||
|
||||
class DetectorError(Exception):
|
||||
"""
|
||||
This error should be used when something fails
|
||||
on the detector side
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class DetectorSettingDoesNotExist(Exception):
|
||||
"""This error should be used when the setting does not exist"""
|
||||
pass
|
||||
|
||||
|
||||
class DetectorValueError(Exception):
|
||||
"""This error should be used when the set value is outside the allowed range"""
|
||||
pass
|
||||
@@ -0,0 +1,275 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Jungfrau detector class and support functions.
|
||||
Inherits from Detector.
|
||||
"""
|
||||
from .adcs import Adc, DetectorAdcs
|
||||
from .decorators import error_handling
|
||||
from .detector import Detector
|
||||
from .dacs import DetectorDacs
|
||||
from .utils import element_if_equal
|
||||
|
||||
|
||||
class JungfrauDacs(DetectorDacs):
|
||||
_dacs = [('vb_comp', 0, 4000, 1220),
|
||||
('vdd_prot', 0, 4000, 3000),
|
||||
('vin_com', 0, 4000, 1053),
|
||||
('vref_prech', 0, 4000, 1450),
|
||||
('vb_pixbuff', 0, 4000, 750),
|
||||
('vb_ds', 0, 4000, 1000),
|
||||
('vref_ds', 0, 4000, 480),
|
||||
('vref_comp', 0, 4000, 420),
|
||||
]
|
||||
_dacnames = [_d[0] for _d in _dacs]
|
||||
|
||||
class Jungfrau(Detector):
|
||||
"""
|
||||
Class used to control a Jungfrau detector. Inherits from the Detector class but a specialized
|
||||
class is needed to provide the correct dacs and unique functions.
|
||||
|
||||
"""
|
||||
_detector_dynamic_range = [4, 8, 16, 32]
|
||||
|
||||
_settings = ['dynamichg0',
|
||||
'dynamicgain',
|
||||
'fixgain1',
|
||||
'fixgain2',
|
||||
'forceswitchg1',
|
||||
'forceswitchg2']
|
||||
"""Available settings for Jungfrau"""
|
||||
|
||||
def __init__(self, multi_id=0):
|
||||
#Init on base calss
|
||||
super().__init__(multi_id)
|
||||
self._dacs = JungfrauDacs(self)
|
||||
|
||||
#Jungfrau specific temps, this can be reduced to a single value?
|
||||
self._temp = DetectorAdcs()
|
||||
self._temp.fpga = Adc('temp_fpga', self)
|
||||
# self._register = Register(self)
|
||||
|
||||
|
||||
@property
|
||||
def dacs(self):
|
||||
"""
|
||||
|
||||
An instance of DetectorDacs used for accessing the dacs of a single
|
||||
or multi detector.
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
#Jungfrau
|
||||
|
||||
|
||||
"""
|
||||
return self._dacs
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def power_chip(self):
|
||||
"""Power on or off the ASICs, True for on False for off"""
|
||||
return self._api.isChipPowered()
|
||||
|
||||
@power_chip.setter
|
||||
@error_handling
|
||||
def power_chip(self, value):
|
||||
self._api.powerChip(value)
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def delay(self):
|
||||
"""Delay after trigger [s]"""
|
||||
return self._api.getDelay()/1e9
|
||||
|
||||
@delay.setter
|
||||
@error_handling
|
||||
def delay(self, t):
|
||||
ns_time = int(t * 1e9)
|
||||
self._api.setDelay(ns_time)
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def n_gates(self):
|
||||
return self._api.getNumberOfGates()
|
||||
|
||||
@n_gates.setter
|
||||
@error_handling
|
||||
def n_gates(self, n):
|
||||
self._api.setNumberOfGates(n)
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def n_probes(self):
|
||||
return self._api.getNumberOfProbes()
|
||||
|
||||
@n_probes.setter
|
||||
@error_handling
|
||||
def n_probes(self, n):
|
||||
self._api.setNumberOfProbes(n)
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def storagecell_start(self):
|
||||
"""
|
||||
First storage cell
|
||||
"""
|
||||
return self._api.getStoragecellStart()
|
||||
|
||||
@storagecell_start.setter
|
||||
@error_handling
|
||||
def storagecell_start(self, value):
|
||||
self._api.setStoragecellStart(value)
|
||||
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def n_storagecells(self):
|
||||
"""
|
||||
number of storage cells used for the measurements
|
||||
"""
|
||||
return self._api.getNumberOfStorageCells()
|
||||
|
||||
@n_storagecells.setter
|
||||
@error_handling
|
||||
def n_storagecells(self, value):
|
||||
self._api.setNumberOfStorageCells(value)
|
||||
|
||||
@property
|
||||
def temp(self):
|
||||
"""
|
||||
An instance of DetectorAdcs used to read the temperature
|
||||
of different components
|
||||
|
||||
Examples
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
detector.temp
|
||||
>>
|
||||
temp_fpga : 36.90°C, 45.60°C
|
||||
|
||||
a = detector.temp.fpga[:]
|
||||
a
|
||||
>> [36.568, 45.542]
|
||||
|
||||
|
||||
"""
|
||||
return self._temp
|
||||
|
||||
@property
|
||||
def temperature_threshold(self):
|
||||
"""Threshold for switching of chips"""
|
||||
return self._api.getThresholdTemperature()
|
||||
|
||||
@temperature_threshold.setter
|
||||
def temperature_threshold(self, t):
|
||||
self._api.setThresholdTemperature(t)
|
||||
|
||||
@property
|
||||
def temperature_control(self):
|
||||
"""
|
||||
Monitor the temperature of the detector and switch off chips if temperature_threshold is
|
||||
crossed
|
||||
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
#activate
|
||||
detector.temperature_control = True
|
||||
|
||||
#deactivate
|
||||
detector.temperature_control = False
|
||||
|
||||
|
||||
"""
|
||||
return self._api.getTemperatureControl()
|
||||
|
||||
@temperature_control.setter
|
||||
def temperature_control(self, v):
|
||||
self._api.setTemperatureControl(v)
|
||||
|
||||
@property
|
||||
def temperature_event(self):
|
||||
"""Have the temperature threshold been crossed?
|
||||
|
||||
Returns
|
||||
---------
|
||||
|
||||
:py:obj:`True` if the threshold have been crossed and temperature_control is active
|
||||
otherwise :py:obj:`False`
|
||||
|
||||
"""
|
||||
return self._api.getTemperatureEvent()
|
||||
|
||||
def reset_temperature_event(self):
|
||||
"""Reset the temperature_event. After reset temperature_event is False"""
|
||||
self._api.resetTemperatureEvent()
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def rx_udpport(self):
|
||||
"""
|
||||
UDP port for the receiver. Each module have one port.
|
||||
Note! Eiger has two ports
|
||||
|
||||
::
|
||||
|
||||
[0:rx_udpport]
|
||||
|
||||
Examples
|
||||
-----------
|
||||
|
||||
::
|
||||
|
||||
d.rx_udpport
|
||||
>> [50010]
|
||||
|
||||
d.rx_udpport = [50010]
|
||||
|
||||
"""
|
||||
return self._api.getNetworkParameter('rx_udpport')
|
||||
|
||||
|
||||
@rx_udpport.setter
|
||||
@error_handling
|
||||
def rx_udpport(self, ports):
|
||||
"""Requires iterating over elements two and two for setting ports"""
|
||||
for i, p in enumerate(ports):
|
||||
self._api.setNetworkParameter('rx_udpport', str(p), i)
|
||||
|
||||
@property
|
||||
def detector_mac(self):
|
||||
s = self._api.getNetworkParameter('detectormac')
|
||||
return element_if_equal(s)
|
||||
|
||||
|
||||
@detector_mac.setter
|
||||
def detector_mac(self, mac):
|
||||
if isinstance(mac, list):
|
||||
for i, m in enumerate(mac):
|
||||
self._api.setNetworkParameter('detectormac', m, i)
|
||||
else:
|
||||
self._api.setNetworkParameter('detectormac', mac, -1)
|
||||
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def detector_ip(self):
|
||||
s = self._api.getNetworkParameter('detectorip')
|
||||
return element_if_equal(s)
|
||||
|
||||
@detector_ip.setter
|
||||
def detector_ip(self, ip):
|
||||
if isinstance(ip, list):
|
||||
for i, addr in enumerate(ip):
|
||||
self._api.setNetworkParameter('detectorip', addr, i)
|
||||
else:
|
||||
self._api.setNetworkParameter('detectorip', ip, -1)
|
||||
@@ -0,0 +1,181 @@
|
||||
from functools import partial
|
||||
from collections.abc import Iterable
|
||||
from collections import namedtuple
|
||||
import socket
|
||||
|
||||
from .detector import Detector
|
||||
from .utils import element_if_equal
|
||||
from .adcs import DetectorAdcs, Adc
|
||||
from .dacs import DetectorDacs
|
||||
from .detector_property import DetectorProperty
|
||||
from .decorators import error_handling
|
||||
from .registers import Register, Adc_register
|
||||
|
||||
class JungfrauCTBDacs(DetectorDacs):
|
||||
_dacs = [('dac0', 0, 4000, 1400),
|
||||
('dac1', 0, 4000, 1200),
|
||||
('dac2', 0, 4000, 900),
|
||||
('dac3', 0, 4000, 1050),
|
||||
('dac4', 0, 4000, 1400),
|
||||
('dac5', 0, 4000, 655),
|
||||
('dac6', 0, 4000, 2000),
|
||||
('dac7', 0, 4000, 1400),
|
||||
('dac8', 0, 4000, 850),
|
||||
('dac9', 0, 4000, 2000),
|
||||
('dac10', 0, 4000, 2294),
|
||||
('dac11', 0, 4000, 983),
|
||||
('dac12', 0, 4000, 1475),
|
||||
('dac13', 0, 4000, 1200),
|
||||
('dac14', 0, 4000, 1600),
|
||||
('dac15', 0, 4000, 1455),
|
||||
('dac16', 0, 4000, 0),
|
||||
('dac17', 0, 4000, 1000),
|
||||
]
|
||||
_dacnames = [_d[0] for _d in _dacs]
|
||||
|
||||
|
||||
|
||||
class JungfrauCTB(Detector):
|
||||
def __init__(self, id = 0):
|
||||
super().__init__(id)
|
||||
self._dacs = JungfrauCTBDacs(self)
|
||||
self._register = Register(self)
|
||||
self._adc_register = Adc_register(self)
|
||||
|
||||
@property
|
||||
def v_a(self):
|
||||
return self._api.getDac_mV('v_a', -1)
|
||||
|
||||
@v_a.setter
|
||||
def v_a(self, value):
|
||||
self._api.setDac_mV('v_a', -1, value)
|
||||
|
||||
@property
|
||||
def v_b(self):
|
||||
return self._api.getDac_mV('v_b', -1)
|
||||
|
||||
@v_b.setter
|
||||
def v_b(self, value):
|
||||
self._api.setDac_mV('v_b', -1, value)
|
||||
|
||||
|
||||
@property
|
||||
def v_c(self):
|
||||
return self._api.getDac_mV('v_c', -1)
|
||||
|
||||
@v_c.setter
|
||||
def v_c(self, value):
|
||||
self._api.setDac_mV('v_c', -1, value)
|
||||
|
||||
@property
|
||||
def v_d(self):
|
||||
return self._api.getDac_mV('v_d', -1)
|
||||
|
||||
@v_d.setter
|
||||
def v_d(self, value):
|
||||
self._api.setDac_mV('v_d', -1, value)
|
||||
|
||||
@property
|
||||
def v_io(self):
|
||||
return self._api.getDac_mV('v_io', -1)
|
||||
|
||||
@v_io.setter
|
||||
def v_io(self, value):
|
||||
self._api.setDac_mV('v_io', -1, value)
|
||||
|
||||
@property
|
||||
def v_limit(self):
|
||||
return self._api.getDac_mV('v_limit', -1)
|
||||
|
||||
@v_limit.setter
|
||||
def v_limit(self, value):
|
||||
self._api.setDac_mV('v_limit', -1, value)
|
||||
|
||||
@property
|
||||
def adc_register(self):
|
||||
return self._adc_register
|
||||
|
||||
# @property
|
||||
# def register(self):
|
||||
# return self._register
|
||||
|
||||
def adcOFF(self):
|
||||
"""Switch off the ADC"""
|
||||
self.adc_register[0x8] = 1
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def dacs(self):
|
||||
"""
|
||||
|
||||
An instance of DetectorDacs used for accessing the dacs of a single
|
||||
or multi detector.
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
#JungfrauCTB
|
||||
|
||||
|
||||
"""
|
||||
return self._dacs
|
||||
|
||||
@property
|
||||
def dbitpipeline(self):
|
||||
return self._api.getDbitPipeline()
|
||||
|
||||
@dbitpipeline.setter
|
||||
def dbitpipeline(self, value):
|
||||
self._api.setDbitPipeline(value)
|
||||
|
||||
|
||||
@property
|
||||
def dbitphase(self):
|
||||
return self._api.getDbitPhase()
|
||||
|
||||
@dbitphase.setter
|
||||
def dbitphase(self, value):
|
||||
self._api.setDbitPhase(value)
|
||||
|
||||
@property
|
||||
def dbitclock(self):
|
||||
return self._api.getDbitClock()
|
||||
|
||||
@dbitclock.setter
|
||||
def dbitclock(self, value):
|
||||
self._api.setDbitClock(value)
|
||||
|
||||
@property
|
||||
def samples(self):
|
||||
return self._api.getJCTBSamples()
|
||||
|
||||
@samples.setter
|
||||
def samples(self, value):
|
||||
self._api.setJCTBSamples(value)
|
||||
|
||||
@property
|
||||
@error_handling
|
||||
def readout_clock(self):
|
||||
"""
|
||||
Speed of the readout clock relative to the full speed
|
||||
|
||||
|
||||
Examples
|
||||
---------
|
||||
|
||||
::
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
return self._api.getReadoutClockSpeed()
|
||||
|
||||
|
||||
@readout_clock.setter
|
||||
@error_handling
|
||||
def readout_clock(self, value):
|
||||
self._api.setReadoutClockSpeed(value)
|
||||
@@ -0,0 +1,18 @@
|
||||
from .decorators import error_handling, property_error_handling
|
||||
class Register:
|
||||
def __init__(self, detector):
|
||||
self._detector = detector
|
||||
|
||||
@property_error_handling
|
||||
def __getitem__(self, key):
|
||||
return self._detector._api.readRegister(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._detector._api.writeRegister(key, value)
|
||||
|
||||
class Adc_register:
|
||||
def __init__(self, detector):
|
||||
self._detector = detector
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._detector._api.writeAdcRegister(key, value)
|
||||
@@ -0,0 +1,32 @@
|
||||
"""
|
||||
Utility functions that are useful for testing and troubleshooting
|
||||
but not directly used in controlling the detector
|
||||
"""
|
||||
|
||||
|
||||
def all_equal(mylist):
|
||||
"""If all elements are equal return true otherwise false"""
|
||||
return all(x == mylist[0] for x in mylist)
|
||||
|
||||
|
||||
def element_if_equal(mylist):
|
||||
"""If all elements are equal return only one element"""
|
||||
if all_equal(mylist):
|
||||
if len(mylist) == 0:
|
||||
return None
|
||||
else:
|
||||
return mylist[0]
|
||||
else:
|
||||
return mylist
|
||||
|
||||
|
||||
def eiger_register_to_time(register):
|
||||
"""
|
||||
Decode register value and return time in s. Values are stored in
|
||||
a 32bit register with bits 2->0 containing the exponent and bits
|
||||
31->3 containing the significand (int value)
|
||||
|
||||
"""
|
||||
clocks = register >> 3
|
||||
exponent = register & 0b111
|
||||
return clocks*10**exponent / 100e6
|
||||
Reference in New Issue
Block a user