#--------------------------------------------------------------------------
"""
Class :py:class:`PyDetectorAccess` contains a collection of python access methods to detector associated information
====================================================================================================================
Access method to calibration and geometry parameters, raw data, etc.
Low level implementation is done on python.
See classes
- :class:`AlgoAccess`
- :class:`AreaDetector`
- :class:`ControlDataDetector`
- :class:`DdlDetector` - access to DDL data
- :class:`DetectorTypes`
- :class:`EpicsDetector` - access to EPICS data
- :class:`EvrDetector` - access to EVR data
- :class:`Generic1DDetector`
- :class:`GenericWFDetector`
- :class:`GlobalUtils`
- :class:`IpimbDetector`
- :class:`OceanDetector`
- :class:`PyDataAccess`
- :class:`PyDetectorAccess` - Python access interface to data
- :class:`PyDetector` - factory for different detectors
- :class:`TDCDetector`
- :class:`UsdUsbDetector` - UsdUsb Encoder Box
- :class:`WFDetector` - access to waveform detector dataSee classes
This software was developed for the LCLS project.
If you use all or part of it, please give an appropriate acknowledgment.
Author Mikhail Dubrovin
"""
#------------------------------
import sys
import numpy as np
import _psana
import Detector.PyDataAccess as pda
#import Detector.GlobalUtils as gu
import PSCalib.GlobalUtils as gu
from PSCalib.CalibParsStore import cps
from PSCalib.CalibFileFinder import CalibFileFinder
from PSCalib.GeometryAccess import GeometryAccess, img_from_pixel_arrays
from PSCalib.NDArrIO import save_txt, load_txt
from pyimgalgos.cm_epix import cm_epix
#from pypdsdata.xtc import TypeId # types from pdsdata/xtc/TypeId.hh, ex: TypeId.Type.Id_CspadElement
##-----------------------------
[docs]class PyDetectorAccess :
"""Class :py:class:`PyDetectorAccess` - python access to detector data.
"""
GEO_NOT_LOADED = 0
GEO_LOADED_CALIB = 1
GEO_LOADED_DEFAULT = 2
GEO_LOADED_DCS = 3
##-----------------------------
[docs] def __init__(self, source, env, pbits=0) :
"""Class :py:class:`PyDetectorAccess` constructor parameters:
- source - data source, ex: _psana.Source('DetInfo(CxiDs2.0:Cspad.0)')
- env - environment
- pbits - print control bit-word
"""
#print 'In c-tor DetPyAccess'
self.source = source
self.str_src= gu.string_from_source(source)
self.env = env
self.pbits = pbits
self.dettype = gu.det_type_from_source(self.str_src)
self.do_offset = False # works for camera
self.correct_time = True # works for acqiris
self.do_calib_imp = False # works for imp
if self.pbits & 1 : self.print_attributes()
self.cpst = None
self.runnum_cps = -1
self.geo = None
self.runnum_geo = -1
self.mbits = None
self.cfg_gain_mask_is_loaded = False
self.runnum_cfg = -1
self._gain_mask = None
self.counter_cspad2x2_msg = 0
self.reshape_to_3d = False
##-----------------------------
[docs] def runnum(self, par) :
"""Returns integer run number from different options of input parameter.
Parameter
- par : int or psana.Event() - integer run number or psana event object.
Returns
- int - run number or 0 if can't be defined.
"""
return par if isinstance(par, int) else par.run() if isinstance(par, _psana.Event) else 0
##-----------------------------
[docs] def time_par(self, par) :
"""Returns parameter which can be used to define dataset time, _psana.Event or _psana.Env.object.
"""
return par if isinstance(par,_psana.Event) else self.env
##-----------------------------
def cpstore(self, par) : # par = evt or runnum
runnum = self.runnum(par)
#print 80*'_'
#print 'cpstore XXX runnum = %d' % runnum
#print 'cpstore XXX par', par,
#print 'XXX isinstance(par, psana.Event)', isinstance(par, _psana.Event)
# for 1st entry and when runnum is changing:
if runnum != self.runnum_cps or self.cpst is None :
self.runnum_cps = runnum
group = gu.dic_det_type_to_calib_group[self.dettype]
#self.cpst = cps.Create(self.env.calibDir(), group, self.str_src, runnum, self.pbits)
self.cpst = cps.CreateForEvtEnv(self.env.calibDir(), group, self.str_src, par, self.env, self.pbits & 0377)
if self.pbits & 1 : print 'PSCalib.CalibParsStore object is created for run %d' % runnum
return self.cpst
##-----------------------------
[docs] def default_geometry(self) :
"""Returns default geometry object for some of detectors"""
import CalibManager.AppDataPath as apputils
defname = 'Detector/geometry-def-epix100a.data' if self.dettype == gu.EPIX100A\
else 'Detector/geometry-def-pnccd.data' if self.dettype == gu.PNCCD\
else 'Detector/geometry-def-cspad.data' if self.dettype == gu.CSPAD\
else 'Detector/geometry-def-cspad2x2.data' if self.dettype == gu.CSPAD2X2\
else None
if defname is None :
self.geo = None
return
fname = apputils.AppDataPath(defname).path()
if self.pbits : print '%s: Load default geometry from file %s' % (self.__class__.__name__, fname)
self.geo = GeometryAccess(fname, 0377 if self.pbits else 0)
if self.geo is not None : self.geo_load_status = self.GEO_LOADED_DEFAULT
#return GeometryAccess(fname, 0377 if self.pbits else 0)
##-----------------------------
[docs] def geoaccess_dcs(self, tpar) :
"""Returns geometry object.
Parameter
- tpar : _psana.Event | _psana.Env | float time
Returns
- GeometryAccess - geometry object
"""
import PSCalib.DCMethods as dcm
cdir = self.env.calibDir()
data = dcm.get_constants(tpar, self.env, self.str_src, ctype=gu.GEOMETRY, calibdir=cdir, vers=None, verb=self.pbits & 16)
if data is None : return
#for s in data : print 'XXX: %s' % s
#import tempfile
#fntmp = tempfile.NamedTemporaryFile(mode='r+b',suffix='.data')
#print 'XXX Save constants in tmp file: %s' % fntmp.name
#save_txt(fntmp.name, data, cmts='', fmt='%.1f')
#self.geo = GeometryAccess(fntmp.name, 0377 if self.pbits else 0)
self.geo = GeometryAccess(pbits=0377 if self.pbits else 0)
if self.geo is not None :
self.geo.load_pars_from_str(data)
#self.geo.print_list_of_geos()
self.geo_load_status = self.GEO_LOADED_DCS
##-----------------------------
[docs] def geoaccess_calib(self, runnum) :
"""Returns geometry object from calib store or None if can't load geometry.
"""
group = gu.dic_det_type_to_calib_group[self.dettype]
cff = CalibFileFinder(self.env.calibDir(), group, 0377 if self.pbits else 0)
fname = cff.findCalibFile(self.str_src, 'geometry', runnum)
if fname :
self.geo = GeometryAccess(fname, 0377 if self.pbits else 0)
if self.pbits & 1 : print 'PSCalib.GeometryAccess object is created for run %d' % runnum
if self.geo.valid : self.geo_load_status = self.GEO_LOADED_CALIB
else :
self.geo = None
if self.pbits & 1 : print 'WARNING: PSCalib.GeometryAccess object is NOT created for run %d - geometry file is missing.' % runnum
##-----------------------------
[docs] def geoaccess(self, par) : # par = _psana.Event | int runnum
"""Returns geometry or None if can't load.
"""
runnum = self.runnum(par)
if runnum != self.runnum_geo :
# for 1st entry and when run is changing:
self.runnum_geo = runnum
self.geo_load_status = self.GEO_NOT_LOADED
self.geo = None
# 1) load geometry object from calib store
self.geoaccess_calib(runnum)
# 2) fallback load file from DCS
if self.geo is None :
self.geoaccess_dcs(self.time_par(par))
# 3) load default geometry if it is still unavailable. Implemented for a few detectors.
if self.geo is None :
self.default_geometry()
# arrays for caching
self.iX = None
self.iY = None
self.coords_x_arr = None
self.coords_y_arr = None
self.coords_z_arr = None
self.areas_arr = None
self.mask_geo_arr = None
self.mbits = None
self.pixel_size_val = None
return self.geo
##-----------------------------
def print_attributes(self) :
print 'PyDetectorAccess attributes:\n source: %s\n dtype : %d\n pbits : %d' % \
(self.source, self.dettype, self.pbits), \
'\n do_offset (Camera): %s\n correct_time (Acqiris): %s\n do_calib_imp (Imp): %s' % \
(self.do_offset, self.correct_time, self.do_calib_imp)
##-----------------------------
def set_env(self, env) :
self.env = env
##-----------------------------
def set_source(self, source) :
self.source = source
self.str_src = gu.string_from_source(source)
##-----------------------------
def pedestals(self, par) : # par: evt or runnum(int)
return self.cpstore(par).pedestals()
##-----------------------------
def pixel_rms(self, par) :
return self.cpstore(par).pixel_rms()
##-----------------------------
def pixel_gain(self, par) :
return self.cpstore(par).pixel_gain()
##-----------------------------
def pixel_offset(self, par) :
return self.cpstore(par).pixel_offset()
##-----------------------------
def pixel_mask(self, par) :
return self.cpstore(par).pixel_mask()
##-----------------------------
def pixel_bkgd(self, par) :
return self.cpstore(par).pixel_bkgd()
##-----------------------------
def pixel_status(self, par) :
return self.cpstore(par).pixel_status()
##-----------------------------
def pixel_datast(self, par) :
return self.cpstore(par).pixel_datast()
##-----------------------------
def common_mode(self, par) :
return self.cpstore(par).common_mode()
##-----------------------------
def common_mode_apply(self, alg_num, kwargs, nda):
if alg_num == 6:
cm_epix(img=nda, **kwargs)
else:
print 'alg_num does not exist'
##-----------------------------
def ndim(self, par=0, ctype=gu.PEDESTALS) :
sh = np.array(self.shape())
if sh is not None : return sh.size
return self.cpstore(par).ndim(ctype)
##-----------------------------
def size(self, par=0, ctype=gu.PEDESTALS) :
sh = np.array(self.shape())
if sh is not None : return sh.prod()
return self.cpstore(par).size(ctype)
##-----------------------------
def shape_calib(self, par, ctype=gu.PEDESTALS) :
return self.cpstore(par).shape(ctype)
##-----------------------------
def status(self, par, ctype=gu.PEDESTALS) :
return self.cpstore(par).status(ctype)
##-----------------------------
##-----------------------------
##-----------------------------
def _shaped_geo_array(self, arr) :
if arr is None : return None
if self.dettype == gu.EPIX100A : arr.shape = (704, 768)
#if self.dettype == gu.CSPAD : arr.shape = (32, 185, 388)
return arr
##-----------------------------
def _update_coord_arrays(self, par, do_update=False) :
""" Returns True if pixel index arrays are available, othervise False.
"""
if self.geoaccess(par) is None : return False
else :
if self.coords_x_arr is None or do_update :
self.coords_x_arr, self.coords_y_arr, self.coords_z_arr = self.geo.get_pixel_coords()
if self.coords_x_arr is None : return False
return True
##-----------------------------
def coords_x(self, par) :
if not self._update_coord_arrays(par) : return None
return self._shaped_geo_array(self.coords_x_arr)
##-----------------------------
def coords_y(self, par) :
if not self._update_coord_arrays(par) : return None
return self._shaped_geo_array(self.coords_y_arr)
##-----------------------------
def coords_z(self, par) :
if not self._update_coord_arrays(par) : return None
return self._shaped_geo_array(self.coords_z_arr)
##-----------------------------
def coords_xy(self, par) :
if not self._update_coord_arrays(par) : return None
return self._shaped_geo_array(self.coords_x_arr), self._shaped_geo_array(self.coords_y_arr)
##-----------------------------
def coords_xyz(self, par) :
if not self._update_coord_arrays(par) : return None
return self._shaped_geo_array(self.coords_x_arr),\
self._shaped_geo_array(self.coords_y_arr),\
self._shaped_geo_array(self.coords_z_arr)
##-----------------------------
def areas(self, par) :
if self.geoaccess(par) is None : return None
else :
if self.areas_arr is None :
self.areas_arr = self.geo.get_pixel_areas()
return self._shaped_geo_array(self.areas_arr)
##-----------------------------
# mbits = +1-edges; +2-wide central cols; +4-non-bond; +8/+16-four/eight non-bond neighbors
def mask_geo(self, par, mbits=15, **kwargs) :
if mbits != self.mbits : # check if update is required
self.mbits = mbits
self.mask_geo_arr = None
if self.geoaccess(par) is None : return None
else :
if self.mask_geo_arr is None :
self.mask_geo_arr = self.geo.get_pixel_mask(mbits=mbits, **kwargs)
return self._shaped_geo_array(self.mask_geo_arr)
##-----------------------------
def _update_index_arrays(self, par, pix_scale_size_um=None, xy0_off_pix=None, do_update=False) :
""" Returns True if pixel index arrays are available, othervise False.
"""
if self.geoaccess(par) is None : return False
else :
if self.iX is None or do_update :
self.iX, self.iY = self.geo.get_pixel_coord_indexes(oname=None, oindex=0,\
pix_scale_size_um=pix_scale_size_um,\
xy0_off_pix=xy0_off_pix, do_tilt=True)
if self.iX is None : return False
return True
##-----------------------------
[docs] def indexes_x(self, par, pix_scale_size_um=None, xy0_off_pix=None, do_update=False) :
"""Returns pixel index array iX."""
if not self._update_index_arrays(par, pix_scale_size_um, xy0_off_pix, do_update) : return None
return self._shaped_geo_array(self.iX)
##-----------------------------
[docs] def indexes_y(self, par, pix_scale_size_um=None, xy0_off_pix=None, do_update=False) :
"""Returns pixel index array iY."""
if not self._update_index_arrays(par, pix_scale_size_um, xy0_off_pix, do_update) : return None
return self._shaped_geo_array(self.iY)
##-----------------------------
[docs] def indexes_xy(self, par, pix_scale_size_um=None, xy0_off_pix=None, do_update=False) :
"""Returns two pixel index arrays iX and iY."""
if not self._update_index_arrays(par, pix_scale_size_um, xy0_off_pix, do_update) : return None
if self.iX is None : return None, None # single None is not the same as (None, None) !
return self._shaped_geo_array(self.iX), self._shaped_geo_array(self.iY)
##-----------------------------
[docs] def point_indexes(self, par, pxy_um=(0,0), pix_scale_size_um=None, xy0_off_pix=None) :
"""Returns ix, iy indexes of the point p_um x,y coordinates in [um]"""
if self.geoaccess(par) is None : return None, None
ix, iy = self.geo.point_coord_indexes(p_um=pxy_um, oname=None, oindex=0,\
pix_scale_size_um=pix_scale_size_um,\
xy0_off_pix=xy0_off_pix, do_tilt=True)
return ix, iy
##-----------------------------
def pixel_size(self, par) :
if self.geoaccess(par) is None : return None
else :
if self.pixel_size_val is None :
self.pixel_size_val = self.geo.get_pixel_scale_size()
return self.pixel_size_val
##-----------------------------
def move_geo(self, par, dx, dy, dz) :
if self.geoaccess(par) is None : pass
else : return self.geo.move_geo(None, 0, dx, dy, dz)
##-----------------------------
def tilt_geo(self, par, dtx, dty, dtz) :
if self.geoaccess(par) is None : pass
else : return self.geo.tilt_geo(None, 0, dtx, dty, dtz)
##-----------------------------
def image_xaxis(self, par, pix_scale_size_um=None, x0_off_pix=None) :
pix_size = pix_scale_size_um if pix_scale_size_um is not None else self.pixel_size(par)
carr = self.coords_x(par)
if carr is None : return None
cmin, cmax = carr.min(), carr.max() + 0.5*pix_size
if x0_off_pix is None :
return np.arange(cmin, cmax, pix_size)
else :
return np.arange(cmin-pix_size*x0_off_pix, cmax, pix_size)
##-----------------------------
def image_yaxis(self, par, pix_scale_size_um=None, y0_off_pix=None) :
pix_size = pix_scale_size_um if pix_scale_size_um is not None else self.pixel_size(par)
carr = self.coords_y(par)
if carr is None : return None
cmin, cmax = carr.min(), carr.max() + 0.5*pix_size
if y0_off_pix is None :
return np.arange(cmin, cmax, pix_size)
else :
return np.arange(cmin-pix_size*y0_off_pix, cmax, pix_size)
##-----------------------------
def image(self, par, img_nda, pix_scale_size_um=None, xy0_off_pix=None, do_update=False) :
if not self._update_index_arrays(par, pix_scale_size_um, xy0_off_pix, do_update) : return None
return img_from_pixel_arrays(self.iX, self.iY, img_nda)
##-----------------------------
def do_reshape_2d_to_3d(self, flag=False) :
self.reshape_to_3d = flag
##-----------------------------
def ndarray_from_image(self, par, image, pix_scale_size_um=None, xy0_off_pix=None, do_update=False) :
if image is None : return None
if len(image.shape) != 2 : return None
# 2016-06-05 return image if reshaping to 3d is requested and geometry is missing
# !!! there is no check that original array is 2d or specific detector type.
if self.reshape_to_3d and self.geoaccess(par) is None :
return np.array(image, copy=True)
if not self._update_index_arrays(par, pix_scale_size_um, xy0_off_pix, do_update) : return None
return self._shaped_geo_array(np.array([image[r,c] for r,c in zip(self.iX, self.iY)]))
##-----------------------------
##-----------------------------
##-----------------------------
##-----------------------------
def inst(self) :
return self.env.instrument()
##-----------------------------
def print_config(self, evt) :
print '%s:print_config(evt) - is not implemented in pythonic version' % self.__class__.__name__
##-----------------------------
def set_print_bits(self, pbits) :
self.pbits = pbits
if self.cpst is not None : self.cpst.set_print_bits(0177777)
if self.geo is not None : self.geo.set_print_bits(0377 if pbits else 0)
##-----------------------------
[docs] def set_do_offset(self, do_offset=False) :
"""On/off application of offset in raw_data_camera(...)
"""
self.do_offset = do_offset
##-----------------------------
[docs] def set_correct_acqiris_time(self, correct_time=True) :
"""On/off correction of time for acqiris
"""
self.correct_time = correct_time
##-----------------------------
[docs] def set_calib_imp(self, do_calib_imp=False) :
"""On/off imp calibration
"""
self.do_calib_imp = do_calib_imp
##-----------------------------
[docs] def gain_mask(self, par, gain=None) :
"""Returns a gain map extracted from detector configuration data.
Currently implemented for CSPAD only.
Returns None for other detectors or missing configuration for CSPAD.
"""
runnum = self.runnum(par)
if runnum == self.runnum_cfg :
if self.cfg_gain_mask_is_loaded :
return self._gain_mask
else :
self.runnum_cfg = runnum
self.cfg_gain_mask_is_loaded = False
if self.dettype == gu.CSPAD : self._gain_mask = self.cspad_gain_mask(gain)
elif self.dettype == gu.CSPAD2X2 : self._gain_mask = self.cspad2x2_gain_mask(gain)
else :
self.cfg_gain_mask_is_loaded = True
self._gain_mask = None
return self._gain_mask
##-----------------------------
[docs] def gain_mask_non_zero(self, par, gain=None) :
"""The same as gain_mask, but returns None if ALL pixels have high gain"""
gm = self.gain_mask(par, gain)
if gm is not None :
if not gm.any() : return None
return gm
##-----------------------------
def raw_data(self, evt, env) :
#print 'TypeId.Type.Id_CspadElement: ', TypeId.Type.Id_CspadElement
#print 'TypeId.Type.Id_CspadConfig: ', TypeId.Type.Id_CspadConfig
if self.dettype == gu.CSPAD : return self.raw_data_cspad(evt, env) # 3 ms
elif self.dettype == gu.CSPAD2X2 : return self.raw_data_cspad2x2(evt, env) # 0.6 ms
elif self.dettype == gu.PRINCETON : return self.raw_data_princeton(evt, env) # 0.7 ms
elif self.dettype == gu.PNCCD : return self.raw_data_pnccd(evt, env) # 0.8 ms
elif self.dettype == gu.ANDOR : return self.raw_data_andor(evt, env) # 0.1 ms
elif self.dettype == gu.ANDOR3D : return self.raw_data_andor(evt, env)
elif self.dettype == gu.JUNGFRAU : return self.raw_data_jungfrau(evt, env)
elif self.dettype == gu.FCCD960 : return self.raw_data_fccd960(evt, env) # 11 ms
elif self.dettype == gu.EPIX100A : return self.raw_data_epix(evt, env) # 0.3 ms
elif self.dettype == gu.EPIX10K : return self.raw_data_epix(evt, env)
elif self.dettype == gu.EPIX : return self.raw_data_epix(evt, env)
elif self.dettype == gu.ACQIRIS : return self.raw_data_acqiris(evt, env)
elif self.dettype == gu.OPAL1000 : return self.raw_data_camera(evt, env) # 1 ms
elif self.dettype == gu.OPAL2000 : return self.raw_data_camera(evt, env)
elif self.dettype == gu.OPAL4000 : return self.raw_data_camera(evt, env)
elif self.dettype == gu.OPAL8000 : return self.raw_data_camera(evt, env)
elif self.dettype == gu.ORCAFL40 : return self.raw_data_camera(evt, env)
elif self.dettype == gu.TM6740 : return self.raw_data_camera(evt, env) # 0.24 ms
elif self.dettype == gu.QUARTZ4A150: return self.raw_data_camera(evt, env)
elif self.dettype == gu.RAYONIX : return self.raw_data_camera(evt, env)
elif self.dettype == gu.IMP : return self.raw_data_imp(evt, env)
elif self.dettype == gu.FCCD : return self.raw_data_camera(evt, env)
elif self.dettype == gu.TIMEPIX : return self.raw_data_timepix(evt, env)
elif self.dettype == gu.FLI : return self.raw_data_fli(evt, env)
elif self.dettype == gu.PIMAX : return self.raw_data_pimax(evt, env)
elif self.dettype == gu.ZYLA : return self.raw_data_zyla(evt, env)
elif self.dettype == gu.EPICSCAM : return self.raw_data_camera(evt, env)
else : return None
##-----------------------------
def raw_data_cspad(self, evt, env) :
# data object
d = pda.get_cspad_data_object(evt, self.source)
if d is None :
if self.pbits & 1 : print 'cspad data object is not found'
return None
# configuration from data
c = pda.get_cspad_config_object(env, self.source)
if c is None :
if self.pbits & 1 : print 'cspad config object is not found'
return None
nquads = d.quads_shape()[0]
nquads_c = c.numQuads()
#print 'd.TypeId: ', d.TypeId
if self.pbits & 8 : print 'nquads in data: %d and config: %d' % (nquads, nquads_c)
arr = np.zeros((4,8,185,388), dtype=np.int16) if nquads<4 else np.empty((4,8,185,388), dtype=np.int16)
for iq in range(nquads) :
q = d.quads(iq)
qnum = q.quad()
qdata = q.data()
roim = c.roiMask(qnum)
if self.pbits & 8 : print 'qnum: %d qdata.shape: %s, mask: %d' % (qnum, str(qdata.shape), roim)
#roim = 0375 # for test only
if roim == 0377 :
arr[qnum,:] = qdata
else :
if self.pbits : print 'PyDetectorAccessr: quad configuration has non-complete mask = %d of included 2x1' % roim
qdata_full = np.zeros((8,185,388), dtype=qdata.dtype)
i = 0
for s in range(8) :
if roim & (1<<s) :
qdata_full[s,:] = qdata[i,:]
i += 1
arr[qnum,:,:] = qdata_full
if self.pbits & 8 : print 'arr.shape: ', arr.shape
arr.shape = (32,185,388)
return arr
##-----------------------------
def raw_data_cspad2x2(self, evt, env) :
# data object
d = pda.get_cspad2x2_data_object(evt, self.source)
if d is None : return None
# configuration object
c = pda.get_cspad2x2_config_object(env, self.source)
if c is None :
if self.pbits and self.counter_cspad2x2_msg <3 :
print 'WARNING PyDetectorAccess: missing configuration object for source %s' % (self.str_src)
if self.counter_cspad2x2_msg == 2 : print 'Stop WARNING messages for %s configuration' % self.str_src
self.counter_cspad2x2_msg += 1
#return None
if c.roiMask() != 3 :
if self.pbits and self.counter_cspad2x2_msg <3 :
print 'WARNING PyDetectorAccess: configuration of %s has non-complete mask=%d of included 2x1' % (self.str_src, c.roiMask())
if self.counter_cspad2x2_msg == 2 : print 'Stop WARNING messages for %s configuration' % self.str_src
self.counter_cspad2x2_msg += 1
return d.data()
##-----------------------------
def raw_data_camera(self, evt, env) :
# data object
d = pda.get_camera_data_object(evt, self.source)
if d is None : return None
# configuration object
#c = pda.get_camera_config_object(env, self.source)
#if c is None : return None
#print 'data width: %d, height: %d, depth: %d, offset: %f' % (d.width(), d.height(), d.depth(), d.offset())
offset = d.offset()
d16 = d.data16()
if d16 is not None and d16 != [] :
if self.do_offset : return np.array(d16, dtype=np.int32) - d.offset()
else : return d16
d8 = d.data8()
if d8 is not None and d8 != [] :
if self.do_offset : return np.array(d8, dtype=np.int32) - d.offset()
else : return d8
return None
##-----------------------------
def raw_data_fccd960(self, evt, env) :
# data object
d = pda.get_camera_data_object(evt, self.source)
if d is None : return None
# configuration object
#c = pda.get_camera_config_object(env, self.source)
#if c is None : return None
arr = d.data16()
if arr is None : return None
arr_c = (arr>>13)&03
arr_v = arr&017777
#print 'arr_c:\n', arr_c
#print 'arr_v:\n', arr_v
return np.select([arr_c==0, arr_c==1, arr_c==3], \
[arr_v, arr_v<<2, arr_v<<3])
##-----------------------------
def raw_data_princeton(self, evt, env) :
# data object
d = pda.get_princeton_data_object(evt, self.source)
if d is None : return None
# configuration object
#c = pda.get_princeton_config_object(env, self.source)
#if c is None : return None
#print 'config: width: %d, height: %d' % (c.width(), c.height())
nda = d.data()
return nda if nda is not None else None
##-----------------------------
def raw_data_pnccd(self, evt, env) :
# data object
#print '=== in raw_data_pnccd'
#d = evt.get(_psana.PNCCD.FullFrameV1, self.source)
#d = evt.get(_psana.PNCCD.FramesV1, self.source)
d = pda.get_pnccd_data_object(evt, self.source)
if d is None : return None
#c = pda.get_pnccd_config_object(env, self.source)
#if c is None : return None
#print 'config: numLinks: %d, payloadSizePerLink: %d' % (d.numLinks(), c.payloadSizePerLink())
arr = []
nlinks = d.numLinks()
for i in range(nlinks) :
frame = d.frame(i)
fdata = frame.data()
arr.append(fdata)
#print ' data.shape: %s' % (str(fdata.shape))
nda = np.array(arr)
#print 'nda.shape: ', nda.shape
return nda
##-----------------------------
def raw_data_andor(self, evt, env) :
d = pda.get_andor_data_object(evt, self.source)
if d is None : return None
if self.pbits & 4 :
print 'Data object:', d
print 'shotIdStart = ', d.shotIdStart()
print 'readoutTime = ', d.readoutTime()
print 'temperature = ', d.temperature()
c = pda.get_andor_config_object(env, self.source)
if c is not None and self.pbits & 4 :
print 'Configuration object:', c
print 'width = ', c.width()
print 'height = ', c.height()
print 'numSensors = ', c.numSensors()
print 'orgX = ', c.orgX()
print 'orgY = ', c.orgY()
print 'binX = ', c.binX()
print 'binY = ', c.binY()
print 'exposureTime = ', c.exposureTime()
print 'coolingTemp = ', c.coolingTemp ()
print 'fanMode = ', c.fanMode ()
print 'baselineClamp = ', c.baselineClamp()
print 'highCapacity = ', c.highCapacity()
print 'gainIndex = ', c.gainIndex()
print 'readoutSpeedIndex = ', c.readoutSpeedIndex()
print 'exposureEventCode = ', c.exposureEventCode()
print 'exposureStartDelay = ', c.exposureStartDelay()
print 'numDelayShots = ', c.numDelayShots()
print 'frameSize = ', c.frameSize()
print 'numPixelsX = ', c.numPixelsX()
print 'numPixelsY = ', c.numPixelsY()
print 'numPixelsPerSensor = ', c.numPixelsPerSensor()
print 'numPixels = ', c.numPixels()
nda = d.data()
return nda if nda is not None else None
##-----------------------------
def raw_data_jungfrau(self, evt, env) :
d = pda.get_jungfrau_data_object(evt, self.source)
if d is None : return None
return d.frame()
##-----------------------------
def raw_data_epix(self, evt, env) :
# data object
d = pda.get_epix_data_object(evt, self.source)
if d is None : return None
# configuration object
#c = pda.get_epix_config_object(env, self.source)
#if c is None : return None
#print 'config: rows: %d, cols: %d, asics: %d' % (c.numberOfRows(), c.numberOfColumns(), c.numberOfAsics())
#print 'config: digitalCardId0: %d, 1: %d' % (c.digitalCardId0(), c.digitalCardId1())
#print 'config: analogCardId0 : %d, 1: %d' % (c.analogCardId0(), c.analogCardId1())
#print 'config: version: %d, asicMask: %d' % (c.version(), c.asicMask())
nda = d.frame()
return nda if nda is not None else None
##-----------------------------
def raw_data_timepix(self, evt, env) :
# data object
d = pda.get_timepix_data_object(evt, self.source)
if d is None : return None
# configuration object
#c = pda.get_timepix_config_object(env, self.source)
#if c is None : return None
#print 'config: width: %d, height: %d' % (c.width(), c.height())
nda = d.data()
return nda if nda is not None else None
##-----------------------------
def raw_data_fli(self, evt, env) :
# data object
d = pda.get_fli_data_object(evt, self.source)
if d is None : return None
# configuration object
#c = pda.get_fli_config_object(env, self.source)
#if c is None : return None
#print 'config: width: %d, height: %d' % (c.width(), c.height())
nda = d.data()
return nda if nda is not None else None
##-----------------------------
def raw_data_pimax(self, evt, env) :
# data object
d = pda.get_pimax_data_object(evt, self.source)
if d is None : return None
# configuration object
#c = pda.get_pimax_config_object(env, self.source)
#if c is None : return None
#print 'config: width: %d, height: %d' % (c.width(), c.height())
nda = d.data()
return nda if nda is not None else None
##-----------------------------
def raw_data_zyla(self, evt, env) :
# data object
d = pda.get_zyla_data_object(evt, self.source)
if d is None : return None
# configuration object
#c = pda.get_zyla_config_object(env, self.source)
#if c is None : return None
#print 'config: width: %d, height: %d' % (c.width(), c.height())
nda = d.data()
return nda if nda is not None else None
##-----------------------------
##-----------------------------
##-----------------------------
##-----------------------------
[docs] def raw_data_acqiris(self, evt, env) :
"""returns two 2-d ndarrays wf,wt with shape=(nbrChannels, nbrSamples) or None
"""
# data object
d = pda.get_acqiris_data_object(evt, self.source)
if d is None : return None
# configuration object
c = pda.get_acqiris_config_object(env, self.source)
if c is None : return None
#nchan = d.data_shape()[0]
nbrChannels = c.nbrChannels()
h = c.horiz()
sampInterval = h.sampInterval()
nbrSamples = h.nbrSamples()
if self.pbits & 4 : print ' nbrChannels: %d, H-nbrSamples: %d, H-sampInterval: %g' \
% (nbrChannels, nbrSamples, sampInterval)
shape = (nbrChannels, nbrSamples)
wf = np.zeros(shape, dtype=np.float)
wt = np.zeros(shape, dtype=np.float)
for chan in range(nbrChannels) :
elem = d.data(chan)
vert = c.vert()[chan]
slope = vert.slope()
offset= vert.offset()
nbrSegments = elem.nbrSegments()
nbrSamplesInSeg = elem.nbrSamplesInSeg()
indexFirstPoint = elem.indexFirstPoint()
tstamps = elem.timestamp()
wforms = elem.waveforms()
if self.pbits & 4 :
print ' chan: %d, nbrSegments: %d, nbrSamplesInSeg: %d, indexFirstPoint: %d,' \
% (chan, nbrSegments, nbrSamplesInSeg, indexFirstPoint), \
' V-slope: %f, V-offset: %f, H-pos[seg=0]: %g' % (slope, offset, tstamps[0].pos())
for seg in range(nbrSegments) :
raw = wforms[seg]
pos = tstamps[seg].pos()
i0_seg = seg * nbrSamplesInSeg + int(pos/sampInterval) if self.correct_time else seg * nbrSamplesInSeg
size = nbrSamplesInSeg if (i0_seg + nbrSamplesInSeg) <= nbrSamples else nbrSamples - i0_seg
if self.correct_time :
if (indexFirstPoint + size) > nbrSamplesInSeg : size = nbrSamplesInSeg - indexFirstPoint
wf[chan, i0_seg:i0_seg+size] = raw[indexFirstPoint:indexFirstPoint+size]*slope - offset
else :
wf[chan, i0_seg:i0_seg+size] = raw[0:size]*slope - offset
wt[chan, i0_seg:i0_seg+size] = np.arange(size)*sampInterval + pos
return wf, wt
##-----------------------------
[docs] def raw_data_imp(self, evt, env) :
"""returns ndarray with shape=(4, 1023) or None
"""
# data object
d = pda.get_imp_data_object(evt, self.source)
if d is None : return None
if self.pbits & 4 :
# configuration object
c = pda.get_imp_config_object(env, self.source)
if c is None : return None
print "Configuration object for %s" % self.source
print " range =", c.range()
print " calRange =", c.calRange()
print " reset =", c.reset()
print " biasData =", c.biasData()
print " calData =", c.calData()
print " biasDacData =", c.biasDacData()
print " calStrobe =", c.calStrobe()
print " numberOfSamples =", c.numberOfSamples()
print " trigDelay =", c.trigDelay()
print " adcDelay =", c.adcDelay()
print "Data object for %s" % self.source
print " vc =", d.vc()
print " lane =", d.lane()
print " frameNumber =", d.frameNumber()
print " range =", d.range()
laneStatus = d.laneStatus()
print " laneStatus.linkErrCount =", laneStatus.linkErrCount()
print " laneStatus.linkDownCount =", laneStatus.linkDownCount()
print " laneStatus.cellErrCount =", laneStatus.cellErrCount()
print " laneStatus.rxCount =", laneStatus.rxCount()
print " laneStatus.locLinked =", laneStatus.locLinked()
print " laneStatus.remLinked =", laneStatus.remLinked()
print " laneStatus.zeros =", laneStatus.zeros()
print " laneStatus.powersOkay =", laneStatus.powersOkay()
lst_of_samps = d.samples()
a = np.array([sample.channels() for sample in lst_of_samps])
# Transpose converts (1023, 4) to (4, 1023)
a = np.transpose(a)
if self.do_calib_imp :
c = pda.get_imp_config_object(env, self.source)
if c is None : return None
bias = c.biasData()
return np.array(a, dtype=np.int32) - bias
return a
##-----------------------------
[docs] def cspad_gain_mask(self, gain=None) :
""" Returns the gain mask of 1/0 for low/high gain pixels as a numpy array of shape=(32,185,388), dtype=uint8.
If gain is set, method returns a map of (float) gain/1 values for low/high gain pixels, respectively.
None is returned if configuration data is missing.
"""
# configuration from data
c = pda.get_cspad_config_object(self.env, self.source)
if c is None :
msg = '%s.cspad_gain_mask - config object is not available' % self.__class__.__name__
#raise IOError(msg)
print msg
return None
#self.gm = np.empty((32,185,388), dtype=np.uint8)
self.gm = np.zeros((32,185,388), dtype=np.uint8)
asic1 = np.ones((185,194), dtype=np.uint8)
for iquad in range(c.quads_shape()[0]):
# need in copy to right shift bits
gm = np.array(c.quads(iquad).gm().gainMap())
for i2x1 in range(8):
gmasic0 = gm & 1 # take the lowest bit only
gm = np.right_shift(gm, asic1)
gm2x1 = np.hstack((gmasic0, gm & 1))
self.gm[i2x1+iquad*8][:][:] = np.logical_not(gm2x1)
if i2x1 < 7 : gm = np.right_shift(gm, asic1) # do not shift for last asic
self.cfg_gain_mask_is_loaded = True
if gain is None :
return self.gm
else :
f=float(gain-1.)
return np.array(self.gm,dtype=np.float) * f + 1
##-----------------------------
[docs] def cspad2x2_gain_mask(self, gain=None) :
""" Returns the gain mask of 1/0 for low/high gain pixels as a numpy array of shape=(2,185,388), dtype=uint8.
If gain is set, method returns a map of (float) gain/1 values for low/high gain pixels, respectively.
None is returned if configuration data is missing.
"""
# configuration from data
c = pda.get_cspad2x2_config_object(self.env, self.source)
if c is None :
msg = '%s.cspad_gain_mask - config object is not available' % self.__class__.__name__
#raise IOError(msg)
if self.pbits : print msg
return None
#self.gm = np.empty((2,185,388), dtype=np.uint8)
self.gm = np.zeros((2,185,388), dtype=np.uint8)
asic1 = np.ones((185,194), dtype=np.uint8)
gm = np.array(c.quad().gm().gainMap())
# see the DAQ pdsapp/config/Cspad2x2GainMap.cc:export_() to see that
# the 4 bits in each element of the gainmap array correspond to the
# 4 ASICs in the 2x2. playing around with the DAQ configdb_gui
# (export/import the text file) shows that the ASICS are numbered
# like this:
# 1 3
# 0 2
# I am assuming the above corresponds to the 2x2 "natural shape"
# of [2,185,388] in a natural way.
for i2x1 in range(2):
gmasic0 = gm & 1 # take the lowest bit only
gm = np.right_shift(gm, asic1)
gm2x1 = np.hstack((gmasic0, gm & 1))
self.gm[i2x1][:][:] = np.logical_not(gm2x1)
if i2x1 < 1 : gm = np.right_shift(gm, asic1) # do not shift for last asic
self.cfg_gain_mask_is_loaded = True
if gain is None :
return self.gm
else :
f=float(gain-1.)
return np.array(self.gm,dtype=np.float) * f + 1
##-----------------------------
def raw_data_cspad_v0(self, evt, env) :
# data object
d = pda.get_cspad_data_object(evt, self.source)
if d is None :
if self.pbits & 1 : print 'cspad data object is not found'
return None
# configuration from data
c = pda.get_cspad_config_object(env, self.source)
if c is None :
if self.pbits & 1 : print 'cspad config object is not found'
return None
nquads = d.quads_shape()[0]
nquads_c = c.numQuads()
#print 'd.TypeId: ', d.TypeId
if self.pbits & 8 : print 'nquads in data: %d and config: %d' % (nquads, nquads_c)
arr = []
for iq in range(nquads) :
q = d.quads(iq)
qnum = q.quad()
qdata = q.data()
#n2x1stored = qdata.shape[0]
roim = c.roiMask(qnum)
if self.pbits & 8 : print 'qnum: %d qdata.shape: %s, mask: %d' % (qnum, str(qdata.shape), roim)
# ' n2x1stored: %d' % (n2x1stored)
#roim = 0375 # for test only
if roim == 0377 : arr.append(qdata)
else :
if self.pbits : print 'PyDetectorAccessr: quad configuration has non-complete mask = %d of included 2x1' % roim
qdata_full = np.zeros((8,185,388), dtype=qdata.dtype)
i = 0
for s in range(8) :
if roim & (1<<s) :
qdata_full[s,:] = qdata[i,:]
i += 1
arr.append(qdata_full)
nda = np.array(arr)
if self.pbits & 8 : print 'nda.shape: ', nda.shape
nda.shape = (32,185,388)
return nda
##-----------------------------
##-----------------------------
def shape_config_cspad(self, env) :
# configuration from data file
# config object for cspad contains a number of used 2x1-s numSect()
c = pda.get_cspad_config_object(env, self.source)
if c is None : return None
#c.numQuads()
#return (c.numSect(), 185, 388)
return (32, 185, 388)
##-----------------------------
def shape_config_cspad2x2(self, env) :
c = pda.get_cspad2x2_config_object(env, self.source)
#c.numAsicsStored(), o.payloadSize()
return (185, 388, 2) # no other choice
##-----------------------------
def shape_config_epix100(self, env) :
c = pda.get_epix_config_object(env, self.source)
if c is None : return None
return (c.numberOfRows(), c.numberOfColumns())
#return (704, 768) # no other choice
##-----------------------------
def shape_config_pnccd(self, env) :
c = pda.get_pnccd_config_object(env, self.source)
if c is None : return None
return (c.numSubmodules(), c.numSubmoduleRows(), c.numSubmoduleChannels())
#c.numRows(), c.numChannels(), c.numLinks()
#return (4, 512, 512) # no other choice
##-----------------------------
def shape_config_princeton(self, env) :
c = pda.get_princeton_config_object(env, self.source)
if c is None : return None
return (c.numPixelsY(), c.numPixelsX())
#return (c.height()/c.binY(), c.width()/c.binX())
#return (1300, 1340)
##-----------------------------
def shape_config_rayonix(self, env) :
# configuration from data file
# config object for rayonix has a number of pixel in the bin for both dimansions
# maximal detector size is 3840x3840, pixel size is 44.5um
c = pda.get_rayonix_config_object(env, self.source)
if c is None : return None
npix_in_colbin = c.binning_f()
npix_in_rowbin = c.binning_s()
if npix_in_rowbin>0 and npix_in_colbin>0 : return (3840/npix_in_rowbin, 3840/npix_in_colbin)
return None
##-----------------------------
def shape_config_andor(self, env) :
# configuration from data file
c = pda.get_andor_config_object(env, self.source)
if c is None : return None
nsegs = None
try : nsegs = c.numSensors()
except : pass
#npixx = c.numPixelsY() # for Andor3D only
#npixy = c.numPixelsX() # for Andor3D only
npixx = c.width() / c.binX()
npixy = c.height() / c.binY()
if npixx and npixy :
return (npixy, npixx) if nsegs is None else (nsegs, npixy, npixx)
return None
##-----------------------------
[docs] def shape_config_jungfrau(self, env) :
"""Shape of jungfrau data array. Returns tuple like (nsegs,512,1024)
"""
c = pda.get_jungfrau_config_object(env, self.source)
if c is None : return None
nsegs = c.numberOfModules()
nrows = c.numberOfRowsPerModule()
ncols = c.numberOfColumnsPerModule()
return (nsegs, nrows, ncols)
##-----------------------------
def shape_config_timepix(self, env) :
# configuration from data file
#c = pda.get_timepix_config_object(env, self.source)
# config object does not have shape parameters,
# (o.height(), o.width()) are available in data object
return (512, 512)
def shape_config_fli(self, env) :
# configuration from data file
c = pda.get_fli_config_object(env, self.source)
if c is None : return None
return (c.numPixelsY(), c.numPixelsX())
#return (c.height()/c.binY(), c.width()/c.binX()) # (4096, 4096)
def shape_config_pimax(self, env) :
# configuration from data file
c = pda.get_pimax_config_object(env, self.source)
if c is None : return None
return (c.numPixelsY(), c.numPixelsX())
#return (c.height()/c.binY(), c.width()/c.binX()) # (1024, 1024)
def shape_config_zyla(self, env) :
# configuration from data file
c = pda.get_zyla_config_object(env, self.source)
if c is None : return None
return (c.numPixelsY(), c.numPixelsX())
#return (c.height()/c.binY(), c.width()/c.binX()) # (1024, 1024)
#def shape_config_imp(self, env) :
# #Waveform detector
# # configuration from data file
# #c = pda.get_imp_config_object(env, self.source)
# # config object does not have shape parameters,
# return (4, 1023) # ???
##-----------------------------
def shape_config_camera(self, env) :
# configuration from data file
c = None
if self.dettype in (gu.OPAL1000, gu.OPAL2000, gu.OPAL4000, gu.OPAL8000) :
c = pda.get_opal1k_config_object(env, self.source)
elif self.dettype == gu.FCCD :
c = pda.get_fccd_config_object(env, self.source)
#return (c.height(), c.width())
elif self.dettype == gu.FCCD960 :
c = pda.get_fccd_config_object(env, self.source)
#return (c.height(), c.width())
elif self.dettype == gu.ORCAFL40 :
c = pda.get_orca_config_object(env, self.source)
#return (c.height(), c.width())
elif self.dettype == gu.TM6740 :
c = pda.get_tm6740_config_object(env, self.source)
elif self.dettype == gu.QUARTZ4A150 :
c = pda.get_quartz_config_object(env, self.source)
elif self.dettype == gu.EPICSCAM :
c = pda.get_epicscam_config_object(env, self.source)
return (c.height(), c.width())
#print c
if c is None : return None
return (c.Row_Pixels, c.Column_Pixels)
##-----------------------------
def shape_data_camera(self, evt) :
# camera shape from data object
o = pda.get_camera_data_object(evt, self.source)
if o is None : return None
return (o.width(),o.height())
##-----------------------------
def shape_config(self, env) :
#print 'TypeId.Type.Id_CspadElement: ', TypeId.Type.Id_CspadElement
#print 'TypeId.Type.Id_CspadConfig: ', TypeId.Type.Id_CspadConfig
if self.dettype == gu.CSPAD : return self.shape_config_cspad(env)
elif self.dettype == gu.CSPAD2X2 : return self.shape_config_cspad2x2(env)
elif self.dettype == gu.EPIX100A : return self.shape_config_epix100(env)
elif self.dettype == gu.PRINCETON : return self.shape_config_princeton(env)
elif self.dettype == gu.PNCCD : return self.shape_config_pnccd(env)
elif self.dettype == gu.ANDOR : return self.shape_config_andor(env)
elif self.dettype == gu.ANDOR3D : return self.shape_config_andor(env)
elif self.dettype == gu.JUNGFRAU : return self.shape_config_jungfrau(env)
elif self.dettype == gu.RAYONIX : return self.shape_config_rayonix(env)
elif self.dettype in (gu.OPAL1000, gu.OPAL2000, gu.OPAL4000, gu.OPAL8000,
gu.FCCD, gu.FCCD960, gu.ORCAFL40, gu.TM6740, gu.QUARTZ4A150, gu.EPICSCAM) \
: return self.shape_config_camera(env)
elif self.dettype == gu.TIMEPIX : return self.shape_config_timepix(env)
elif self.dettype == gu.FLI : return self.shape_config_fli(env)
elif self.dettype == gu.PIMAX : return self.shape_config_pimax(env)
elif self.dettype == gu.ZYLA : return self.shape_config_zyla(env)
# waveform detectors:
#elif self.dettype == gu.ACQIRIS : return self.shape_config_acqiris(env)
#elif self.dettype == gu.IMP : return self.shape_config_imp(env)
else : return None
##-----------------------------
[docs] def shape(self, par=0) :
"""Returns the detector shape.
Shape is retrieved from configuration object and
if it is None, then from calibration file.
"""
shc = self.shape_config(self.env)
return shc if shc is not None else self.shape_calib(par, ctype=gu.PEDESTALS)
##-----------------------------
# Static methods
##-----------------------------
def save_txtnda(self, fname='nda.txt', ndarr=None, cmts=(), fmt='%.1f', verbos=False, addmetad=True) :
save_txt(fname, ndarr, cmts, fmt, verbos, addmetad)
##-----------------------------
def load_txtnda(self, fname) :
nda = load_txt(fname)
if len(nda.shape)>2 : return nda
shape = self.shape()
if shape is not None : nda.shape = shape
return nda
##-----------------------------
from time import time
if __name__ == "__main__" :
ds, src = _psana.DataSource('exp=cxif5315:run=169'), _psana.Source('DetInfo(CxiDs2.0:Cspad.0)')
#ds, src = _psana.DataSource('exp=xcsi0112:run=15'), _psana.Source('DetInfo(XcsBeamline.0:Princeton.0)')
env = ds.env()
cls = env.calibStore()
evts = ds.events()
evt = evts.next()
for key in evt.keys() : print key
det = PyDetectorAccess(src, env, pbits=255)
nda = det.pedestals(evt)
print '\npedestals nda:', nda
if nda is not None : print 'nda.dtype: %s nda.shape: %s' % (nda.dtype, nda.shape)
t0_sec = time()
nda = det.raw_data(evt, env)
print '\nPython consumed time to get raw data (sec) =', time()-t0_sec
print '\nraw_data nda:\n', nda
sys.exit ('Self test is done')
##-----------------------------