This commit is contained in:
Erik Frojdh 2020-01-08 14:55:16 +01:00
parent 639ed52d65
commit c223f00511
8 changed files with 347 additions and 431 deletions

View File

@ -1,7 +1,6 @@
cmake_minimum_required(VERSION 3.11) cmake_minimum_required(VERSION 3.11)
project(slsDetectorPackage) project(slsDetectorPackage)
set(PROJECT_VERSION 5.0.0) set(PROJECT_VERSION 5.0.0)
include(CheckIPOSupported) include(CheckIPOSupported)

View File

@ -26,7 +26,7 @@ set( PYTHON_FILES
decorators.py decorators.py
detector_property.py detector_property.py
# detector.py # detector.py
# eiger.py eiger.py
errors.py errors.py
experimental.py experimental.py
# jungfrau_ctb.py # jungfrau_ctb.py

View File

@ -3,8 +3,13 @@ import sys
import numpy as np import numpy as np
sys.path.append(os.path.join(os.getcwd(), 'bin')) sys.path.append(os.path.join(os.getcwd(), 'bin'))
# from sls_detector import Eiger, Jungfrau, Detector, defs # from sls_detector import Eiger, Jungfrau, Detector, defs
from sls_detector import Detector
from sls_detector import Detector, Eiger, DetectorDacs, Dac
from sls_detector import dacIndex from sls_detector import dacIndex
d = Detector() d = Detector()
e = Eiger()
# from sls_detector.eiger import EigerVcmp
# v = EigerVcmp(d)

View File

@ -1,5 +1,6 @@
# from .detector import Detector, DetectorError, free_shared_memory # from .detector import Detector, DetectorError, free_shared_memory
# from .eiger import Eiger from .eiger import Eiger
from .dacs import DetectorDacs, Dac
from .experimental import Detector from .experimental import Detector
# from .jungfrau import Jungfrau # from .jungfrau import Jungfrau

View File

@ -1,7 +1,8 @@
from .detector_property import DetectorProperty from .detector_property import DetectorProperty
from functools import partial from functools import partial
import numpy as np import numpy as np
import _sls_detector
dacIndex = _sls_detector.slsDetectorDefs.dacIndex
class Dac(DetectorProperty): class Dac(DetectorProperty):
""" """
This class represents a dac on the detector. One instance handles all This class represents a dac on the detector. One instance handles all
@ -14,11 +15,11 @@ class Dac(DetectorProperty):
""" """
def __init__(self, name, low, high, default, detector): def __init__(self, name, enum, low, high, default, detector):
super().__init__(partial(detector._api.getDac, name), super().__init__(partial(detector.getDAC, enum, False),
partial(detector._api.setDac, name), lambda x, y : detector.setDAC(enum, x, False, y),
detector._api.getNumberOfDetectors, detector.size,
name) name)
self.min_value = low self.min_value = low
@ -29,29 +30,28 @@ class Dac(DetectorProperty):
def __repr__(self): def __repr__(self):
"""String representation for a single dac in all modules""" """String representation for a single dac in all modules"""
r_str = ['{:10s}: '.format(self.__name__)] dacstr = ''.join([f'{item:5d}' for item in self.get()])
r_str += ['{:5d}, '.format(self.get(i)) for i in range(self.get_nmod())] return f'{self.__name__:10s}:{dacstr}'
return ''.join(r_str).strip(', ')
# a = Dac('vrf', dacIndex.VRF, 0, 4000, 2500, d )
class DetectorDacs: class DetectorDacs:
_dacs = [('vsvp', 0, 4000, 0), _dacs = [('vsvp', dacIndex.SVP,0, 4000, 0),
('vtr', 0, 4000, 2500), ('vtr', dacIndex.VTR,0, 4000, 2500),
('vrf', 0, 4000, 3300), ('vrf', dacIndex.VRF,0, 4000, 3300),
('vrs', 0, 4000, 1400), ('vrs', dacIndex.VRS,0, 4000, 1400),
('vsvn', 0, 4000, 4000), ('vsvn', dacIndex.SVN,0, 4000, 4000),
('vtgstv', 0, 4000, 2556), ('vtgstv', dacIndex.VTGSTV,0, 4000, 2556),
('vcmp_ll', 0, 4000, 1500), ('vcmp_ll', dacIndex.VCMP_LL,0, 4000, 1500),
('vcmp_lr', 0, 4000, 1500), ('vcmp_lr', dacIndex.VCMP_LR,0, 4000, 1500),
('vcall', 0, 4000, 4000), ('vcall', dacIndex.CAL,0, 4000, 4000),
('vcmp_rl', 0, 4000, 1500), ('vcmp_rl', dacIndex.VCMP_RL,0, 4000, 1500),
('rxb_rb', 0, 4000, 1100), ('rxb_rb', dacIndex.RXB_RB,0, 4000, 1100),
('rxb_lb', 0, 4000, 1100), ('rxb_lb', dacIndex.RXB_LB,0, 4000, 1100),
('vcmp_rr', 0, 4000, 1500), ('vcmp_rr', dacIndex.VCMP_RR,0, 4000, 1500),
('vcp', 0, 4000, 200), ('vcp', dacIndex.VCP,0, 4000, 200),
('vcn', 0, 4000, 2000), ('vcn', dacIndex.VCN,0, 4000, 2000),
('vis', 0, 4000, 1550), ('vis', dacIndex.VIS,0, 4000, 1550),
('iodelay', 0, 4000, 660)] ('iodelay', dacIndex.IO_DELAY,0, 4000, 660)]
_dacnames = [_d[0] for _d in _dacs] _dacnames = [_d[0] for _d in _dacs]
def __init__(self, detector): def __init__(self, detector):
@ -95,7 +95,7 @@ class DetectorDacs:
""" """
Read the dacs into a numpy array with dimensions [ndacs, nmodules] Read the dacs into a numpy array with dimensions [ndacs, nmodules]
""" """
dac_array = np.zeros((len(self._dacs), self._detector.n_modules)) dac_array = np.zeros((len(self._dacs), len(self._detector)))
for i, _d in enumerate(self): for i, _d in enumerate(self):
dac_array[i,:] = _d[:] dac_array[i,:] = _d[:]
return dac_array return dac_array
@ -115,11 +115,3 @@ class DetectorDacs:
for _d in self: for _d in self:
_d[:] = _d.default _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

View File

@ -14,21 +14,20 @@ class DetectorProperty:
def __getitem__(self, key): def __getitem__(self, key):
if key == slice(None, None, None): if key == slice(None, None, None):
return [self.get(i) for i in range(self.get_nmod())] return self.get()
elif isinstance(key, Iterable): elif isinstance(key, Iterable):
return [self.get(k) for k in key] return self.get(list(key))
else: else:
return self.get(key) return self.get([key])[0] #No list for single value
def __setitem__(self, key, value): def __setitem__(self, key, value):
#operate on all values #operate on all values
if key == slice(None, None, None): if key == slice(None, None, None):
if isinstance(value, (np.integer, int)): if isinstance(value, (np.integer, int)):
for i in range(self.get_nmod()): self.set(value, [])
self.set(i, value)
elif isinstance(value, Iterable): elif isinstance(value, Iterable):
for i in range(self.get_nmod()): for i in range(self.get_nmod()):
self.set(i, value[i]) self.set(value[i], [i])
else: else:
raise ValueError('Value should be int or np.integer not', type(value)) raise ValueError('Value should be int or np.integer not', type(value))
@ -36,15 +35,14 @@ class DetectorProperty:
elif isinstance(key, Iterable): elif isinstance(key, Iterable):
if isinstance(value, Iterable): if isinstance(value, Iterable):
for k,v in zip(key, value): for k,v in zip(key, value):
self.set(k,v) self.set(v, [k])
elif isinstance(value, int): elif isinstance(value, int):
for k in key: self.set(value, list(key))
self.set(k, value)
#Set single value #Set single value
elif isinstance(key, int): elif isinstance(key, int):
self.set(key, value) self.set(value, [key])
def __repr__(self): def __repr__(self):
s = ', '.join(str(v) for v in self[:]) s = ', '.join(str(v) for v in self[:])

View File

@ -6,17 +6,20 @@ Created on Wed Dec 6 11:51:18 2017
@author: l_frojdh @author: l_frojdh
""" """
import socket
from collections.abc import Iterable
from collections import namedtuple
from functools import partial
from .adcs import Adc, DetectorAdcs from .experimental import Detector
# 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 .dacs import DetectorDacs
from .detector import Detector import _sls_detector
dacIndex = _sls_detector.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty from .detector_property import DetectorProperty
from .utils import element_if_equal # from .utils import element_if_equal
from sls_detector.errors import DetectorValueError, DetectorError # from sls_detector.errors import DetectorValueError, DetectorError
class EigerVcmp: class EigerVcmp:
""" """
@ -30,20 +33,21 @@ class EigerVcmp:
""" """
def __init__(self, detector): def __init__(self, detector):
_names = ['vcmp_ll', _dacs = [ dacIndex.VCMP_LL,
'vcmp_lr', dacIndex.VCMP_LR,
'vcmp_rl', dacIndex.VCMP_RL,
'vcmp_rr'] dacIndex.VCMP_RR]
self.set = [] self.set = []
self.get = [] self.get = []
for i in range(detector.n_modules): for i in range(detector.size()):
if i % 2 == 0: if i % 2 == 0:
name = _names dacs = _dacs
else: else:
name = _names[::-1] dacs = _dacs[::-1]
for n in name: for d in dacs:
self.set.append(partial(detector._api.setDac, n, i)) print(d, i)
self.get.append(partial(detector._api.getDac, n, i)) self.set.append(lambda x, d=d, i=i : detector.setDAC(d, x, False, [i]))
self.get.append(lambda d=d, i=i : detector.getDAC(d, False, [i])[0])
def __getitem__(self, key): def __getitem__(self, key):
if key == slice(None, None, None): if key == slice(None, None, None):
@ -58,80 +62,84 @@ class EigerVcmp:
class EigerDacs(DetectorDacs): class EigerDacs(DetectorDacs):
_dacs = [('vsvp', 0, 4000, 0), """
('vtr', 0, 4000, 2500), Eiger specific dacs
('vrf', 0, 4000, 3300), """
('vrs', 0, 4000, 1400), _dacs = [('vsvp', dacIndex.SVP,0, 4000, 0),
('vsvn', 0, 4000, 4000), ('vtr', dacIndex.VTR,0, 4000, 2500),
('vtgstv', 0, 4000, 2556), ('vrf', dacIndex.VRF,0, 4000, 3300),
('vcmp_ll', 0, 4000, 1500), ('vrs', dacIndex.VRS,0, 4000, 1400),
('vcmp_lr', 0, 4000, 1500), ('vsvn', dacIndex.SVN,0, 4000, 4000),
('vcall', 0, 4000, 4000), ('vtgstv', dacIndex.VTGSTV,0, 4000, 2556),
('vcmp_rl', 0, 4000, 1500), ('vcmp_ll', dacIndex.VCMP_LL,0, 4000, 1500),
('rxb_rb', 0, 4000, 1100), ('vcmp_lr', dacIndex.VCMP_LR,0, 4000, 1500),
('rxb_lb', 0, 4000, 1100), ('vcall', dacIndex.CAL,0, 4000, 4000),
('vcmp_rr', 0, 4000, 1500), ('vcmp_rl', dacIndex.VCMP_RL,0, 4000, 1500),
('vcp', 0, 4000, 200), ('rxb_rb', dacIndex.RXB_RB,0, 4000, 1100),
('vcn', 0, 4000, 2000), ('rxb_lb', dacIndex.RXB_LB,0, 4000, 1100),
('vis', 0, 4000, 1550), ('vcmp_rr', dacIndex.VCMP_RR,0, 4000, 1500),
('iodelay', 0, 4000, 660)] ('vcp', dacIndex.VCP,0, 4000, 200),
('vcn', dacIndex.VCN,0, 4000, 2000),
('vis', dacIndex.VIS,0, 4000, 1550),
('iodelay', dacIndex.IO_DELAY,0, 4000, 660)]
_dacnames = [_d[0] for _d in _dacs] _dacnames = [_d[0] for _d in _dacs]
# # noinspection PyProtectedMember
# class DetectorDelays:
# _delaynames = ['frame', 'left', 'right']
# noinspection PyProtectedMember # def __init__(self, detector):
class DetectorDelays: # # We need to at least initially know which detector we are connected to
_delaynames = ['frame', 'left', 'right'] # self._detector = detector
def __init__(self, detector): # setattr(self, '_frame', DetectorProperty(detector._api.getDelayFrame,
# We need to at least initially know which detector we are connected to # detector._api.setDelayFrame,
self._detector = detector # detector._api.getNumberOfDetectors,
# 'frame'))
setattr(self, '_frame', DetectorProperty(detector._api.getDelayFrame, # setattr(self, '_left', DetectorProperty(detector._api.getDelayLeft,
detector._api.setDelayFrame, # detector._api.setDelayLeft,
detector._api.getNumberOfDetectors, # detector._api.getNumberOfDetectors,
'frame')) # 'left'))
setattr(self, '_left', DetectorProperty(detector._api.getDelayLeft, # setattr(self, '_right', DetectorProperty(detector._api.getDelayRight,
detector._api.setDelayLeft, # detector._api.setDelayRight,
detector._api.getNumberOfDetectors, # detector._api.getNumberOfDetectors,
'left')) # 'right'))
# # Index to support iteration
# self._current = 0
setattr(self, '_right', DetectorProperty(detector._api.getDelayRight, # def __getattr__(self, name):
detector._api.setDelayRight, # return self.__getattribute__('_' + name)
detector._api.getNumberOfDetectors,
'right'))
# Index to support iteration
self._current = 0
def __getattr__(self, name): # def __setattr__(self, name, value):
return self.__getattribute__('_' + name) # if name in self._delaynames:
# return self.__getattribute__('_' + name).__setitem__(slice(None, None, None), value)
# else:
# super().__setattr__(name, value)
def __setattr__(self, name, value): # def __next__(self):
if name in self._delaynames: # if self._current >= len(self._delaynames):
return self.__getattribute__('_' + name).__setitem__(slice(None, None, None), value) # self._current = 0
else: # raise StopIteration
super().__setattr__(name, value) # else:
# self._current += 1
# return self.__getattr__(self._delaynames[self._current-1])
def __next__(self): # def __iter__(self):
if self._current >= len(self._delaynames): # return self
self._current = 0
raise StopIteration
else:
self._current += 1
return self.__getattr__(self._delaynames[self._current-1])
def __iter__(self): # def __repr__(self):
return self # hn = self._detector.hostname
# r_str = ['Transmission delay [ns]\n'
def __repr__(self): # '{:11s}{:>8s}{:>8s}{:>8s}'.format('', 'left', 'right', 'frame')]
hn = self._detector.hostname # for i in range(self._detector.n_modules):
r_str = ['Transmission delay [ns]\n' # r_str.append('{:2d}:{:8s}{:>8d}{:>8d}{:>8d}'.format(i, hn[i], self.left[i], self.right[i], self.frame[i]))
'{:11s}{:>8s}{:>8s}{:>8s}'.format('', 'left', 'right', 'frame')] # return '\n'.join(r_str)
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)
from .experimental import freeze
@freeze
class Eiger(Detector): class Eiger(Detector):
""" """
Subclassing Detector to set up correct dacs and detector specific Subclassing Detector to set up correct dacs and detector specific
@ -145,79 +153,80 @@ class Eiger(Detector):
def __init__(self, id=0): def __init__(self, id=0):
super().__init__(id) super().__init__(id)
self._frozen = False
self._active = DetectorProperty(self._api.getActive,
self._api.setActive,
self._api.getNumberOfDetectors,
'active')
self._vcmp = EigerVcmp(self)
self._dacs = EigerDacs(self) self._dacs = EigerDacs(self)
self._trimbit_limits = namedtuple('trimbit_limits', ['min', 'max'])(0, 63) self._vcmp = EigerVcmp(self)
self._delay = DetectorDelays(self)
# Eiger specific adcs # self._active = DetectorProperty(self.getActive,
self._temp = DetectorAdcs() # self.setActive,
self._temp.fpga = Adc('temp_fpga', self) # self.size,
self._temp.fpgaext = Adc('temp_fpgaext', self) # 'active')
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 # self._trimbit_limits = namedtuple('trimbit_limits', ['min', 'max'])(0, 63)
def active(self): # self._delay = DetectorDelays(self)
"""
Is the detector active? Can be used to enable or disable a detector
module
Examples # # 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
# def active(self):
# """
# Is the detector active? Can be used to enable or disable a detector
# module
d.active # Examples
>> active: [True, True] # ----------
d.active[1] = False # ::
>> active: [True, False]
"""
return self._active
@active.setter # d.active
def active(self, value): # >> active: [True, True]
self._active[:] = value
@property # d.active[1] = False
def measured_period(self): # >> active: [True, False]
return self._api.getMeasuredPeriod() # """
# return self._active
@property # @active.setter
def measured_subperiod(self): # def active(self, value):
return self._api.getMeasuredSubPeriod() # self._active[:] = value
@property # @property
def add_gappixels(self): # def measured_period(self):
"""Enable or disable the (virual) pixels between ASICs # return self._api.getMeasuredPeriod()
Examples # @property
---------- # def measured_subperiod(self):
# return self._api.getMeasuredSubPeriod()
:: # @property
# def add_gappixels(self):
# """Enable or disable the (virual) pixels between ASICs
d.add_gappixels = True # Examples
# ----------
d.add_gappixels # ::
>> True
""" # d.add_gappixels = True
return self._api.getGapPixels()
@add_gappixels.setter # d.add_gappixels
def add_gappixels(self, value): # >> True
self._api.setGapPixels(value)
# """
# return self._api.getGapPixels()
# @add_gappixels.setter
# def add_gappixels(self, value):
# self._api.setGapPixels(value)
@property @property
def dacs(self): def dacs(self):
@ -280,114 +289,68 @@ class Eiger(Detector):
""" """
return self._dacs return self._dacs
@property # @property
def tx_delay(self): # def tx_delay(self):
""" # """
Transmission delay of the modules to allow running the detector # Transmission delay of the modules to allow running the detector
in a network not supporting the full speed of the detector. # in a network not supporting the full speed of the detector.
:: # ::
d.tx_delay # d.tx_delay
>> # >>
Transmission delay [ns] # Transmission delay [ns]
left right frame # left right frame
0:beb048 0 15000 0 # 0:beb048 0 15000 0
1:beb049 100 190000 100 # 1:beb049 100 190000 100
d.tx_delay.left = [2000,5000] # d.tx_delay.left = [2000,5000]
""" # """
return self._delay # return self._delay
def default_settings(self): # def pulse_all_pixels(self, n):
""" # """
reset the detector to some type of standard settings # Pulse each pixel of the chip **n** times using the analog test pulses.
mostly used when testing # The pulse height is set using d.dacs.vcall with 4000 being 0 and 0 being
""" # the highest pulse.
self.n_frames = 1
self.exposure_time = 1
self.period = 0
self.n_cycles = 1
self.n_measurements = 1
self.dynamic_range = 16
@property # ::
def eiger_matrix_reset(self):
"""
Matrix reset bit for Eiger.
:py:obj:`True` : Normal operation, the matrix is reset before each acq. # #Pulse all pixels ten times
:py:obj:`False` : Matrix reset disabled. Used to not reset before # d.pulse_all_pixels(10)
reading out analog test pulses.
"""
return self._api.getCounterBit()
@eiger_matrix_reset.setter # #Avoid resetting before acq
def eiger_matrix_reset(self, value): # d.eiger_matrix_reset = False
self._api.setCounterBit(value)
@property # d.acq() #take frame
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.
""" # #Restore normal behaviour
fc = self._api.getNetworkParameter('flow_control_10g') # d.eiger_matrix_reset = True
return element_if_equal([bool(int(e)) for e in fc])
@flowcontrol_10g.setter
def flowcontrol_10g(self, value):
if value is True:
v = '1'
else:
v = '0'
self._api.setNetworkParameter('flow_control_10g', v, -1)
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) # self._api.pulseAllPixels(n)
def pulse_diagonal(self, n): # def pulse_diagonal(self, n):
""" # """
Pulse pixels in super colums in a diagonal fashion. Used for calibration # Pulse pixels in super colums in a diagonal fashion. Used for calibration
of vcall. Saves time compared to pulsing all pixels. # of vcall. Saves time compared to pulsing all pixels.
""" # """
self._api.pulseDiagonal(n) # self._api.pulseDiagonal(n)
def pulse_chip(self, n): # def pulse_chip(self, n):
""" # """
Advance the counter by toggling enable. Gives 2*n+2 int the counter # Advance the counter by toggling enable. Gives 2*n+2 int the counter
""" # """
n = int(n) # n = int(n)
if n >= -1: # if n >= -1:
self._api.pulseChip(n) # self._api.pulseChip(n)
else: # else:
raise ValueError('n must be equal or larger than -1') # raise ValueError('n must be equal or larger than -1')
@property @property
def vcmp(self): def vcmp(self):
@ -420,177 +383,99 @@ class Eiger(Detector):
else: else:
raise ValueError('vcmp only compatible with setting all') raise ValueError('vcmp only compatible with setting all')
@property # @property
def rx_udpport(self): # def rx_udpport(self):
""" # """
UDP port for the receiver. Each module has two ports referred to # UDP port for the receiver. Each module has two ports referred to
as rx_udpport and rx_udpport2 in the command line interface # as rx_udpport and rx_udpport2 in the command line interface
here they are grouped for each detector # here they are grouped for each detector
:: # ::
[0:rx_udpport, 0:rx_udpport2, 1:rx_udpport ...] # [0:rx_udpport, 0:rx_udpport2, 1:rx_udpport ...]
Examples # Examples
----------- # -----------
:: # ::
d.rx_udpport # d.rx_udpport
>> [50010, 50011, 50004, 50005] # >> [50010, 50011, 50004, 50005]
d.rx_udpport = [50010, 50011, 50012, 50013] # d.rx_udpport = [50010, 50011, 50012, 50013]
""" # """
p0 = self._api.getReceiverUDPPort() # p0 = self._api.getReceiverUDPPort()
p1 = self._api.getReceiverUDPPort2() # p1 = self._api.getReceiverUDPPort2()
return [int(val) for pair in zip(p0, p1) for val in pair] # return [int(val) for pair in zip(p0, p1) for val in pair]
@rx_udpport.setter # @rx_udpport.setter
def rx_udpport(self, ports): # def rx_udpport(self, ports):
"""Requires iterating over elements two and two for setting ports""" # """Requires iterating over elements two and two for setting ports"""
a = iter(ports) # a = iter(ports)
for i, p in enumerate(zip(a, a)): # for i, p in enumerate(zip(a, a)):
self._api.setReceiverUDPPort(p[0], i) # self._api.setReceiverUDPPort(p[0], i)
self._api.setReceiverUDPPort2(p[1], i) # self._api.setReceiverUDPPort2(p[1], i)
@property @property
def rx_zmqport(self): def rx_zmqport(self):
""" """
Return the receiver zmq ports. Note that Eiger has two ports per receiver! Return the receiver zmq ports. Note that Eiger has two ports per receiver!
This functions therefore differ from the base class.
:: ::
detector.rx_zmqport e.rx_zmqport
>> [30001, 30002, 30003, 30004] >> [30001, 30002, 30003, 30004]
""" """
_s = self._api.getReceiverStreamingPort() ports = self.getRxZmqPort()
if _s == '': return [p + i for p in ports for i in range(2)]
return []
else:
return [int(_p) + i for _p in _s for i in range(2)]
@rx_zmqport.setter
def rx_zmqport(self, port):
if isinstance(port, Iterable):
for i, p in enumerate(port):
self._api.setReceiverStreamingPort(p, i)
else:
self._api.setReceiverStreamingPort(port, -1)
@property # @rx_zmqport.setter
def sub_exposure_time(self): # def rx_zmqport(self, port):
""" # if isinstance(port, Iterable):
Sub frame exposure time in *seconds* for Eiger in 32bit autosumming mode # for i, p in enumerate(port):
# self._api.setReceiverStreamingPort(p, i)
# else:
# self._api.setReceiverStreamingPort(port, -1)
:: # @property
# def temp(self):
# """
# An instance of DetectorAdcs used to read the temperature
# of different components
d.sub_exposure_time # Examples
>> 0.0023 # -----------
d.sub_exposure_time = 0.002 # ::
""" # detector.temp
return self._api.getSubExposureTime() / 1e9 # >>
# 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]
@sub_exposure_time.setter # """
def sub_exposure_time(self, t): # return self._temp
#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
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
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 set_delays(self, delta):
def temp(self): # 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)]
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
def tengiga(self):
"""Enable 10Gbit/s data output
Examples
----------
::
d.tengiga
>> False
d.tengiga = True
"""
return self._api.getTenGigabitEthernet()
@tengiga.setter
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

