Ejemplo n.º 1
0
    def get_values(self, from_index, *counters):
        roi_counter_size = len(RoiStat)
        raw_data = self.controller._proxy.readCounters(from_index)
        if not raw_data.size:
            return len(counters)*(numpy.array(()),)
        raw_data.shape = (raw_data.size) / roi_counter_size, roi_counter_size
        result = OrderedDict([int(counter), []] for counter in counters)

        for roi_counter in raw_data:
            roi_id = int(roi_counter[0])
            for stat in range(roi_counter_size):
                full_id = RoiStatCounter.roi_stat_id(roi_id, stat)
                counter_data = result.get(full_id)
                if counter_data is not None:
                    counter_data.append(roi_counter[stat])
        return map(numpy.array, result.values())
Ejemplo n.º 2
0
    def get_values(self, from_index, *counters):
        roi_counter_size = len(RoiStat)
        raw_data = self.controller._proxy.readCounters(from_index)
        if not raw_data.size:
            return len(counters) * (numpy.array(()), )
        raw_data.shape = (raw_data.size) / roi_counter_size, roi_counter_size
        result = OrderedDict([int(counter), []] for counter in counters)

        for roi_counter in raw_data:
            roi_id = int(roi_counter[0])
            for stat in range(roi_counter_size):
                full_id = RoiStatCounter.roi_stat_id(roi_id, stat)
                counter_data = result.get(full_id)
                if counter_data is not None:
                    counter_data.append(roi_counter[stat])
        return map(numpy.array, result.values())
