|
import DECameraClientLib
|
|
import numpy
|
|
import time
|
|
import pyami.imagefun
|
|
import ccdcamera
|
|
import threading
|
|
|
|
# Shared global connection to the DE Server
|
|
__deserver = None
|
|
__active_camera = None
|
|
__deserver_lock = threading.RLock()
|
|
|
|
## decorator to put a thread lock around a function
|
|
def locked(fun):
|
|
def newfun(*args, **kwargs):
|
|
__deserver_lock.acquire()
|
|
try:
|
|
return fun(*args, **kwargs)
|
|
finally:
|
|
__deserver_lock.release()
|
|
return newfun
|
|
|
|
##### Begin thread safe functions to operate on DE Server #####
|
|
|
|
@locked
|
|
def de_connect():
|
|
global __deserver
|
|
if __deserver:
|
|
return
|
|
__deserver = DECameraClientLib.DECameraClientLib()
|
|
__deserver.connect()
|
|
|
|
@locked
|
|
def de_disconnect():
|
|
global __deserver
|
|
if __deserver.connected:
|
|
__deserver.disconnect()
|
|
|
|
@locked
|
|
def de_setActiveCamera(de_name):
|
|
global __deserver
|
|
global __active_camera
|
|
if __active_camera != de_name:
|
|
__deserver.setActiveCamera(de_name)
|
|
__active_camera = de_name
|
|
|
|
@locked
|
|
def de_print_props(de_name):
|
|
global __deserver
|
|
de_setActiveCamera(de_name)
|
|
camera_properties = __deserver.getActiveCameraProperties()
|
|
for one_property in camera_properties:
|
|
print one_property, __deserver.getProperty(one_property)
|
|
|
|
@locked
|
|
def de_getProperty(de_name, name):
|
|
global __deserver
|
|
de_setActiveCamera(de_name)
|
|
value = __deserver.getProperty(name)
|
|
return value
|
|
|
|
@locked
|
|
def de_setProperty(de_name, name, value):
|
|
global __deserver
|
|
de_setActiveCamera(de_name)
|
|
value = __deserver.setProperty(name, value)
|
|
return value
|
|
|
|
@locked
|
|
def de_getDictProp(de_name, name):
|
|
global __deserver
|
|
de_setActiveCamera(de_name)
|
|
x = int(__deserver.getProperty(name + ' X'))
|
|
y = int(__deserver.getProperty(name + ' Y'))
|
|
return {'x': x, 'y': y}
|
|
|
|
@locked
|
|
def de_setDictProp(de_name, name, xydict):
|
|
global __deserver
|
|
de_setActiveCamera(de_name)
|
|
__deserver.setProperty(name + ' X', int(xydict['x']))
|
|
__deserver.setProperty(name + ' Y', int(xydict['y']))
|
|
|
|
@locked
|
|
def de_getImage(de_name):
|
|
global __deserver
|
|
de_setActiveCamera(de_name)
|
|
image = __deserver.GetImage()
|
|
return image
|
|
|
|
##### End thread safe functions to operate on DE Server #####
|
|
|
|
|
|
class DECameraBase(ccdcamera.CCDCamera):
|
|
'''
|
|
All DE camera classes should inherit this to allow
|
|
for a shared connection to the DE server.
|
|
Subclasses should define an attribute "de_name"
|
|
to inform this base class how to set the active camera.
|
|
'''
|
|
logged_methods_on = False
|
|
def __init__(self):
|
|
ccdcamera.CCDCamera.__init__(self)
|
|
|
|
de_connect()
|
|
|
|
## instance specific
|
|
self.offset = {'x': 0, 'y': 0}
|
|
self.binning = {'x': 1, 'y': 1}
|
|
self.dimension = self._getCameraSize()
|
|
|
|
#update a few essential camera properties to default values
|
|
self.setProperty('Correction Mode', 'Uncorrected Raw')
|
|
|
|
def getProperty(self, name):
|
|
value = de_getProperty(self.name, name)
|
|
return value
|
|
|
|
def setProperty(self, name, value):
|
|
value = de_setProperty(self.name, name, value)
|
|
return value
|
|
|
|
def getDictProp(self, name):
|
|
return de_getDictProp(self.name, name)
|
|
|
|
def setDictProp(self, name, xydict):
|
|
return de_setDictProp(self.name, name, xydict)
|
|
|
|
def _getImage(self):
|
|
old_frames_name = self.getPreviousRawFramesName()
|
|
t0 = time.time()
|
|
image = de_getImage(self.name)
|
|
t1 = time.time()
|
|
self.exposure_timestamp = (t1 + t0) / 2.0
|
|
if not isinstance(image, numpy.ndarray):
|
|
raise ValueError('GetImage did not return array')
|
|
image = self.finalizeGeometry(image)
|
|
## wait for frames name to be updated before returning
|
|
if self.getSaveRawFrames():
|
|
new_frames_name = self.getPreviousRawFramesName()
|
|
while not new_frames_name or (new_frames_name == old_frames_name):
|
|
time.sleep(1.0)
|
|
new_frames_name = self.getPreviousRawFramesName()
|
|
return image
|
|
|
|
def _getCameraSize(self):
|
|
return self.getDictProp('Sensor Size')
|
|
|
|
def getExposureTime(self):
|
|
seconds = self.getProperty('Exposure Time (seconds)')
|
|
ms = int(seconds * 1000.0)
|
|
return ms
|
|
|
|
def setExposureTime(self, ms):
|
|
seconds = ms / 1000.0
|
|
self.setProperty('Exposure Time (seconds)', seconds)
|
|
|
|
def getDimension(self):
|
|
return self.dimension
|
|
|
|
def setDimension(self, dimdict):
|
|
self.dimension = dimdict
|
|
|
|
def getBinning(self):
|
|
return self.binning
|
|
|
|
def setBinning(self, bindict):
|
|
self.binning = bindict
|
|
|
|
def getOffset(self):
|
|
return self.offset
|
|
|
|
def setOffset(self, offdict):
|
|
self.offset = offdict
|
|
|
|
def finalizeGeometry(self, image):
|
|
row_start = self.offset['y'] * self.binning['y']
|
|
col_start = self.offset['x'] * self.binning['x']
|
|
nobin_rows = self.dimension['y'] * self.binning['y']
|
|
nobin_cols = self.dimension['x'] * self.binning['x']
|
|
row_end = row_start + nobin_rows
|
|
col_end = col_start + nobin_cols
|
|
nobin_image = image[row_start:row_end, col_start:col_end]
|
|
assert self.binning['x'] == self.binning['y']
|
|
binning = self.binning['x']
|
|
## NOTE: non-standard binning: mean, not sum
|
|
## See method getBinnedMultiplier below.
|
|
bin_image = pyami.imagefun.bin(nobin_image, binning)
|
|
return bin_image
|
|
|
|
def getBinnedMultiplier(self):
|
|
'''
|
|
Our software binning calculates a binned pixel
|
|
as the average of the component pixels, which is
|
|
different that typical hardware binning. This will
|
|
return a multiplier that can be used to calculate
|
|
a "standard" binned pixel.
|
|
'''
|
|
return self.binning['x'] * self.binning['y']
|
|
|
|
def getPixelSize(self):
|
|
psize = 6e-6
|
|
return {'x': psize, 'y': psize}
|
|
|
|
def getRetractable(self):
|
|
return True
|
|
|
|
def setInserted(self, value):
|
|
# return if already at this insertion state
|
|
if not (value ^ self.getInserted()):
|
|
return
|
|
if value:
|
|
de12value = 'Extended'
|
|
sleeptime = 20
|
|
else:
|
|
de12value = 'Retracted'
|
|
sleeptime = 8
|
|
self.setProperty("Camera Position", de12value)
|
|
time.sleep(sleeptime)
|
|
|
|
def getInserted(self):
|
|
de12value = self.getProperty('Camera Position Status')
|
|
return de12value == 'Extended'
|
|
|
|
def getExposureTypes(self):
|
|
return ['normal','dark']
|
|
|
|
def getExposureType(self):
|
|
exposure_type = self.getProperty('Exposure Mode')
|
|
return exposure_type.lower()
|
|
|
|
def setExposureType(self, value):
|
|
self.setProperty('Exposure Mode', value.capitalize())
|
|
|
|
def getNumberOfFrames(self):
|
|
return self.getProperty('Total Number of Frames')
|
|
|
|
def getSaveRawFrames(self):
|
|
'''Save or Discard'''
|
|
value = self.getProperty('Autosave Raw Frames')
|
|
if value == 'Save':
|
|
return True
|
|
elif value == 'Discard':
|
|
return False
|
|
else:
|
|
raise ValueError('unexpected value from Autosave Raw Frames: %s' % (value,))
|
|
|
|
def setSaveRawFrames(self, value):
|
|
'''True: save frames, False: discard frames'''
|
|
if value:
|
|
value_string = 'Save'
|
|
else:
|
|
value_string = 'Discard'
|
|
self.setProperty('Autosave Raw Frames', value_string)
|
|
|
|
def getPreviousRawFramesName(self):
|
|
frames_name = self.getProperty('Autosave Frames - Previous Dataset Name')
|
|
return frames_name
|
|
|
|
def getNumberOfFramesSaved(self):
|
|
nframes = self.getProperty('Autosave Raw Frames - Frames Written in Last Exposure')
|
|
return int(nframes)
|
|
|
|
def getUseFrames(self):
|
|
nsum = self.getProperty('Autosave Sum Frames - Sum Count')
|
|
first = self.getProperty('Autosave Sum Frames - Ignored Frames')
|
|
last = first + nsum
|
|
ntotal = self.getNumberOfFrames()
|
|
if last > ntotal:
|
|
last = ntotal
|
|
sumframes = range(first,last)
|
|
return tuple(sumframes)
|
|
|
|
def setUseFrames(self, frames):
|
|
total_frames = self.getNumberOfFrames()
|
|
if frames:
|
|
nskip = frames[0]
|
|
last = frames[-1]
|
|
else:
|
|
nskip = 0
|
|
last = total_frames - 1
|
|
nsum = last - nskip + 1
|
|
if nsum > total_frames:
|
|
nsum = total_frames
|
|
nsum = int(nsum)
|
|
self.setProperty('Autosave Sum Frames - Sum Count', nsum)
|
|
self.setProperty('Autosave Sum Frames - Ignored Frames', nskip)
|
|
|
|
def getFrameTime(self):
|
|
fps = self.getProperty('Frames Per Second')
|
|
ms = (1.0 / fps) * 1000.0
|
|
return ms
|
|
|
|
def setFrameTime(self, ms):
|
|
seconds = ms / 1000.0
|
|
fps = 1.0 / seconds
|
|
self.setProperty('Frames Per Second', fps)
|
|
|
|
def getReadoutDelay(self):
|
|
return self.getProperty('Sensor Readout Delay (milliseconds)')
|
|
|
|
def setReadoutDelay(self, milliseconds):
|
|
self.setProperty('Sensor Readout Delay (milliseconds)', milliseconds)
|
|
|
|
def getTemperatureStatus(self):
|
|
return self.getProperty('Temperature Control')
|
|
|
|
## method name altered to prevent Leginon from setting temperature
|
|
def set_TemperatureStatus(self, state):
|
|
return self.setProperty('Temperature Control', state)
|
|
|
|
def getTemperature(self):
|
|
return self.getProperty('Temperature - Detector (Celsius)')
|
|
|
|
def set_Temperature(self, degrees):
|
|
return self.setProperty('Temperature Control - Setpoint (Celsius)', degrees)
|
|
|
|
#### Classes for specific cameras
|
|
|
|
class DE12Survey(DECameraBase):
|
|
name = 'DE12 Survey'
|
|
def __init__(self):
|
|
DECameraBase.__init__(self)
|
|
self.dimension = {'x': 1024, 'y': 1024}
|
|
|
|
class DE20Survey(DECameraBase):
|
|
name = 'DE20 Survey'
|
|
def __init__(self):
|
|
DECameraBase.__init__(self)
|
|
self.dimension = {'x': 2048, 'y': 2048}
|
|
|
|
|
|
class DD(DECameraBase):
|
|
name = 'DD'
|
|
def __init__(self):
|
|
'''
|
|
DD camera
|
|
'''
|
|
# Keep the proxy object to be used later
|
|
# to avoid "not an instance of the module problem
|
|
# after reload.
|
|
self.as_super = super(DD,self)
|
|
|
|
self.as_super.__init__()
|
|
self.setProperty('Ignore Number of Frames', 0)
|
|
frame_time_ms = self.getFrameTime()
|
|
self.setProperty('Preexposure Time (seconds)', frame_time_ms/1000.0)
|
|
|
|
def finalizeGeometry(self, image):
|
|
image = self.as_super.finalizeGeometry(image)
|
|
image = numpy.fliplr(image)
|
|
return image
|
|
|
|
class DE12(DD):
|
|
name = 'DE12'
|
|
|
|
class DE20(DD):
|
|
name = 'DE20'
|
|
def getPixelSize(self):
|
|
psize = 6.4e-6
|
|
return {'x': psize, 'y': psize}
|
|
|