View File

@ -28,10 +28,10 @@ class Register:
def freeze(cls): def freeze(cls):
cls.__frozen = False cls._frozen = False
def frozensetattr(self, key, value): def frozensetattr(self, key, value):
if self.__frozen and not hasattr(self, key): if self._frozen and not hasattr(self, key):
raise AttributeError( raise AttributeError(
"Class {} is frozen. Cannot set {} = {}".format( "Class {} is frozen. Cannot set {} = {}".format(
cls.__name__, key, value cls.__name__, key, value
@ -44,7 +44,7 @@ def freeze(cls):
@wraps(func) @wraps(func)
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs) func(self, *args, **kwargs)
self.__frozen = True self._frozen = True
return wrapper return wrapper
@ -70,6 +70,8 @@ class Detector(CppDetectorApi):
super().__init__(multi_id) super().__init__(multi_id)
self._register = Register(self) self._register = Register(self)
# CONFIGURATION # CONFIGURATION
def __len__(self): def __len__(self):
return self.size() return self.size()
@ -197,6 +199,18 @@ class Detector(CppDetectorApi):
else: else:
self.setSubExptime(dt.timedelta(seconds=t)) self.setSubExptime(dt.timedelta(seconds=t))
@property
def subdeadtime(self):
res = self.getSubDeadTime()
return element_if_equal([it.total_seconds() for it in res])
@subdeadtime.setter
def subdeadtime(self, t):
if isinstance(t, dt.timedelta):
self.setSubDeadTime(t)
else:
self.setSubDeadTime(dt.timedelta(seconds=t))
@property @property
def period(self): def period(self):
@ -232,14 +246,7 @@ class Detector(CppDetectorApi):
def startingfnum(self, value): def startingfnum(self, value):
self.setStartingFrameNumber(value) self.setStartingFrameNumber(value)
#TODO! testing switches on automatically?
@property
def flowcontrol_10g(self):
return element_if_equal(self.getTenGigaFlowControl())
@flowcontrol_10g.setter
def flowcontrol_10g(self, enable):
self.setTenGigaFlowControl(enable)
#TODO! add txdelay #TODO! add txdelay
@ -647,3 +654,32 @@ class Detector(CppDetectorApi):
@dsamples.setter @dsamples.setter
def dsamples(self, N): def dsamples(self, N):
self.setNumberOfDigitalSamples(N) self.setNumberOfDigitalSamples(N)
"""
Some Eiger stuff, does this have to be here or can we move it to subclass?
"""
@property
def partialreset(self):
return element_if_equal(self.getPartialReset())
@partialreset.setter
def partialreset(self, value):
self.setPartialReset(value)
@property
def tengiga(self):
return element_if_equal(self.getTenGiga())
@tengiga.setter
def tengiga(self, value):
self.setTenGiga(value)
@property
def flowcontrol10g(self):
return element_if_equal(self.getTenGigaFlowControl())
@flowcontrol10g.setter
def flowcontrol10g(self, enable):
self.setTenGigaFlowControl(enable)