Ejemplo n.º 3
0
class NanoBpm(object):
    # Errors codes
    NO_ERROR,CODE1,CODE2,CODE3,CODE4,CODE5,CODE6,CODE7,CODE8 = range(9)
    BPP8, BPP16, BPP32 = range(3)

    SETTINGS     = _config_property('Settings',"Configuration settings")
    GAIN         = _config_property("Gain","Set device gain")
    OFFSET       = _config_property('Offset',"Set device offset")
    LINEINTTIME  = _config_property('LineIntTime',"Line time")
    YEND         = _config_property('YEnd',"ROI Y end")
    FRAMEINTTIME = _config_property('FrameIntTime',"Frame time")
    YSTART       = _config_property('YStart',"ROI Y start")
    XSTART       = _config_property('XStart',"ROI X start")
    XEND         = _config_property('XEnd',"ROI X end")
    ADCPHASE     = _config_property('AdcPhase',"adc phase")
    SUBTRACTDARK = _config_property('SubtractDarkImage',"Subtract the stored dark image")

    MAXDELTACHISQ = _fit_property('MaxDeltaChiSq',"Maximum Delta CHI squared")
    THRESHOLD     = _fit_property('Threshold',"Threshold")
    MAXITER       = _fit_property('MaxIter',"Maximum nos. of iterations")
    FILTERSPAN    = _fit_property('FilterSpan',"Filter span")
    FILTERCTRL    = _fit_property('FilterCtrl',"Filter enable/disable & running average/median")

    V_MAXWIDTH   = _v_result_property('MaxWidth',"Maximum width allowed in um")
    V_MINWIDTH   = _v_result_property('MinWidth',"Minimum width allowed in um")
    V_MINRSQ     = _v_result_property('MinRSQ',"Minimum RSQ allowed")
    V_MINAMP     = _v_result_property('MinAmp',"Minimum intensity allowed")
    V_CALIBCOEFF = _v_result_property('CalibCoeff',"Calibration coefficient")
    V_CALIBOFF   = _v_result_property('CalibOffset',"Calibration offset")

    H_MAXWIDTH   = _h_result_property('MaxWidth',"Maximum width allowed in um")
    H_MINWIDTH   = _h_result_property('MinWidth',"Minimum width allowed in um")
    H_MINRSQ     = _h_result_property('MinRSQ',"Minimum RSQ allowed")
    H_MINAMP     = _h_result_property('MinAmp',"Minimum intensity allowed")
    H_CALIBCOEFF = _h_result_property('CalibCoeff',"Calibration coefficient")
    H_CALIBOFF   = _h_result_property('CalibOffset',"Calibration offset")

    class DataSelector():
        # DataSelector bit fields
        XCOG = 1
        YCOG = 2
        XPROFILE = 4
        YPROFILE = 8
        HISTOGRAM = 16
        QUADSUM = 32
        XPROFILE_FIT = 64
        YPROFILE_FIT = 128
        #only for stream data
        IMAGE = 256

    class Control():
        # Control word bit fields
        COLLECT_SUM = 1
        STORE_DARK = 2
        PROGRESS_UPDATE = 4

    callbacks = []

    def __init__(self, name, config):
        """ FireFlash hardware controller.

        name -- the controller's name
        config -- controller configuration,
        in this dictionary we need to have:
        command_url -- url of the command port
        control_url -- url of the control port
        """
        # Status bits for getStatus command
        self.StatusBits = {'IDLE':0, 'BUSY':1, 'REMOTE':2, 'STREAMING':4}
        # Status bits for Remote Data Toggle
        self.ToggleBits = {'OFF':0,'XFIT':1,'YFIT':2,'COG':4}

        try:
            self.command_socket = get_comm(self.config['command'], ctype=TCP)
        except KeyError:
            command_url = config["command_url"]
            warn("'command_url' keyword is deprecated." \
                 " Use 'command: tcp' instead", DeprecationWarning)
            comm_cfg = {'tcp': {'url': command_url } }
            self.command_socket = get_comm(comm_cfg)

        try:
            self.control_socket = get_comm(self.config['control'], ctype=TCP)
        except KeyError:
            control_url = config["control_url"]
            warn("'control_url' keyword is deprecated." \
                 " Use 'control: tcp' instead", DeprecationWarning)
            comm_cfg = {'tcp': {'url': control_url } }
            self.control_socket = get_comm(comm_cfg)

        # Commands ready packed in network byte order
        self.commandReset         = struct.pack(">H",0xAA00)
        self.commandInterrupt     = struct.pack(">H",0xAA01)
        self.commandStatus        = struct.pack(">H",0xAA02)
        self.commandDataToggle    = struct.pack(">H",0xAA03)
        self.commandGetDeviceInfo = struct.pack(">H",0xAA20)
        self.commandSetConfig     = struct.pack(">H",0xAA22)
        self.commandGetConfig     = struct.pack(">H",0xAA21)
        self.commandGetIntTime    = struct.pack(">H",0xAA23)
        self.commandSetIntTime    = struct.pack(">H",0xAA24)
        self.commandSetParams     = struct.pack(">H",0xAA26)
        self.commandGetParams     = struct.pack(">H",0xAA27)
        self.commandReadImage16   = struct.pack(">H",0xAA30)
        self.commandReadImage8    = struct.pack(">H",0xAA31)
        self.commandReadDark16    = struct.pack(">H",0xAA32)
        self.commandAve16Sum32    = struct.pack(">H",0xAA33)
        self.commandContinuous    = struct.pack(">H",0xAA37)
        self.commandStreamData    = struct.pack(">H",0xAA3A)

        self._errorCode2string = {
            self.NO_ERROR : "No Error",
            self.CODE1 : "Error parsing .ini file",
            self.CODE2 : "Could not establish network connection",
            self.CODE3 : "Network data transfer failed",
            self.CODE4 : "Incorrect FPGA type",
            self.CODE5 : "Invalid argument or config param error",
            self.CODE6 : "I^C-bus communication error",
            self.CODE7 : "Memory initialization error",
            self.CODE8 : "I^C-bus initialization error"
        }
        # Device Info structure keys
        self._deviceInfoKeys = ['ProcessorType', 'ProcessorVersion', 'FPGAType', 'FPGAVersion', 'BoardType', 'BoardVersion',
            'BuidYear', 'BuildMonth', 'Buildday', 'BuildHour', 'BuildMinute', 'BuildSecond', 'SWMajor', 'SWMinor', 'SWbuild',
            'FirmWare Major', 'FirmWareMinor', 'FirmWareBuild'', BoardID']
        # Device configuration parameter keys
        self._deviceConfigKeys = ['Settings', 'Gain', 'Offset', 'LineIntTime', 'YEnd',
             'FrameIntTime', 'YStart', 'XStart', 'XEnd', 'AdcPhase', 'SubtractDarkImage']
        #Image Descriptor keys
        self._imageDescriptorKeys = ['FrameNb', 'IntegrationTime', 'XSize', 'YSize', 'InternalPtr']
        self._quadConfigKeys = ['XCentre', 'YCentre', 'WinStartX', 'WinEndX', 'WinStartY', 'WinEndY']
        self._sensorConfigKeys = ['YSize', 'DarkImageSubtract']
        #device parameter keys
        self._deviceParameterKeys = ['configurationKeys', 'DAC0Keys', 'DAC1Keys', 'DAC2keys', 'DAC3Keys', 'fitParameterKeys',
                                    'verticalFitResultCriteria', 'horizontalFitResultCriteria']
        self._configurationKeys = ['Control', 'XStart', 'YStart', 'Width', 'Height', 'Gain', 'SensorFineOffset', 'SensorCourseOffset',
                                   'IntegrationTime', 'ImageClock', 'AdcPhase', 'Orientation', 'RampInc']
        self._DAC0Keys = self._DAC1Keys = self._DAC2Keys = self._DAC3Keys = ['MinOutVoltage', 'MaxOutVoltage', 'MinDACCode',
                                                                             'MaxDACCode', 'Action;']
        self._fitParameterKeys = ['MaxDeltaChiSq', 'Threshold', 'MaxIter', 'FilterSpan', 'FilterCtrl']
        self._fitResultCriteriaKeys = ['MaxWidth', 'MinWidth', 'MinRSQ', 'MinAmp', 'CalibCoeff', 'CalibOffset']

        self._logger = logging.getLogger("NanoBpmCtrl.NanoBpm")
        logging.basicConfig(level=logging.INFO)
        self._logger.setLevel(logging.DEBUG)
        self._controlWord = 0
        self._nbFramesToSum = 8
        self._thread = None
        self._lock = lock.Semaphore()
        self._remoteDataSelector = 0
        self._frameNbAcquired = -1
        self._actionByte = 0
        self._imageDescriptor = None
        self._configurationParameters = None
        self._dac0Parameters = None
        self._dac1Parameters = None
        self._dac2Parameters = None
        self._dac3Parameters = None
        self._fitParameters = None
        self._vertFitResultParameters = None
        self._horFitResultParameters = None
        self._deviceInfo = self.getDeviceInfo()
        self._deviceConfig = self.getDeviceConfig()
        self._deviceParameters = self.getDeviceParameters()
        # This defines the quad window as the full Image - We do not use the Quad's ROI
        self._quadConfig = OrderedDict(zip(self._quadConfigKeys, ((self._deviceConfig['XEnd'] - self._deviceConfig['XStart']) / 2,
                                (self._deviceConfig['YEnd'] - self._deviceConfig['YStart']) / 2, self._deviceConfig['XStart'],
                                self._deviceConfig['XEnd'], self._deviceConfig['YStart'], self._deviceConfig['YEnd'])))

    def subscribe(self, cbfunc):
        self.callbacks.append(cbfunc)

    @property
    def CoG(self):
        return self._CoG

    @property
    def xProfile(self):
        return [float(x) for x in self._xProfile]

    @property
    def yProfile(self):
        return [float(x) for x in self._yProfile]

    @property
    def xFit(self):
        return self._xFit

    @property
    def yFit(self):
        return self._yFit

    @property
    def frameNbAcquired(self):
        return self._frameNbAcquired

    @property
    def nbFramesToSum(self):
        return self._nbFramesToSum

    @nbFramesToSum.setter
    def nbFramesToSum(self, frames):
        """ Set the number of frames to average or sum """
        self._nbFramesToSum = frames

    @property
    def storeDark(self):
        return self._controlWord & self.Control.STORE_DARK

    @storeDark.setter
    def storeDark(self, store_dark):
        """ Enable/disable store dark image mode """
        if store_dark:
            self._controlWord |= self.Control.STORE_DARK
        else:
            self._controlWord &= ~self.Control.STORE_DARK

    @property
    def collectSum32(self):
        return self._controlWord & self.Control.COLLECT_SUM

    @collectSum32.setter
    def collectSum32(self, collect_sum):
        """ Enable/disable collect 32 bit summed images """
        if collect_sum:
            self._controlWord |= self.Control.COLLECT_SUM
        else:
            self._controlWord &= ~self.Control.COLLECT_SUM

    def _checkReplyOK(self, command_sent, reply):
        errorCode = struct.unpack('>I', reply[2:])[0]
        if command_sent != reply[0:2]:
            self._logger.error("Acknowledged the wrong Code: sent {0} replied {1}"
                               .format([ord(c) for c in command_sent],[ord(c) for c in reply[0:2]]))
            return False
        elif errorCode != self.NO_ERROR:
            self._logger.error("Acknowledgement error: %s" % self._errorCode2string[errorCode])
            return False
        else:
            return True

    def getDeviceInfo(self):
        """ Get the basic information about the hardware and software configuration of the device
        """
        reply = self.command_socket.write_read(self.commandGetDeviceInfo, size=70, timeout=100)
        if self._checkReplyOK(self.commandGetDeviceInfo, reply[0:6]):
            data = numpy.ndarray((32,), dtype='>u2', buffer=reply[6:])
            return OrderedDict(zip(self._deviceInfoKeys, data))
        else:
            return None

    def getDeviceConfig(self):
        """ Read the current device configuration from the nanoBpm """
        reply = self.command_socket.write_read(self.commandGetConfig, size=28, timeout=10)
        if self._checkReplyOK(self.commandGetConfig, reply[0:6]):
            return OrderedDict(zip(self._deviceConfigKeys, struct.unpack('>11H', reply[6:])))
        else:
            return None

    def setDeviceConfig(self):
        """ set the current config values """
        buff = struct.pack('>11H', *self._deviceConfig.values())
        reply = self.command_socket.write_read(self.commandSetConfig + buff, size=6, timeout=10)
        return self._checkReplyOK(self.commandSetConfig, reply[0:6])

    def getIntegrationTime(self):
        self.getDeviceParameters()
        return self._configurationParameters['IntegrationTime']

    def setIntegrationTime(self, time):
        self._configurationParameters['IntegrationTime'] = time
        self.setDeviceParameters()

    def setDeviceParameters(self):
        buff = struct.pack('>8Hf4H', *self._configurationParameters.values())
        buff += struct.pack('>ff3H', *self._dac0Parameters.values())
        buff += struct.pack('>ff3H', *self._dac1Parameters.values())
        buff += struct.pack('>ff3H', *self._dac2Parameters.values())
        buff += struct.pack('>ff3H', *self._dac3Parameters.values())
        buff += struct.pack('>ff3H', *self._fitParameters.values())
        buff += struct.pack('>6f', *self._vertFitResultParameters.values())
        buff += struct.pack('>6f', *self._horFitResultParameters.values())
        reply = self.command_socket.write_read(self.commandSetParams + buff, size=6, timeout=10)
        return self._checkReplyOK(self.commandSetParams, reply)

    def getDeviceParameters(self):
        reply = self.command_socket.write_read(self.commandGetParams, size=152, timeout=10)
        if self._checkReplyOK(self.commandGetParams, reply[0:6]):
            self._configurationParameters = OrderedDict(zip(self._configurationKeys, struct.unpack('>8Hf4H', reply[6:34])))
            self._dac0Parameters = OrderedDict(zip(self._DAC0Keys, struct.unpack('>ff3H', reply[34:48])))
            self._dac1Parameters = OrderedDict(zip(self._DAC1Keys, struct.unpack('>ff3H', reply[48:62])))
            self._dac2Parameters = OrderedDict(zip(self._DAC2Keys, struct.unpack('>ff3H', reply[62:76])))
            self._dac3Parameters = OrderedDict(zip(self._DAC3Keys, struct.unpack('>ff3H', reply[76:90])))
            self._fitParameters = OrderedDict(zip(self._fitParameterKeys, struct.unpack('>ff3H', reply[90:104])))
            self._vertFitResultParameters = OrderedDict(zip(self._fitResultCriteriaKeys,
                                                            struct.unpack('>6f', reply[104:128])))
            self._horFitResultParameters = OrderedDict(zip(self._fitResultCriteriaKeys,
                                                           struct.unpack('>6f', reply[128:])))
            return OrderedDict(zip(self._deviceParameterKeys, [
                self._configurationParameters, self._dac0Parameters, self._dac1Parameters,
                self._dac2Parameters, self._dac3Parameters, self._fitParameters,
                self._vertFitResultParameters, self._horFitResultParameters]))
        else:
            return None

    def deviceReset(self):
        reply = self.control_socket.write_read(self.commandReset, size=6, timeout=10)
        return self._checkReplyOK(self.commandReset, reply)

    def deviceInterrupt(self):
        reply = self.control_socket.write_read(self.commandInterrupt, size=6, timeout=10)
        return self._checkReplyOK(self.commandInterrupt, reply)

    def getDeviceStatus(self):
        reply = self.control_socket.write_read(self.commandStatus, size=8, timeout=10)
        if self._checkReplyOK(self.commandStatus, reply[0:6]):
            return struct.unpack('>H', reply[6:])[0] & 0x8

    def remoteDataToggle(self):
        buf = struct.pack('>H', self._remoteDataSelector)
        reply = self.command_socket.write_read(self.commandDataToggle + buf, size=6, timeout=10)
        return self._checkReplyOK(self.commandDataToggle, reply)

    def readImage8(self):
        reply = self.command_socket.write_read(self.commandReadImage8, size=20, timeout=10)
        if self._checkReplyOK(self.commandReadImage8, reply[0:6]):
            imageDescriptor = OrderedDict(zip(self._imageDescriptorKeys, struct.unpack('>HIHHI', reply[6:])))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            self._logger.debug("imageDescriptor returned image size [{0},{1}]".format(imageDescriptor['XSize'],
                                                                                      imageDescriptor['YSize']))

            image_length = imageDescriptor['XSize'] * imageDescriptor['YSize']
            data = self.command_socket.read(size=image_length, timeout=10)
            #  need to invert X & Y to get the real image)
            image = numpy.ndarray(shape=(imageDescriptor['YSize'], imageDescriptor['XSize']), dtype='>u1', buffer=data)
            # do callback function
            imageData=(self.BPP8, image)
            for doCallback in self.callbacks:
                doCallback(None, None, None, None, None, imageData)


    def readImage16(self):
        reply = self.command_socket.write_read(self.commandReadImage16, size=20, timeout=10)
        if self._checkReplyOK(self.commandReadImage16, reply[0:6]):
            imageDescriptor = OrderedDict(zip(self._imageDescriptorKeys, struct.unpack('>HIHHI', reply[6:])))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            image_length = imageDescriptor['XSize'] * imageDescriptor['YSize'] * 2
            data = self.command_socket.read(size=image_length, timeout=10)
            image =  numpy.ndarray(shape=(imageDescriptor['YSize'], imageDescriptor['XSize']), dtype='>u2', buffer=data)
            # do callback function
            imageData=(self.BPP16, image)
            for doCallback in self.callbacks:
                doCallback(None, None, None, None, None, imageData)

    def readDark16(self):
        reply = self.command_socket.write_read(self.commandReadDark16, size=20, timeout=10)
        if self._checkReplyOK(self.commandReadDark16, reply[0:6]):
            imageDescriptor = OrderedDict(zip(self._imageDescriptorKeys, struct.unpack('>HIHHI', reply[6:])))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            image_length = imageDescriptor['XSize'] * imageDescriptor['YSize'] * 2
            data = self.command_socket.read(size=image_length, timeout=10)
            image = numpy.ndarray(shape=(imageDescriptor['YSize'], imageDescriptor['XSize']), dtype='>u2', buffer=data)
            # do callback function
            imageData=(self.BPP16, image)
            for doCallback in self.callbacks:
                doCallback(None, None, None, None, None, imageData)

    def readAve16Sum32(self):
        buf = struct.pack('>HH', self._nbFramesToSum, self._controlWord)
        reply = self.command_socket.write_read(self.commandAve16Sum32 + buf, size=20, timeout=4000)
        if self._checkReplyOK(self.commandAve16Sum32, reply[0:6]):
            imageDescriptor = OrderedDict(zip(self._imageDescriptorKeys, struct.unpack('>HIHHI', reply[6:])))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            if self._controlWord & self.Control.COLLECT_SUM:
                bytes = 4
                type = '>u4'
                depth = self.BPP32
            else:
                type = '>u2'
                bytes = 2
                depth = self.BPP16
            image_length = imageDescriptor['XSize'] * imageDescriptor['YSize'] * bytes
            data = self.command_socket.read(size=image_length, timeout=10)
            image = numpy.ndarray(shape=(imageDescriptor['YSize'], imageDescriptor['XSize']), dtype=type, buffer=data)
            # do callback function
            imageData=(depth, image)
            for doCallback in self.callbacks:
                doCallback(None, None, None, None, None, imageData)

    def readContinuousFrame(self, dataSelector):
        self._logger.debug("readContinuousFrame(): dataSelector {0}".format(dataSelector))
        buf = struct.pack('>B6H', dataSelector, *self._quadConfig.values())
        reply = self.command_socket.write_read(self.commandContinuous + buf, size=10, timeout=10)
        sensorConfig = struct.unpack('>3H', reply[:6])
        self._logger.debug("readContinuousFrame(): sensor config [{0},{1}]".format(sensorConfig[0], sensorConfig[1]))
        payloadSize = struct.unpack('>I', reply[6:])
        while(1):
            reply = self.command_socket.read(size=14, timeout=10)
            imageDescriptor = OrderedDict(zip(self._imageDescriptorKeys, struct.unpack('>HIHHI', reply)))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            xsize = imageDescriptor['XSize']
            ysize = imageDescriptor['YSize']
            self._logger.debug("readContinuousFrame(): image size [{0},{1}]".format(xsize, ysize))
            self._logger.debug("readContinuousFrame(): payload {0}".format(payload))
            data = self.command_socket.read(size=payloadSize[0], timeout=10)
            nextIndex = 0
            (imageSum, XMultAcc, YMultAcc) = struct.unpack('>3Q', data[:24])
            nextIndex += 24
            xprofile = numpy.ndarray(shape=(xsize,), dtype='>u4', buffer=data[nextIndex:nextIndex + xsize * 4])
            nextIndex += xsize * 4
            yprofile = numpy.ndarray(shape=(ysize,), dtype='>u4', buffer=data[nextIndex:nextIndex + ysize * 4])
            nextIndex += ysize * 4
            if dataSelector & self.DataSelector.XPROFILE_FIT:
                xfit = numpy.ndarray(shape=(xsize,), dtype='>u4', buffer=data[nextIndex:nextIndex + 40])
                self._logger.debug("readContinuousFrame(): xfit {0}".format(xfit))
                nextIndex += 20
            else:
                xfit = None
            if dataSelector & self.DataSelector.YPROFILE_FIT:
                yfit = numpy.ndarray(shape=(xsize,), dtype='>u4', buffer=data[nextIndex:nextIndex + 40])
                self._logger.debug("readContinuousFrame(): yfit {0}".format(yfit))
                nextIndex += 20
            else:
                yfit = None
            xcog = XMultAcc / imageSum
            ycog = YMultAcc / imageSum
            cog = (xcog, ycog)
            self._logger.debug("readContinuousFrame(): xcog,ycog [{0},{1}]".format(xcog, ycog))
            # do callback function
            for func in self.callbacks:
                func(cog, xprofile, yprofile, xfit, yfit)
            self._logger.debug("readContinuousFrame(): actionByte {0}".format(self._actionByte))
            buf = struct.pack('>B', self._actionByte)
            self.command_socket.write(buf)
            if self._actionByte == 0:
                break

    def startContinuousFrame(self):
        # select(XCoG + YCoG and X + Y Profiles)
        dataSelector = self.DataSelector.XCOG | self.DataSelector.YCOG
        dataSelector |= self.DataSelector.XPROFILE | self.DataSelector.YPROFILE
#        dataSelector |= self.DataSelector.XPROFILE_FIT | self.DataSelector.YPROFILE_FIT
        self._actionByte = 1
        self._logger.info("startContinuousFrame(): Starting")
        self._thread = gevent.spawn(self.readContinuousFrame, dataSelector)


    def stopContinuousFrame(self):
        with self._lock:
            self._actionByte = 0
        gevent.joinall([self._thread])
        self._thread = None

    def startDataStreaming(self):
        dataSelector = self.DataSelector.XCOG | self.DataSelector.YCOG
        dataSelector |= self.DataSelector.XPROFILE | self.DataSelector.YPROFILE
        dataSelector |= self.DataSelector.XPROFILE_FIT | self.DataSelector.YPROFILE_FIT
        # image cannot be selected with anything else
        if dataSelector & self.DataSelector.IMAGE:
            dataSelector & ~0xe
        self._thread = gevent.spawn(self.streamData, dataSelector)
        self._logger.info("startDataStreaming(): data selector {0}".format(dataSelector))

    def stopDataStreaming(self):
        self.deviceInterrupt()
        self._thread.kill()
        gevent.joinall([self._thread])
        self._thread = None

    def streamData(self, dataSelector):
        buf = struct.pack('>7H', dataSelector, *self._quadConfig.values())
        reply = self.command_socket.write_read(self.commandStreamData + buf, size=10, timeout=10)
        (xsize, ysize, darkSubtract) = struct.unpack('>3H', reply[:6])
        self._logger.debug("streamData(): sensor config [{0},{1}]".format(xsize, ysize))
        (payloadSize,) = struct.unpack('>I', reply[6:10])
        self._logger.debug("streamData(): payload size {0}".format(payloadSize))
        while(1):
            try:
                data = self.command_socket.read(size=payloadSize, timeout=10)
            except:
                break;
            (frameNb,) = struct.unpack('>H', data[:2])
            self._logger.debug("streamData(): frame nos {0}".format(frameNb))
            nextIndex = 2
            cog = xfit = yfit = xprofile = yprofile = None
            xcentre = ycentre = 0.0  # beware 0.0 means do nothing
            if dataSelector & self.DataSelector.IMAGE:
                self._logger.debug("streamData(): decode image {0}".format(len(data)))
                image = numpy.ndarray(shape=(ysize, xsize), dtype='>u1', buffer=data[2:])
            if dataSelector & self.DataSelector.XCOG and dataSelector & self.DataSelector.YCOG:
                (imageSum, XMultAcc, YMultAcc) = struct.unpack('>3Q', data[nextIndex:nextIndex + 24])
                self._logger.debug("streamData(): imageSum, XMultAcc, YMultAcc {0} {1} {2}".format(imageSum, XMultAcc, YMultAcc))
                nextIndex += 24
                # Deprecated use cog from profile fitting instead
                # xcog = XMultAcc / imageSum
                # ycog = YMultAcc / imageSum
                # cog = (xcog, ycog)
            if dataSelector & self.DataSelector.XPROFILE:
                self._logger.debug("streamData(): decode xprofile from {0} to {1}".format(nextIndex, nextIndex + xsize * 4))
                xprofile = numpy.ndarray(shape=(xsize,), dtype='>u4', buffer=data[nextIndex:nextIndex + xsize * 4])
                nextIndex += xsize * 4
                self._logger.debug("streamData(): x {0} {1} {2}".format(xprofile[0], xprofile[1], xprofile[ysize - 1]))
            if dataSelector & self.DataSelector.YPROFILE:
                self._logger.debug("streamData(): decode yprofile from {0} to {1}".format(nextIndex, nextIndex + xsize * 4))
                yprofile = numpy.ndarray(shape=(ysize,), dtype='>u4', buffer=data[nextIndex:nextIndex + ysize * 4])
                nextIndex += ysize * 4
                self._logger.debug("streamData(): y {0} {1} {2}".format(yprofile[0], yprofile[1], yprofile[ysize - 1]))
            if dataSelector & self.DataSelector.XPROFILE_FIT:
                self._logger.debug("streamData(): decode xprofile fit from {0} to {1}".format(nextIndex, nextIndex + 20))
                (xb, xa, x0, xsigma, xrsq) = struct.unpack('>5f', data[nextIndex:nextIndex + 20])
                self._logger.debug("streamData(): xfit {0} {1} {2} {3} {4}".format(xb, xa, x0, xsigma, xrsq))
                nextIndex += 20
                if xrsq <= 1.0 and xrsq > 0.8:  # its a good fit
                    xfit = (xb, xa, x0, xsigma, xrsq)
                    if x0 <= xsize:
                        xcentre = x0
            if dataSelector & self.DataSelector.YPROFILE_FIT:
                self._logger.debug("streamData(): decode yprofile fit from {0} to {1}".format(nextIndex, nextIndex + 20))
                (yb, ya, y0, ysigma, yrsq) = struct.unpack('>5f', data[nextIndex:nextIndex + 20])
                self._logger.debug("streamData(): yfit {0} {1} {2} {3} {4}".format(yb, ya, y0, ysigma, yrsq))
                nextIndex += 20
                if yrsq <= 1.0 and yrsq > 0.8:  # its a good fit
                    yfit = (yb, ya, y0, ysigma, yrsq)
                    if y0 <= ysize:
                        ycentre = y0
            cog = (xcentre, ycentre)
            # do callback function
            for doCallback in self.callbacks:
                doCallback(cog, xprofile, yprofile, xfit, yfit, None)
Ejemplo n.º 4
0
class NanoBpm(object):
    # Errors codes
    NO_ERROR, CODE1, CODE2, CODE3, CODE4, CODE5, CODE6, CODE7, CODE8 = range(9)
    BPP8, BPP16, BPP32 = range(3)

    SETTINGS = _config_property('Settings', "Configuration settings")
    GAIN = _config_property("Gain", "Set device gain")
    OFFSET = _config_property('Offset', "Set device offset")
    LINEINTTIME = _config_property('LineIntTime', "Line time")
    YEND = _config_property('YEnd', "ROI Y end")
    FRAMEINTTIME = _config_property('FrameIntTime', "Frame time")
    YSTART = _config_property('YStart', "ROI Y start")
    XSTART = _config_property('XStart', "ROI X start")
    XEND = _config_property('XEnd', "ROI X end")
    ADCPHASE = _config_property('AdcPhase', "adc phase")
    SUBTRACTDARK = _config_property('SubtractDarkImage',
                                    "Subtract the stored dark image")

    MAXDELTACHISQ = _fit_property('MaxDeltaChiSq', "Maximum Delta CHI squared")
    THRESHOLD = _fit_property('Threshold', "Threshold")
    MAXITER = _fit_property('MaxIter', "Maximum nos. of iterations")
    FILTERSPAN = _fit_property('FilterSpan', "Filter span")
    FILTERCTRL = _fit_property(
        'FilterCtrl', "Filter enable/disable & running average/median")

    V_MAXWIDTH = _v_result_property('MaxWidth', "Maximum width allowed in um")
    V_MINWIDTH = _v_result_property('MinWidth', "Minimum width allowed in um")
    V_MINRSQ = _v_result_property('MinRSQ', "Minimum RSQ allowed")
    V_MINAMP = _v_result_property('MinAmp', "Minimum intensity allowed")
    V_CALIBCOEFF = _v_result_property('CalibCoeff', "Calibration coefficient")
    V_CALIBOFF = _v_result_property('CalibOffset', "Calibration offset")

    H_MAXWIDTH = _h_result_property('MaxWidth', "Maximum width allowed in um")
    H_MINWIDTH = _h_result_property('MinWidth', "Minimum width allowed in um")
    H_MINRSQ = _h_result_property('MinRSQ', "Minimum RSQ allowed")
    H_MINAMP = _h_result_property('MinAmp', "Minimum intensity allowed")
    H_CALIBCOEFF = _h_result_property('CalibCoeff', "Calibration coefficient")
    H_CALIBOFF = _h_result_property('CalibOffset', "Calibration offset")

    class DataSelector():
        # DataSelector bit fields
        XCOG = 1
        YCOG = 2
        XPROFILE = 4
        YPROFILE = 8
        HISTOGRAM = 16
        QUADSUM = 32
        XPROFILE_FIT = 64
        YPROFILE_FIT = 128
        #only for stream data
        IMAGE = 256

    class Control():
        # Control word bit fields
        COLLECT_SUM = 1
        STORE_DARK = 2
        PROGRESS_UPDATE = 4

    callbacks = []

    def __init__(self, name, config):
        """ FireFlash hardware controller.

        name -- the controller's name
        config -- controller configuration,
        in this dictionary we need to have:
        command_url -- url of the command port
        control_url -- url of the control port
        """
        # Status bits for getStatus command
        self.StatusBits = {'IDLE': 0, 'BUSY': 1, 'REMOTE': 2, 'STREAMING': 4}
        # Status bits for Remote Data Toggle
        self.ToggleBits = {'OFF': 0, 'XFIT': 1, 'YFIT': 2, 'COG': 4}

        try:
            self.command_socket = get_comm(self.config['command'], ctype=TCP)
        except KeyError:
            command_url = config["command_url"]
            warn("'command_url' keyword is deprecated." \
                 " Use 'command: tcp' instead", DeprecationWarning)
            comm_cfg = {'tcp': {'url': command_url}}
            self.command_socket = get_comm(comm_cfg)

        try:
            self.control_socket = get_comm(self.config['control'], ctype=TCP)
        except KeyError:
            control_url = config["control_url"]
            warn("'control_url' keyword is deprecated." \
                 " Use 'control: tcp' instead", DeprecationWarning)
            comm_cfg = {'tcp': {'url': control_url}}
            self.control_socket = get_comm(comm_cfg)

        # Commands ready packed in network byte order
        self.commandReset = struct.pack(">H", 0xAA00)
        self.commandInterrupt = struct.pack(">H", 0xAA01)
        self.commandStatus = struct.pack(">H", 0xAA02)
        self.commandDataToggle = struct.pack(">H", 0xAA03)
        self.commandGetDeviceInfo = struct.pack(">H", 0xAA20)
        self.commandSetConfig = struct.pack(">H", 0xAA22)
        self.commandGetConfig = struct.pack(">H", 0xAA21)
        self.commandGetIntTime = struct.pack(">H", 0xAA23)
        self.commandSetIntTime = struct.pack(">H", 0xAA24)
        self.commandSetParams = struct.pack(">H", 0xAA26)
        self.commandGetParams = struct.pack(">H", 0xAA27)
        self.commandReadImage16 = struct.pack(">H", 0xAA30)
        self.commandReadImage8 = struct.pack(">H", 0xAA31)
        self.commandReadDark16 = struct.pack(">H", 0xAA32)
        self.commandAve16Sum32 = struct.pack(">H", 0xAA33)
        self.commandContinuous = struct.pack(">H", 0xAA37)
        self.commandStreamData = struct.pack(">H", 0xAA3A)

        self._errorCode2string = {
            self.NO_ERROR: "No Error",
            self.CODE1: "Error parsing .ini file",
            self.CODE2: "Could not establish network connection",
            self.CODE3: "Network data transfer failed",
            self.CODE4: "Incorrect FPGA type",
            self.CODE5: "Invalid argument or config param error",
            self.CODE6: "I^C-bus communication error",
            self.CODE7: "Memory initialization error",
            self.CODE8: "I^C-bus initialization error"
        }
        # Device Info structure keys
        self._deviceInfoKeys = [
            'ProcessorType', 'ProcessorVersion', 'FPGAType', 'FPGAVersion',
            'BoardType', 'BoardVersion', 'BuidYear', 'BuildMonth', 'Buildday',
            'BuildHour', 'BuildMinute', 'BuildSecond', 'SWMajor', 'SWMinor',
            'SWbuild', 'FirmWare Major', 'FirmWareMinor', 'FirmWareBuild'
            ', BoardID'
        ]
        # Device configuration parameter keys
        self._deviceConfigKeys = [
            'Settings', 'Gain', 'Offset', 'LineIntTime', 'YEnd',
            'FrameIntTime', 'YStart', 'XStart', 'XEnd', 'AdcPhase',
            'SubtractDarkImage'
        ]
        #Image Descriptor keys
        self._imageDescriptorKeys = [
            'FrameNb', 'IntegrationTime', 'XSize', 'YSize', 'InternalPtr'
        ]
        self._quadConfigKeys = [
            'XCentre', 'YCentre', 'WinStartX', 'WinEndX', 'WinStartY',
            'WinEndY'
        ]
        self._sensorConfigKeys = ['YSize', 'DarkImageSubtract']
        #device parameter keys
        self._deviceParameterKeys = [
            'configurationKeys', 'DAC0Keys', 'DAC1Keys', 'DAC2keys',
            'DAC3Keys', 'fitParameterKeys', 'verticalFitResultCriteria',
            'horizontalFitResultCriteria'
        ]
        self._configurationKeys = [
            'Control', 'XStart', 'YStart', 'Width', 'Height', 'Gain',
            'SensorFineOffset', 'SensorCourseOffset', 'IntegrationTime',
            'ImageClock', 'AdcPhase', 'Orientation', 'RampInc'
        ]
        self._DAC0Keys = self._DAC1Keys = self._DAC2Keys = self._DAC3Keys = [
            'MinOutVoltage', 'MaxOutVoltage', 'MinDACCode', 'MaxDACCode',
            'Action;'
        ]
        self._fitParameterKeys = [
            'MaxDeltaChiSq', 'Threshold', 'MaxIter', 'FilterSpan', 'FilterCtrl'
        ]
        self._fitResultCriteriaKeys = [
            'MaxWidth', 'MinWidth', 'MinRSQ', 'MinAmp', 'CalibCoeff',
            'CalibOffset'
        ]

        self._logger = logging.getLogger("NanoBpmCtrl.NanoBpm")
        logging.basicConfig(level=logging.INFO)
        self._logger.setLevel(logging.DEBUG)
        self._controlWord = 0
        self._nbFramesToSum = 8
        self._thread = None
        self._lock = lock.Semaphore()
        self._remoteDataSelector = 0
        self._frameNbAcquired = -1
        self._actionByte = 0
        self._imageDescriptor = None
        self._configurationParameters = None
        self._dac0Parameters = None
        self._dac1Parameters = None
        self._dac2Parameters = None
        self._dac3Parameters = None
        self._fitParameters = None
        self._vertFitResultParameters = None
        self._horFitResultParameters = None
        self._deviceInfo = self.getDeviceInfo()
        self._deviceConfig = self.getDeviceConfig()
        self._deviceParameters = self.getDeviceParameters()
        # This defines the quad window as the full Image - We do not use the Quad's ROI
        self._quadConfig = OrderedDict(
            zip(self._quadConfigKeys,
                ((self._deviceConfig['XEnd'] - self._deviceConfig['XStart']) /
                 2,
                 (self._deviceConfig['YEnd'] - self._deviceConfig['YStart']) /
                 2, self._deviceConfig['XStart'], self._deviceConfig['XEnd'],
                 self._deviceConfig['YStart'], self._deviceConfig['YEnd'])))

    def subscribe(self, cbfunc):
        self.callbacks.append(cbfunc)

    @property
    def CoG(self):
        return self._CoG

    @property
    def xProfile(self):
        return [float(x) for x in self._xProfile]

    @property
    def yProfile(self):
        return [float(x) for x in self._yProfile]

    @property
    def xFit(self):
        return self._xFit

    @property
    def yFit(self):
        return self._yFit

    @property
    def frameNbAcquired(self):
        return self._frameNbAcquired

    @property
    def nbFramesToSum(self):
        return self._nbFramesToSum

    @nbFramesToSum.setter
    def nbFramesToSum(self, frames):
        """ Set the number of frames to average or sum """
        self._nbFramesToSum = frames

    @property
    def storeDark(self):
        return self._controlWord & self.Control.STORE_DARK

    @storeDark.setter
    def storeDark(self, store_dark):
        """ Enable/disable store dark image mode """
        if store_dark:
            self._controlWord |= self.Control.STORE_DARK
        else:
            self._controlWord &= ~self.Control.STORE_DARK

    @property
    def collectSum32(self):
        return self._controlWord & self.Control.COLLECT_SUM

    @collectSum32.setter
    def collectSum32(self, collect_sum):
        """ Enable/disable collect 32 bit summed images """
        if collect_sum:
            self._controlWord |= self.Control.COLLECT_SUM
        else:
            self._controlWord &= ~self.Control.COLLECT_SUM

    def _checkReplyOK(self, command_sent, reply):
        errorCode = struct.unpack('>I', reply[2:])[0]
        if command_sent != reply[0:2]:
            self._logger.error(
                "Acknowledged the wrong Code: sent {0} replied {1}".format(
                    [ord(c) for c in command_sent],
                    [ord(c) for c in reply[0:2]]))
            return False
        elif errorCode != self.NO_ERROR:
            self._logger.error("Acknowledgement error: %s" %
                               self._errorCode2string[errorCode])
            return False
        else:
            return True

    def getDeviceInfo(self):
        """ Get the basic information about the hardware and software configuration of the device
        """
        reply = self.command_socket.write_read(self.commandGetDeviceInfo,
                                               size=70,
                                               timeout=100)
        if self._checkReplyOK(self.commandGetDeviceInfo, reply[0:6]):
            data = numpy.ndarray((32, ), dtype='>u2', buffer=reply[6:])
            return OrderedDict(zip(self._deviceInfoKeys, data))
        else:
            return None

    def getDeviceConfig(self):
        """ Read the current device configuration from the nanoBpm """
        reply = self.command_socket.write_read(self.commandGetConfig,
                                               size=28,
                                               timeout=10)
        if self._checkReplyOK(self.commandGetConfig, reply[0:6]):
            return OrderedDict(
                zip(self._deviceConfigKeys, struct.unpack('>11H', reply[6:])))
        else:
            return None

    def setDeviceConfig(self):
        """ set the current config values """
        buff = struct.pack('>11H', *self._deviceConfig.values())
        reply = self.command_socket.write_read(self.commandSetConfig + buff,
                                               size=6,
                                               timeout=10)
        return self._checkReplyOK(self.commandSetConfig, reply[0:6])

    def getIntegrationTime(self):
        self.getDeviceParameters()
        return self._configurationParameters['IntegrationTime']

    def setIntegrationTime(self, time):
        self._configurationParameters['IntegrationTime'] = time
        self.setDeviceParameters()

    def setDeviceParameters(self):
        buff = struct.pack('>8Hf4H', *self._configurationParameters.values())
        buff += struct.pack('>ff3H', *self._dac0Parameters.values())
        buff += struct.pack('>ff3H', *self._dac1Parameters.values())
        buff += struct.pack('>ff3H', *self._dac2Parameters.values())
        buff += struct.pack('>ff3H', *self._dac3Parameters.values())
        buff += struct.pack('>ff3H', *self._fitParameters.values())
        buff += struct.pack('>6f', *self._vertFitResultParameters.values())
        buff += struct.pack('>6f', *self._horFitResultParameters.values())
        reply = self.command_socket.write_read(self.commandSetParams + buff,
                                               size=6,
                                               timeout=10)
        return self._checkReplyOK(self.commandSetParams, reply)

    def getDeviceParameters(self):
        reply = self.command_socket.write_read(self.commandGetParams,
                                               size=152,
                                               timeout=10)
        if self._checkReplyOK(self.commandGetParams, reply[0:6]):
            self._configurationParameters = OrderedDict(
                zip(self._configurationKeys,
                    struct.unpack('>8Hf4H', reply[6:34])))
            self._dac0Parameters = OrderedDict(
                zip(self._DAC0Keys, struct.unpack('>ff3H', reply[34:48])))
            self._dac1Parameters = OrderedDict(
                zip(self._DAC1Keys, struct.unpack('>ff3H', reply[48:62])))
            self._dac2Parameters = OrderedDict(
                zip(self._DAC2Keys, struct.unpack('>ff3H', reply[62:76])))
            self._dac3Parameters = OrderedDict(
                zip(self._DAC3Keys, struct.unpack('>ff3H', reply[76:90])))
            self._fitParameters = OrderedDict(
                zip(self._fitParameterKeys,
                    struct.unpack('>ff3H', reply[90:104])))
            self._vertFitResultParameters = OrderedDict(
                zip(self._fitResultCriteriaKeys,
                    struct.unpack('>6f', reply[104:128])))
            self._horFitResultParameters = OrderedDict(
                zip(self._fitResultCriteriaKeys,
                    struct.unpack('>6f', reply[128:])))
            return OrderedDict(
                zip(self._deviceParameterKeys, [
                    self._configurationParameters, self._dac0Parameters,
                    self._dac1Parameters, self._dac2Parameters,
                    self._dac3Parameters, self._fitParameters,
                    self._vertFitResultParameters, self._horFitResultParameters
                ]))
        else:
            return None

    def deviceReset(self):
        reply = self.control_socket.write_read(self.commandReset,
                                               size=6,
                                               timeout=10)
        return self._checkReplyOK(self.commandReset, reply)

    def deviceInterrupt(self):
        reply = self.control_socket.write_read(self.commandInterrupt,
                                               size=6,
                                               timeout=10)
        return self._checkReplyOK(self.commandInterrupt, reply)

    def getDeviceStatus(self):
        reply = self.control_socket.write_read(self.commandStatus,
                                               size=8,
                                               timeout=10)
        if self._checkReplyOK(self.commandStatus, reply[0:6]):
            return struct.unpack('>H', reply[6:])[0] & 0x8

    def remoteDataToggle(self):
        buf = struct.pack('>H', self._remoteDataSelector)
        reply = self.command_socket.write_read(self.commandDataToggle + buf,
                                               size=6,
                                               timeout=10)
        return self._checkReplyOK(self.commandDataToggle, reply)

    def readImage8(self):
        reply = self.command_socket.write_read(self.commandReadImage8,
                                               size=20,
                                               timeout=10)
        if self._checkReplyOK(self.commandReadImage8, reply[0:6]):
            imageDescriptor = OrderedDict(
                zip(self._imageDescriptorKeys,
                    struct.unpack('>HIHHI', reply[6:])))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            self._logger.debug(
                "imageDescriptor returned image size [{0},{1}]".format(
                    imageDescriptor['XSize'], imageDescriptor['YSize']))

            image_length = imageDescriptor['XSize'] * imageDescriptor['YSize']
            data = self.command_socket.read(size=image_length, timeout=10)
            #  need to invert X & Y to get the real image)
            image = numpy.ndarray(shape=(imageDescriptor['YSize'],
                                         imageDescriptor['XSize']),
                                  dtype='>u1',
                                  buffer=data)
            # do callback function
            imageData = (self.BPP8, image)
            for doCallback in self.callbacks:
                doCallback(None, None, None, None, None, imageData)

    def readImage16(self):
        reply = self.command_socket.write_read(self.commandReadImage16,
                                               size=20,
                                               timeout=10)
        if self._checkReplyOK(self.commandReadImage16, reply[0:6]):
            imageDescriptor = OrderedDict(
                zip(self._imageDescriptorKeys,
                    struct.unpack('>HIHHI', reply[6:])))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            image_length = imageDescriptor['XSize'] * imageDescriptor[
                'YSize'] * 2
            data = self.command_socket.read(size=image_length, timeout=10)
            image = numpy.ndarray(shape=(imageDescriptor['YSize'],
                                         imageDescriptor['XSize']),
                                  dtype='>u2',
                                  buffer=data)
            # do callback function
            imageData = (self.BPP16, image)
            for doCallback in self.callbacks:
                doCallback(None, None, None, None, None, imageData)

    def readDark16(self):
        reply = self.command_socket.write_read(self.commandReadDark16,
                                               size=20,
                                               timeout=10)
        if self._checkReplyOK(self.commandReadDark16, reply[0:6]):
            imageDescriptor = OrderedDict(
                zip(self._imageDescriptorKeys,
                    struct.unpack('>HIHHI', reply[6:])))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            image_length = imageDescriptor['XSize'] * imageDescriptor[
                'YSize'] * 2
            data = self.command_socket.read(size=image_length, timeout=10)
            image = numpy.ndarray(shape=(imageDescriptor['YSize'],
                                         imageDescriptor['XSize']),
                                  dtype='>u2',
                                  buffer=data)
            # do callback function
            imageData = (self.BPP16, image)
            for doCallback in self.callbacks:
                doCallback(None, None, None, None, None, imageData)

    def readAve16Sum32(self):
        buf = struct.pack('>HH', self._nbFramesToSum, self._controlWord)
        reply = self.command_socket.write_read(self.commandAve16Sum32 + buf,
                                               size=20,
                                               timeout=4000)
        if self._checkReplyOK(self.commandAve16Sum32, reply[0:6]):
            imageDescriptor = OrderedDict(
                zip(self._imageDescriptorKeys,
                    struct.unpack('>HIHHI', reply[6:])))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            if self._controlWord & self.Control.COLLECT_SUM:
                bytes = 4
                type = '>u4'
                depth = self.BPP32
            else:
                type = '>u2'
                bytes = 2
                depth = self.BPP16
            image_length = imageDescriptor['XSize'] * imageDescriptor[
                'YSize'] * bytes
            data = self.command_socket.read(size=image_length, timeout=10)
            image = numpy.ndarray(shape=(imageDescriptor['YSize'],
                                         imageDescriptor['XSize']),
                                  dtype=type,
                                  buffer=data)
            # do callback function
            imageData = (depth, image)
            for doCallback in self.callbacks:
                doCallback(None, None, None, None, None, imageData)

    def readContinuousFrame(self, dataSelector):
        self._logger.debug(
            "readContinuousFrame(): dataSelector {0}".format(dataSelector))
        buf = struct.pack('>B6H', dataSelector, *self._quadConfig.values())
        reply = self.command_socket.write_read(self.commandContinuous + buf,
                                               size=10,
                                               timeout=10)
        sensorConfig = struct.unpack('>3H', reply[:6])
        self._logger.debug(
            "readContinuousFrame(): sensor config [{0},{1}]".format(
                sensorConfig[0], sensorConfig[1]))
        payloadSize = struct.unpack('>I', reply[6:])
        while (1):
            reply = self.command_socket.read(size=14, timeout=10)
            imageDescriptor = OrderedDict(
                zip(self._imageDescriptorKeys, struct.unpack('>HIHHI', reply)))
            self._frameNbAcquired = imageDescriptor['FrameNb']
            xsize = imageDescriptor['XSize']
            ysize = imageDescriptor['YSize']
            self._logger.debug(
                "readContinuousFrame(): image size [{0},{1}]".format(
                    xsize, ysize))
            self._logger.debug(
                "readContinuousFrame(): payload {0}".format(payload))
            data = self.command_socket.read(size=payloadSize[0], timeout=10)
            nextIndex = 0
            (imageSum, XMultAcc, YMultAcc) = struct.unpack('>3Q', data[:24])
            nextIndex += 24
            xprofile = numpy.ndarray(shape=(xsize, ),
                                     dtype='>u4',
                                     buffer=data[nextIndex:nextIndex +
                                                 xsize * 4])
            nextIndex += xsize * 4
            yprofile = numpy.ndarray(shape=(ysize, ),
                                     dtype='>u4',
                                     buffer=data[nextIndex:nextIndex +
                                                 ysize * 4])
            nextIndex += ysize * 4
            if dataSelector & self.DataSelector.XPROFILE_FIT:
                xfit = numpy.ndarray(shape=(xsize, ),
                                     dtype='>u4',
                                     buffer=data[nextIndex:nextIndex + 40])
                self._logger.debug(
                    "readContinuousFrame(): xfit {0}".format(xfit))
                nextIndex += 20
            else:
                xfit = None
            if dataSelector & self.DataSelector.YPROFILE_FIT:
                yfit = numpy.ndarray(shape=(xsize, ),
                                     dtype='>u4',
                                     buffer=data[nextIndex:nextIndex + 40])
                self._logger.debug(
                    "readContinuousFrame(): yfit {0}".format(yfit))
                nextIndex += 20
            else:
                yfit = None
            xcog = XMultAcc / imageSum
            ycog = YMultAcc / imageSum
            cog = (xcog, ycog)
            self._logger.debug(
                "readContinuousFrame(): xcog,ycog [{0},{1}]".format(
                    xcog, ycog))
            # do callback function
            for func in self.callbacks:
                func(cog, xprofile, yprofile, xfit, yfit)
            self._logger.debug("readContinuousFrame(): actionByte {0}".format(
                self._actionByte))
            buf = struct.pack('>B', self._actionByte)
            self.command_socket.write(buf)
            if self._actionByte == 0:
                break

    def startContinuousFrame(self):
        # select(XCoG + YCoG and X + Y Profiles)
        dataSelector = self.DataSelector.XCOG | self.DataSelector.YCOG
        dataSelector |= self.DataSelector.XPROFILE | self.DataSelector.YPROFILE
        #        dataSelector |= self.DataSelector.XPROFILE_FIT | self.DataSelector.YPROFILE_FIT
        self._actionByte = 1
        self._logger.info("startContinuousFrame(): Starting")
        self._thread = gevent.spawn(self.readContinuousFrame, dataSelector)

    def stopContinuousFrame(self):
        with self._lock:
            self._actionByte = 0
        gevent.joinall([self._thread])
        self._thread = None

    def startDataStreaming(self):
        dataSelector = self.DataSelector.XCOG | self.DataSelector.YCOG
        dataSelector |= self.DataSelector.XPROFILE | self.DataSelector.YPROFILE
        dataSelector |= self.DataSelector.XPROFILE_FIT | self.DataSelector.YPROFILE_FIT
        # image cannot be selected with anything else
        if dataSelector & self.DataSelector.IMAGE:
            dataSelector & ~0xe
        self._thread = gevent.spawn(self.streamData, dataSelector)
        self._logger.info(
            "startDataStreaming(): data selector {0}".format(dataSelector))

    def stopDataStreaming(self):
        self.deviceInterrupt()
        self._thread.kill()
        gevent.joinall([self._thread])
        self._thread = None

    def streamData(self, dataSelector):
        buf = struct.pack('>7H', dataSelector, *self._quadConfig.values())
        reply = self.command_socket.write_read(self.commandStreamData + buf,
                                               size=10,
                                               timeout=10)
        (xsize, ysize, darkSubtract) = struct.unpack('>3H', reply[:6])
        self._logger.debug("streamData(): sensor config [{0},{1}]".format(
            xsize, ysize))
        (payloadSize, ) = struct.unpack('>I', reply[6:10])
        self._logger.debug(
            "streamData(): payload size {0}".format(payloadSize))
        while (1):
            try:
                data = self.command_socket.read(size=payloadSize, timeout=10)
            except:
                break
            (frameNb, ) = struct.unpack('>H', data[:2])
            self._logger.debug("streamData(): frame nos {0}".format(frameNb))
            nextIndex = 2
            cog = xfit = yfit = xprofile = yprofile = None
            xcentre = ycentre = 0.0  # beware 0.0 means do nothing
            if dataSelector & self.DataSelector.IMAGE:
                self._logger.debug("streamData(): decode image {0}".format(
                    len(data)))
                image = numpy.ndarray(shape=(ysize, xsize),
                                      dtype='>u1',
                                      buffer=data[2:])
            if dataSelector & self.DataSelector.XCOG and dataSelector & self.DataSelector.YCOG:
                (imageSum, XMultAcc,
                 YMultAcc) = struct.unpack('>3Q',
                                           data[nextIndex:nextIndex + 24])
                self._logger.debug(
                    "streamData(): imageSum, XMultAcc, YMultAcc {0} {1} {2}".
                    format(imageSum, XMultAcc, YMultAcc))
                nextIndex += 24
                # Deprecated use cog from profile fitting instead
                # xcog = XMultAcc / imageSum
                # ycog = YMultAcc / imageSum
                # cog = (xcog, ycog)
            if dataSelector & self.DataSelector.XPROFILE:
                self._logger.debug(
                    "streamData(): decode xprofile from {0} to {1}".format(
                        nextIndex, nextIndex + xsize * 4))
                xprofile = numpy.ndarray(shape=(xsize, ),
                                         dtype='>u4',
                                         buffer=data[nextIndex:nextIndex +
                                                     xsize * 4])
                nextIndex += xsize * 4
                self._logger.debug("streamData(): x {0} {1} {2}".format(
                    xprofile[0], xprofile[1], xprofile[ysize - 1]))
            if dataSelector & self.DataSelector.YPROFILE:
                self._logger.debug(
                    "streamData(): decode yprofile from {0} to {1}".format(
                        nextIndex, nextIndex + xsize * 4))
                yprofile = numpy.ndarray(shape=(ysize, ),
                                         dtype='>u4',
                                         buffer=data[nextIndex:nextIndex +
                                                     ysize * 4])
                nextIndex += ysize * 4
                self._logger.debug("streamData(): y {0} {1} {2}".format(
                    yprofile[0], yprofile[1], yprofile[ysize - 1]))
            if dataSelector & self.DataSelector.XPROFILE_FIT:
                self._logger.debug(
                    "streamData(): decode xprofile fit from {0} to {1}".format(
                        nextIndex, nextIndex + 20))
                (xb, xa, x0, xsigma,
                 xrsq) = struct.unpack('>5f', data[nextIndex:nextIndex + 20])
                self._logger.debug(
                    "streamData(): xfit {0} {1} {2} {3} {4}".format(
                        xb, xa, x0, xsigma, xrsq))
                nextIndex += 20
                if xrsq <= 1.0 and xrsq > 0.8:  # its a good fit
                    xfit = (xb, xa, x0, xsigma, xrsq)
                    if x0 <= xsize:
                        xcentre = x0
            if dataSelector & self.DataSelector.YPROFILE_FIT:
                self._logger.debug(
                    "streamData(): decode yprofile fit from {0} to {1}".format(
                        nextIndex, nextIndex + 20))
                (yb, ya, y0, ysigma,
                 yrsq) = struct.unpack('>5f', data[nextIndex:nextIndex + 20])
                self._logger.debug(
                    "streamData(): yfit {0} {1} {2} {3} {4}".format(
                        yb, ya, y0, ysigma, yrsq))
                nextIndex += 20
                if yrsq <= 1.0 and yrsq > 0.8:  # its a good fit
                    yfit = (yb, ya, y0, ysigma, yrsq)
                    if y0 <= ysize:
                        ycentre = y0
            cog = (xcentre, ycentre)
            # do callback function
            for doCallback in self.callbacks:
                doCallback(cog, xprofile, yprofile, xfit, yfit, None)