Beispiel #1
0
def print_events(component, pretty):
    # find all Events
    for name, value in model.getEvents(component).items():
        print_event(name, value, pretty)
Beispiel #2
0
    def __init__(self, name, role, dependencies, settle_time=0, **kwargs):
        '''
        dependencies (dict string->model.HwComponent): the dependencies
            There must a dependency "detector" and at least one of the dependencies
            "pmt-control" and "pmt-signal".
            "pmt-control" takes a PMTControl or spectrograph object, "pmt-signal" an
            extra detector to be activated during the acquisition
            (eg, for shutter control)
        settle_time (0 < float): time to wait after turning on the gain to have
          it fully working.
        Raise an ValueError exception if the dependencies are not compatible
        '''
        # we will fill the set of dependencies with Components later in ._dependencies
        model.Detector.__init__(self,
                                name,
                                role,
                                dependencies=dependencies,
                                **kwargs)

        if settle_time < 0:
            raise ValueError("Settle time of %g s for '%s' is negative" %
                             (settle_time, name))
        elif settle_time > 10:
            # a large value is a sign that the user mistook in units
            raise ValueError("Settle time of %g s for '%s' is too long" %
                             (settle_time, name))
        self._settle_time = settle_time

        # Check the dependencies
        pmt = dependencies["detector"]
        if not isinstance(pmt, ComponentBase):
            raise ValueError("Dependency detector is not a component.")
        if not hasattr(pmt, "data") or not isinstance(pmt.data, DataFlowBase):
            raise ValueError(
                "Dependency detector is not a Detector component.")
        self._pmt = pmt
        self._shape = pmt.shape
        # copy all the VAs and Events from the PMT to here (but .state and .dependencies).
        pmtVAs = model.getVAs(pmt)
        for key, value in pmtVAs.items():
            if not hasattr(self, key):
                setattr(self, key, value)
        pmtEvents = model.getEvents(pmt)
        for key, value in pmtEvents.items():
            setattr(self, key, value)

        if "pmt-control" in dependencies:
            ctrl = dependencies["pmt-control"]
            self._control = ctrl
            if not isinstance(ctrl, ComponentBase):
                raise ValueError("Dependency pmt-control is not a component.")
            # Duplicate control unit VAs
            # In case of counting PMT these VAs are not available since a
            # spectrograph is given instead of the control unit.
            try:
                if model.hasVA(ctrl, "gain"):
                    gain = ctrl.gain.range[0]
                    self.gain = model.FloatContinuous(gain,
                                                      ctrl.gain.range,
                                                      unit="V",
                                                      setter=self._setGain)
                    self._last_gain = gain
                    self._setGain(gain)  # Just start with no gain
                if model.hasVA(ctrl, "powerSupply"):
                    self.powerSupply = ctrl.powerSupply
                    # Turn on the controller
                    self.powerSupply.value = True
            except IOError:
                # FIXME: needs to be handled directly by PMTControl (at least automatic reconnect)
                raise HwError(
                    "PMT Control Unit connection timeout. "
                    "Please turn off and on the power to the box and "
                    "then restart Odemis.")
                # Protection VA should be available anyway
            if not model.hasVA(ctrl, "protection"):
                raise ValueError(
                    "Given component appears to be neither a PMT control "
                    "unit or a spectrograph since protection VA is not "
                    "available.")
        else:
            self._control = None

        if "pmt-signal" in dependencies:
            self._signal = dependencies["pmt-signal"]
            if not isinstance(self._signal, ComponentBase):
                raise ValueError("Dependency pmt-signal is not a component.")
            if not hasattr(self._signal, "data") or not isinstance(
                    self._signal.data, model.DataFlowBase):
                raise ValueError(
                    "Dependency pmt-signal doesn't have an attribute .data of type DataFlow."
                )
        else:
            self._signal = None

        self.data = PMTDataFlow(self, self._pmt, self._control, self._signal)
Beispiel #3
0
def print_events(component, pretty):
    # find all Events
    for name, value in model.getEvents(component).items():
        print_event(name, value, pretty)
Beispiel #4
0
    def __init__(self, name, role, children=None, daemon=None, **kwargs):
        """
        All the arguments are identical to AndorCam2, expected: 
        children (dict string->kwargs): name of child must be "shamrock" and the
          kwargs contains the arguments passed to instantiate the Shamrock component
        """
        # we will fill the set of children with Components later in ._children
        model.Detector.__init__(self, name, role, daemon=daemon, **kwargs)

        # Create the detector (ccd) child
        try:
            dt_kwargs = children["andorcam2"]
        except Exception:
            raise ValueError("AndorSpec excepts one child named 'andorcam2'")

        # We could inherit from it, but difficult to not mix up .binning, .shape
        # .resolution...
        self._detector = andorcam2.AndorCam2(daemon=daemon, **dt_kwargs)
        self._children.add(self._detector)
        dt = self._detector

        # Copy and adapt the VAs and roattributes from the detector
        # set up the detector part
        # check that the shape is "horizontal"
        if dt.shape[0] <= 1:
            raise ValueError(
                "Child detector must have at least 2 pixels horizontally")
        if dt.shape[0] < dt.shape[1]:
            logging.warning(
                "Child detector is shaped vertically (%dx%d), "
                "this is probably incorrect, as wavelengths are "
                "expected to be along the horizontal axis", dt.shape[0],
                dt.shape[1])
        # shape is same as detector (raw sensor), but the max resolution is always flat
        self._shape = tuple(dt.shape)  # duplicate

        # The resolution and binning are derived from the detector, but with
        # settings set so that there is only one horizontal line.
        if dt.binning.range[1][1] < dt.resolution.range[1][1]:
            # without software binning, we are stuck to the max binning
            logging.info(
                "Spectrometer %s will only use a %d px band of the %d "
                "px of the sensor", name, dt.binning.range[1][1],
                dt.resolution.range[1][1])

        resolution = (dt.resolution.range[1][0], 1)  # max,1
        # vertically: 1, with binning as big as possible
        binning = (dt.binning.value[0],
                   min(dt.binning.range[1][1], dt.resolution.range[1][1]))

        min_res = (dt.resolution.range[0][0], 1)
        max_res = (dt.resolution.range[1][0], 1)
        self.resolution = model.ResolutionVA(resolution, [min_res, max_res],
                                             setter=self._setResolution)
        # 2D binning is like a "small resolution"
        self._binning = binning
        self.binning = model.ResolutionVA(self._binning,
                                          dt.binning.range,
                                          setter=self._setBinning)

        self._setBinning(binning)  # will also update the resolution

        # TODO: update also the metadata MD_SENSOR_PIXEL_SIZE
        pxs = dt.pixelSize.value[
            0], dt.pixelSize.value[1] * dt.binning.value[1]
        self.pixelSize = model.VigilantAttribute(pxs, unit="m", readonly=True)
        # Note: the metadata has no MD_PIXEL_SIZE, but a MD_WL_LIST

        # TODO: support software binning by rolling up our own dataflow that
        # does data merging
        assert dt.resolution.range[0][1] == 1
        self.data = dt.data

        # duplicate every other VA and Event from the detector
        # that includes required VAs like .exposureTime
        for aname, value in model.getVAs(dt).items() + model.getEvents(
                dt).items():
            if not hasattr(self, aname):
                setattr(self, aname, value)
            else:
                logging.debug(
                    "skipping duplication of already existing VA '%s'", aname)

        assert hasattr(self, "exposureTime")

        # Create the spectrograph (actuator) child
        try:
            sp_kwargs = children["shamrock"]
        except Exception:
            raise ValueError("AndorSpec excepts one child named 'shamrock'")

        self._spectrograph = Shamrock(parent=self,
                                      path=self._detector._initpath,
                                      daemon=daemon,
                                      **sp_kwargs)
        self._children.add(self._spectrograph)

        self._spectrograph.position.subscribe(self._onPositionUpdate)
        self.resolution.subscribe(self._onResBinningUpdate)
        self.binning.subscribe(self._onResBinningUpdate, init=True)
Beispiel #5
0
    def __init__(self, name, role, dependencies, **kwargs):
        '''
        dependencies (dict string->model.HwComponent): the dependencies
            There must be exactly two dependencies "spectrograph" and "detector". The
            first dimension of the CCD is supposed to be along the wavelength,
            with the first pixels representing the lowest wavelengths.
        Raise:
          ValueError: if the dependencies are not compatible
        '''
        # we will fill the set of dependencies with Components later in ._dependencies
        model.Detector.__init__(self,
                                name,
                                role,
                                dependencies=dependencies,
                                **kwargs)

        # Check the dependencies
        dt = dependencies["detector"]
        if not isinstance(dt, ComponentBase):
            raise ValueError("Dependency detector is not a component.")
        if ((not hasattr(dt, "shape") or not isinstance(dt.shape, tuple))
                or not model.hasVA(dt, "pixelSize")):
            raise ValueError(
                "Dependency detector is not a Detector component.")
        if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase):
            raise ValueError("Dependency detector has not .data DataFlow.")
        self._detector = dt
        self.dependencies.value.add(dt)

        sp = dependencies["spectrograph"]
        if not isinstance(sp, ComponentBase):
            raise ValueError("Dependency spectrograph is not a component.")
        try:
            if "wavelength" not in sp.axes:
                raise ValueError(
                    "Dependency spectrograph has no 'wavelength' axis.")
        except Exception:
            raise ValueError("Dependency spectrograph is not an Actuator.")
        self._spectrograph = sp
        self.dependencies.value.add(sp)

        # set up the detector part
        # check that the shape is "horizontal"
        if dt.shape[0] <= 1:
            raise ValueError(
                "Dependency detector must have at least 2 pixels horizontally")
        if dt.shape[0] < dt.shape[1]:
            logging.warning(
                "Dependency detector is shaped vertically (%dx%d), "
                "this is probably incorrect, as wavelengths are "
                "expected to be along the horizontal axis", dt.shape[0],
                dt.shape[1])
        # shape is same as detector (raw sensor), but the max resolution is always flat
        self._shape = tuple(dt.shape)  # duplicate

        # Wrapper for the dataflow
        self.data = SpecDataFlow(self, dt.data)

        # The resolution and binning are derived from the detector, but with
        # settings set so that there is only one horizontal line.
        # So max vertical resolution is always 1.
        assert dt.resolution.range[0][1] == 1
        resolution = (dt.resolution.range[1][0], 1)  # max,1
        min_res = (dt.resolution.range[0][0], 1)
        max_res = (dt.resolution.range[1][0], 1)
        self.resolution = model.ResolutionVA(resolution, (min_res, max_res),
                                             setter=self._setResolution)

        # The vertical binning is linked to the detector resolution, as it
        # represents how many pixels are used (and binned). So the maximum
        # binning is the maximum *resolution* (and it's converted either to
        # detector binning, or binned later in software).
        min_bin = (dt.binning.range[0][0], dt.binning.range[0][1])
        max_bin = (dt.binning.range[1][0], dt.resolution.range[1][1])
        # Initial binning is minimum binning horizontally, and maximum vertically
        self._binning = (1, dt.resolution.range[1][1])
        self._max_det_vbin = dt.binning.range[1][1]
        self.binning = model.ResolutionVA(self._binning, (min_bin, max_bin),
                                          setter=self._setBinning)
        self._setBinning(self._binning)  # will also update the resolution

        # TODO: also wrap translation, if it exists?

        # duplicate every other VA and Event from the detector
        # that includes required VAs like .pixelSize and .exposureTime
        for aname, value in chain(
                model.getVAs(dt).items(),
                model.getEvents(dt).items()):
            if not hasattr(self, aname):
                setattr(self, aname, value)
            else:
                logging.debug(
                    "skipping duplication of already existing VA '%s'", aname)

        assert hasattr(self, "pixelSize")
        if not model.hasVA(self, "exposureTime"):
            logging.warning("Spectrometer %s has no exposureTime VA", name)

        sp.position.subscribe(self._onPositionUpdate)
        self.resolution.subscribe(self._onResBinning)
        self.binning.subscribe(self._onResBinning)
        self._updateWavelengthList()
Beispiel #6
0
    def __init__(self, name, role, dependencies, **kwargs):
        '''
        dependencies (dict string->model.HwComponent): the dependencies
            There must be exactly two dependencies "spectrograph" and "detector". The
            first dimension of the CCD is supposed to be along the wavelength,
            with the first pixels representing the lowest wavelengths.
        Raise:
          ValueError: if the dependencies are not compatible
        '''
        # we will fill the set of dependencies with Components later in ._dependencies
        model.Detector.__init__(self, name, role, dependencies=dependencies, **kwargs)

        # Check the dependencies
        dt = dependencies["detector"]
        if not isinstance(dt, ComponentBase):
            raise ValueError("Dependency detector is not a component.")
        if ((not hasattr(dt, "shape") or not isinstance(dt.shape, tuple)) or
            not model.hasVA(dt, "pixelSize")):
            raise ValueError("Dependency detector is not a Detector component.")
        if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase):
            raise ValueError("Dependency detector has not .data DataFlow.")
        self._detector = dt
        self.dependencies.value.add(dt)

        sp = dependencies["spectrograph"]
        if not isinstance(sp, ComponentBase):
            raise ValueError("Dependency spectrograph is not a component.")
        try:
            if "wavelength" not in sp.axes:
                raise ValueError("Dependency spectrograph has no 'wavelength' axis.")
        except Exception:
            raise ValueError("Dependency spectrograph is not an Actuator.")
        self._spectrograph = sp
        self.dependencies.value.add(sp)

        # set up the detector part
        # check that the shape is "horizontal"
        if dt.shape[0] <= 1:
            raise ValueError("Dependency detector must have at least 2 pixels horizontally")
        if dt.shape[0] < dt.shape[1]:
            logging.warning("Dependency detector is shaped vertically (%dx%d), "
                            "this is probably incorrect, as wavelengths are "
                            "expected to be along the horizontal axis",
                            dt.shape[0], dt.shape[1])
        # shape is same as detector (raw sensor), but the max resolution is always flat
        self._shape = tuple(dt.shape) # duplicate

        # Wrapper for the dataflow
        self.data = SpecDataFlow(self, dt.data)

        # The resolution and binning are derived from the detector, but with
        # settings set so that there is only one horizontal line.
        # So max vertical resolution is always 1.
        assert dt.resolution.range[0][1] == 1
        resolution = (dt.resolution.range[1][0], 1)  # max,1
        min_res = (dt.resolution.range[0][0], 1)
        max_res = (dt.resolution.range[1][0], 1)
        self.resolution = model.ResolutionVA(resolution, (min_res, max_res),
                                             setter=self._setResolution)

        # The vertical binning is linked to the detector resolution, as it
        # represents how many pixels are used (and binned). So the maximum
        # binning is the maximum *resolution* (and it's converted either to
        # detector binning, or binned later in software).
        min_bin = (dt.binning.range[0][0], dt.binning.range[0][1])
        max_bin = (dt.binning.range[1][0], dt.resolution.range[1][1])
        # Initial binning is minimum binning horizontally, and maximum vertically
        self._binning = (1, dt.resolution.range[1][1])
        self._max_det_vbin = dt.binning.range[1][1]
        self.binning = model.ResolutionVA(self._binning, (min_bin, max_bin),
                                          setter=self._setBinning)
        self._setBinning(self._binning) # will also update the resolution

        # TODO: also wrap translation, if it exists?

        # duplicate every other VA and Event from the detector
        # that includes required VAs like .pixelSize and .exposureTime
        for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items():
            if not hasattr(self, aname):
                setattr(self, aname, value)
            else:
                logging.debug("skipping duplication of already existing VA '%s'", aname)

        assert hasattr(self, "pixelSize")
        if not model.hasVA(self, "exposureTime"):
            logging.warning("Spectrometer %s has no exposureTime VA", name)

        sp.position.subscribe(self._onPositionUpdate)
        self.resolution.subscribe(self._onResBinning)
        self.binning.subscribe(self._onResBinning)
        self._updateWavelengthList()
Beispiel #7
0
    def __init__(self, name, role, children, settle_time=0, **kwargs):
        '''
        children (dict string->model.HwComponent): the children
            There must be exactly two children "pmt-control" and "detector".
        settle_time (0 < float): time to wait after turning on the gain to have
          it fully working.
        Raise an ValueError exception if the children are not compatible
        '''
        # we will fill the set of children with Components later in ._children
        model.Detector.__init__(self, name, role, **kwargs)

        if settle_time < 0:
            raise ValueError("Settle time of %g s for '%s' is negative"
                             % (settle_time, name))
        elif settle_time > 10:
            # a large value is a sign that the user mistook in units
            raise ValueError("Settle time of %g s for '%s' is too long"
                             % (settle_time, name))
        self._settle_time = settle_time

        # Check the children
        pmt = children["detector"]
        if not isinstance(pmt, ComponentBase):
            raise ValueError("Child detector is not a component.")
        if not hasattr(pmt, "data") or not isinstance(pmt.data, DataFlowBase):
            raise ValueError("Child detector is not a Detector component.")
        self._pmt = pmt
        self.children.value.add(pmt)
        self._shape = pmt.shape
        # copy all the VAs and Events from the PMT to here (but .state and .children).
        pmtVAs = model.getVAs(pmt)
        for key, value in pmtVAs.items():
            setattr(self, key, value)
        pmtEvents = model.getEvents(pmt)
        for key, value in pmtEvents.items():
            setattr(self, key, value)

        ctrl = children["pmt-control"]
        if not isinstance(ctrl, ComponentBase):
            raise ValueError("Child pmt-control is not a component.")
        self._control = ctrl
        self.children.value.add(ctrl)

        self.data = PMTDataFlow(self, self._pmt, self._control)

        # Duplicate control unit VAs
        # In case of counting PMT these VAs are not available since a
        # spectrograph is given instead of the control unit.
        try:
            if (hasattr(ctrl, "gain")
                and isinstance(ctrl.gain, model.VigilantAttributeBase)):
                self._gain = ctrl.gain.range[0]
                self.gain = model.FloatContinuous(self._gain, ctrl.gain.range, unit="V",
                                                  setter=self._setGain)
                self._last_gain = self._gain
                self._setGain(self._gain)  # Just start with no gain
            if (hasattr(ctrl, "powerSupply")
                and isinstance(ctrl.powerSupply, model.VigilantAttributeBase)):
                self.powerSupply = ctrl.powerSupply
                # Turn on the controller
                self.powerSupply.value = True
        except IOError:
            # FIXME: needs to be handled directly by PMTControl (at least automatic reconnect)
            raise HwError("PMT Control Unit connection timeout. "
                          "Please turn off and on the power to the box and "
                          "then restart Odemis.")

        # Protection VA should be available anyway
        if not (hasattr(ctrl, "protection")
            and isinstance(ctrl.protection, model.VigilantAttributeBase)):
            raise IOError("Given component appears to be neither a PMT control "
                          "unit or a spectrograph since protection VA is not "
                          "available.")
Beispiel #8
0
    def __init__(self, name, role, children, **kwargs):
        '''
        children (dict string->model.HwComponent): the children
            There must be exactly two children "spectrograph" and "detector". The 
            first dimension of the CCD is supposed to be along the wavelength,
            with the first pixels representing the lowest wavelengths. 
        Raise an ValueError exception if the children are not compatible
        '''
        # we will fill the set of children with Components later in ._children
        model.Detector.__init__(self, name, role, **kwargs)

        # Check the children
        dt = children["detector"]
        if not isinstance(dt, ComponentBase):
            raise ValueError("Child detector is not a component.")
        if not hasattr(dt, "shape") or not isinstance(dt.shape, tuple):
            raise ValueError("Child detector is not a Detector component.")
        if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase):
            raise ValueError("Child detector is not a Detector component.")
        self._detector = dt
        self.children.add(dt)

        sp = children["spectrograph"]
        if not isinstance(sp, ComponentBase):
            raise ValueError("Child spectrograph is not a component.")
        try:
            if not "wavelength" in sp.axes:
                raise ValueError(
                    "Child spectrograph has no 'wavelength' axis.")
        except Exception:
            raise ValueError("Child spectrograph is not an Actuator.")
        self._spectrograph = sp
        self.children.add(sp)

        # set up the detector part
        # check that the shape is "horizontal"
        if dt.shape[0] <= 1:
            raise ValueError(
                "Child detector must have at least 2 pixels horizontally")
        if dt.shape[0] < dt.shape[1]:
            logging.warning(
                "Child detector is shaped vertically (%dx%d), "
                "this is probably incorrect, as wavelengths are "
                "expected to be along the horizontal axis", dt.shape[0],
                dt.shape[1])
        # shape is same as detector (raw sensor), but the max resolution is always flat
        self._shape = tuple(dt.shape)  # duplicate

        # The resolution and binning are derived from the detector, but with
        # settings set so that there is only one horizontal line.

        # TODO: give a init parameter or VA to specify a smaller window height
        # than the entire CCD (some spectrometers have only noise on the top and
        # bottom)
        if dt.binning.range[1][1] < dt.resolution.range[1][1]:
            # without software binning, we are stuck to the max binning
            logging.info(
                "Spectrometer %s will only use a %d px band of the %d "
                "px of the sensor", name, dt.binning.range[1][1],
                dt.resolution.range[1][1])

        resolution = [dt.resolution.range[1][0], 1]  # max,1
        binning = [1, 1]
        # horizontally: as fine as possible, with a maximum around 256px, over
        #  this, use binning if possible
        binning[0] = min(max(resolution[0] // 256, dt.binning.range[0][0]),
                         dt.binning.range[1][0])
        resolution[0] //= binning[0]

        # vertically: 1, with binning as big as possible
        binning[1] = min(dt.binning.range[1][1], dt.resolution.range[1][1])

        min_res = (dt.resolution.range[0][0], 1)
        max_res = (dt.resolution.range[1][0], 1)
        self.resolution = model.ResolutionVA(resolution, [min_res, max_res],
                                             setter=self._setResolution)
        # 2D binning is like a "small resolution"
        self._binning = binning
        self.binning = model.ResolutionVA(self._binning,
                                          dt.binning.range,
                                          setter=self._setBinning)

        self._setBinning(binning)  # will also update the resolution

        # TODO: support software binning by rolling up our own dataflow that
        # does data merging
        assert dt.resolution.range[0][1] == 1
        self.data = dt.data

        # duplicate every other VA and Event from the detector
        # that includes required VAs like .pixelSize and .exposureTime
        for aname, value in model.getVAs(dt).items() + model.getEvents(
                dt).items():
            if not hasattr(self, aname):
                setattr(self, aname, value)
            else:
                logging.debug(
                    "skipping duplication of already existing VA '%s'", aname)

        assert hasattr(self, "pixelSize")
        assert hasattr(self, "exposureTime")

        # Update metadata of detector with wavelength conversion
        # whenever the wavelength/grating axes moves.
        try:
            self._pn_phys = sp.getPolyToWavelength()
        except AttributeError:
            raise ValueError(
                "Child spectrograph has no getPolyToWavelength() method")

        sp.position.subscribe(self._onPositionUpdate)
        self.resolution.subscribe(self._onResBinningUpdate)
        self.binning.subscribe(self._onResBinningUpdate, init=True)
Beispiel #9
0
    def __init__(self, name, role, children, **kwargs):
        '''
        children (dict string->model.HwComponent): the children
            There must be exactly two children "spectrograph" and "detector". The 
            first dimension of the CCD is supposed to be along the wavelength,
            with the first pixels representing the lowest wavelengths. 
        Raise an ValueError exception if the children are not compatible
        '''
        # we will fill the set of children with Components later in ._children 
        model.Detector.__init__(self, name, role, **kwargs)
        
        # Check the children
        dt = children["detector"]
        if not isinstance(dt, ComponentBase):
            raise ValueError("Child detector is not a component.")
        if not hasattr(dt, "shape") or not isinstance(dt.shape, tuple):
            raise ValueError("Child detector is not a Detector component.")
        if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase):
            raise ValueError("Child detector is not a Detector component.")
        self._detector = dt
        self.children.add(dt)
        
        sp = children["spectrograph"]
        if not isinstance(sp, ComponentBase):
            raise ValueError("Child spectrograph is not a component.")
        try:
            if not "wavelength" in sp.axes:
                raise ValueError("Child spectrograph has no 'wavelength' axis.")
        except Exception:
            raise ValueError("Child spectrograph is not an Actuator.")
        self._spectrograph = sp
        self.children.add(sp)

        # set up the detector part
        # check that the shape is "horizontal"
        if dt.shape[0] <= 1:
            raise ValueError("Child detector must have at least 2 pixels horizontally")
        if dt.shape[0] < dt.shape[1]:
            logging.warning("Child detector is shaped vertically (%dx%d), "
                            "this is probably incorrect, as wavelengths are " 
                            "expected to be along the horizontal axis", 
                            dt.shape[0], dt.shape[1])
        # shape is same as detector (raw sensor), but the max resolution is always flat
        self._shape = tuple(dt.shape) # duplicate
        
        # The resolution and binning are derived from the detector, but with 
        # settings set so that there is only one horizontal line.
        
        # TODO: give a init parameter or VA to specify a smaller window height
        # than the entire CCD (some spectrometers have only noise on the top and
        # bottom)
        if dt.binning.range[1][1] < dt.resolution.range[1][1]:
            # without software binning, we are stuck to the max binning
            logging.info("Spectrometer %s will only use a %d px band of the %d "
                         "px of the sensor", name, dt.binning.range[1][1],
                         dt.resolution.range[1][1])
        
        resolution = [dt.resolution.range[1][0], 1] # max,1
        binning = [1, 1]
        # horizontally: as fine as possible, with a maximum around 256px, over
        #  this, use binning if possible
        binning[0] = min(max(resolution[0] // 256,
                             dt.binning.range[0][0]), dt.binning.range[1][0])
        resolution[0] //= binning[0]
         
        # vertically: 1, with binning as big as possible
        binning[1] = min(dt.binning.range[1][1], dt.resolution.range[1][1]) 
        
        min_res = (dt.resolution.range[0][0], 1)
        max_res = (dt.resolution.range[1][0], 1)
        self.resolution = model.ResolutionVA(resolution, [min_res, max_res], 
                                             setter=self._setResolution)
        # 2D binning is like a "small resolution"
        self._binning = binning
        self.binning = model.ResolutionVA(self._binning, dt.binning.range,
                                          setter=self._setBinning)
   
        self._setBinning(binning) # will also update the resolution
        
        # TODO: support software binning by rolling up our own dataflow that
        # does data merging
        assert dt.resolution.range[0][1] == 1
        self.data = dt.data
        
        # duplicate every other VA and Event from the detector
        # that includes required VAs like .pixelSize and .exposureTime
        for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items():
            if not hasattr(self, aname):
                setattr(self, aname, value)
            else:
                logging.debug("skipping duplication of already existing VA '%s'", aname)

        assert hasattr(self, "pixelSize")
        assert hasattr(self, "exposureTime")

        # Update metadata of detector with wavelength conversion 
        # whenever the wavelength/grating axes moves.
        try:
            self._pn_phys = sp.getPolyToWavelength()
        except AttributeError:
            raise ValueError("Child spectrograph has no getPolyToWavelength() method")

        sp.position.subscribe(self._onPositionUpdate)
        self.resolution.subscribe(self._onResBinningUpdate)
        self.binning.subscribe(self._onResBinningUpdate, init=True) 
Beispiel #10
0
    def __init__(self, name, role, children, **kwargs):
        """
        children (dict string->model.HwComponent): the children
            There must be exactly two children "spectrograph" and "detector". The
            first dimension of the CCD is supposed to be along the wavelength,
            with the first pixels representing the lowest wavelengths.
        Raise:
          ValueError: if the children are not compatible
        """
        # we will fill the set of children with Components later in ._children
        model.Detector.__init__(self, name, role, **kwargs)

        # Check the children
        dt = children["detector"]
        if not isinstance(dt, ComponentBase):
            raise ValueError("Child detector is not a component.")
        if (not hasattr(dt, "shape") or not isinstance(dt.shape, tuple)) or not model.hasVA(dt, "pixelSize"):
            raise ValueError("Child detector is not a Detector component.")
        if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase):
            raise ValueError("Child detector has not .data DataFlow.")
        self._detector = dt
        self.children.value.add(dt)

        sp = children["spectrograph"]
        if not isinstance(sp, ComponentBase):
            raise ValueError("Child spectrograph is not a component.")
        try:
            if "wavelength" not in sp.axes:
                raise ValueError("Child spectrograph has no 'wavelength' axis.")
        except Exception:
            raise ValueError("Child spectrograph is not an Actuator.")
        self._spectrograph = sp
        self.children.value.add(sp)

        # set up the detector part
        # check that the shape is "horizontal"
        if dt.shape[0] <= 1:
            raise ValueError("Child detector must have at least 2 pixels horizontally")
        if dt.shape[0] < dt.shape[1]:
            logging.warning(
                "Child detector is shaped vertically (%dx%d), "
                "this is probably incorrect, as wavelengths are "
                "expected to be along the horizontal axis",
                dt.shape[0],
                dt.shape[1],
            )
        # shape is same as detector (raw sensor), but the max resolution is always flat
        self._shape = tuple(dt.shape)  # duplicate

        # Wrapper for the dataflow
        self.data = SpecDataFlow(self, dt.data)

        # The resolution and binning are derived from the detector, but with
        # settings set so that there is only one horizontal line.

        # TODO: give a init parameter or VA to specify a smaller window height
        # than the entire CCD (some spectrometers have only noise on the top and
        # bottom)
        if dt.binning.range[1][1] < dt.resolution.range[1][1]:
            # without software binning, we are stuck to the max binning
            # TODO: support software binning by rolling up our own dataflow that
            # does data merging
            logging.info(
                "Spectrometer %s will only use a %d px band of the %d " "px of the sensor",
                name,
                dt.binning.range[1][1],
                dt.resolution.range[1][1],
            )

        assert dt.resolution.range[0][1] == 1
        resolution = (dt.resolution.range[1][0], 1)  # max,1
        min_res = (dt.resolution.range[0][0], 1)
        max_res = (dt.resolution.range[1][0], 1)
        self.resolution = model.ResolutionVA(resolution, (min_res, max_res), setter=self._setResolution)
        # 2D binning is like a "small resolution"
        # Initial binning is minimum binning horizontally, and maximum vertically
        self._binning = (1, min(dt.binning.range[1][1], dt.resolution.range[1][1]))
        self.binning = model.ResolutionVA(self._binning, dt.binning.range, setter=self._setBinning)

        self._setBinning(self._binning)  # will also update the resolution

        # TODO: also wrap translation, if it exists?

        # duplicate every other VA and Event from the detector
        # that includes required VAs like .pixelSize and .exposureTime
        for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items():
            if not hasattr(self, aname):
                setattr(self, aname, value)
            else:
                logging.debug("skipping duplication of already existing VA '%s'", aname)

        assert hasattr(self, "pixelSize")
        if not model.hasVA(self, "exposureTime"):
            logging.warning("Spectrometer %s has no exposureTime VA", name)

        sp.position.subscribe(self._onPositionUpdate)
        self.resolution.subscribe(self._onResBinning)
        self.binning.subscribe(self._onResBinning)
        self._updateWavelengthList()
Beispiel #11
0
    def __init__(self, name, role, children=None, daemon=None, **kwargs):
        """
        All the arguments are identical to AndorCam2, expected: 
        children (dict string->kwargs): name of child must be "shamrock" and the
          kwargs contains the arguments passed to instantiate the Shamrock component
        """
        # we will fill the set of children with Components later in ._children
        model.Detector.__init__(self, name, role, daemon=daemon, **kwargs)

        # Create the detector (ccd) child
        try:
            dt_kwargs = children["andorcam2"]
        except Exception:
            raise ValueError("AndorSpec excepts one child named 'andorcam2'")

        # We could inherit from it, but difficult to not mix up .binning, .shape
        # .resolution...
        self._detector = andorcam2.AndorCam2(parent=self, daemon=daemon, **dt_kwargs)
        self.children.value.add(self._detector)
        dt = self._detector

        # Copy and adapt the VAs and roattributes from the detector
                # set up the detector part
        # check that the shape is "horizontal"
        if dt.shape[0] <= 1:
            raise ValueError("Child detector must have at least 2 pixels horizontally")
        if dt.shape[0] < dt.shape[1]:
            logging.warning("Child detector is shaped vertically (%dx%d), "
                            "this is probably incorrect, as wavelengths are "
                            "expected to be along the horizontal axis",
                            dt.shape[0], dt.shape[1])
        # shape is same as detector (raw sensor), but the max resolution is always flat
        self._shape = tuple(dt.shape) # duplicate

        # The resolution and binning are derived from the detector, but with
        # settings set so that there is only one horizontal line.
        if dt.binning.range[1][1] < dt.resolution.range[1][1]:
            # without software binning, we are stuck to the max binning
            logging.info("Spectrometer %s will only use a %d px band of the %d "
                         "px of the sensor", name, dt.binning.range[1][1],
                         dt.resolution.range[1][1])

        resolution = (dt.resolution.range[1][0], 1) # max,1
        # vertically: 1, with binning as big as possible
        binning = (dt.binning.value[0],
                   min(dt.binning.range[1][1], dt.resolution.range[1][1]))

        min_res = (dt.resolution.range[0][0], 1)
        max_res = (dt.resolution.range[1][0], 1)
        self.resolution = model.ResolutionVA(resolution, [min_res, max_res],
                                             setter=self._setResolution)
        # 2D binning is like a "small resolution"
        self._binning = binning
        self.binning = model.ResolutionVA(self._binning, dt.binning.range,
                                          setter=self._setBinning)

        self._setBinning(binning) # will also update the resolution

        # TODO: update also the metadata MD_SENSOR_PIXEL_SIZE
        pxs = dt.pixelSize.value[0], dt.pixelSize.value[1] * dt.binning.value[1]
        self.pixelSize = model.VigilantAttribute(pxs, unit="m", readonly=True)
        # Note: the metadata has no MD_PIXEL_SIZE, but a MD_WL_LIST

        # TODO: support software binning by rolling up our own dataflow that
        # does data merging
        assert dt.resolution.range[0][1] == 1
        self.data = dt.data

        # duplicate every other VA and Event from the detector
        # that includes required VAs like .exposureTime
        for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items():
            if not hasattr(self, aname):
                setattr(self, aname, value)
            else:
                logging.debug("skipping duplication of already existing VA '%s'", aname)

        assert hasattr(self, "exposureTime")

        # Create the spectrograph (actuator) child
        try:
            sp_kwargs = children["shamrock"]
        except Exception:
            raise ValueError("AndorSpec excepts one child named 'shamrock'")

        self._spectrograph = Shamrock(parent=self, camera=self._detector,
                                      path=self._detector._initpath,
                                      daemon=daemon, **sp_kwargs)
        self.children.value.add(self._spectrograph)

        self._spectrograph.position.subscribe(self._onPositionUpdate)
        self.resolution.subscribe(self._onResBinningUpdate)
        self.binning.subscribe(self._onResBinningUpdate, init=True)
Beispiel #12
0
    def __init__(self, name, role, children, settle_time=0, **kwargs):
        '''
        children (dict string->model.HwComponent): the children
            There must be exactly two children "pmt-control" and "detector".
        settle_time (0 < float): time to wait after turning on the gain to have
          it fully working.
        Raise an ValueError exception if the children are not compatible
        '''
        # we will fill the set of children with Components later in ._children
        model.Detector.__init__(self, name, role, **kwargs)

        if settle_time < 0:
            raise ValueError("Settle time of %g s for '%s' is negative" %
                             (settle_time, name))
        elif settle_time > 10:
            # a large value is a sign that the user mistook in units
            raise ValueError("Settle time of %g s for '%s' is too long" %
                             (settle_time, name))
        self._settle_time = settle_time

        # Check the children
        pmt = children["detector"]
        if not isinstance(pmt, ComponentBase):
            raise ValueError("Child detector is not a component.")
        if not hasattr(pmt, "data") or not isinstance(pmt.data, DataFlowBase):
            raise ValueError("Child detector is not a Detector component.")
        self._pmt = pmt
        self.children.value.add(pmt)
        self._shape = pmt.shape
        # copy all the VAs and Events from the PMT to here (but .state and .children).
        pmtVAs = model.getVAs(pmt)
        for key, value in pmtVAs.items():
            setattr(self, key, value)
        pmtEvents = model.getEvents(pmt)
        for key, value in pmtEvents.items():
            setattr(self, key, value)

        ctrl = children["pmt-control"]
        if not isinstance(ctrl, ComponentBase):
            raise ValueError("Child pmt-control is not a component.")
        self._control = ctrl
        self.children.value.add(ctrl)

        self.data = PMTDataFlow(self, self._pmt, self._control)

        # Duplicate control unit VAs
        # In case of counting PMT these VAs are not available since a
        # spectrograph is given instead of the control unit.
        try:
            if (hasattr(ctrl, "gain")
                    and isinstance(ctrl.gain, model.VigilantAttributeBase)):
                self._gain = ctrl.gain.range[0]
                self.gain = model.FloatContinuous(self._gain,
                                                  ctrl.gain.range,
                                                  unit="V",
                                                  setter=self._setGain)
                self._last_gain = self._gain
                self._setGain(self._gain)  # Just start with no gain
            if (hasattr(ctrl, "powerSupply") and isinstance(
                    ctrl.powerSupply, model.VigilantAttributeBase)):
                self.powerSupply = ctrl.powerSupply
                # Turn on the controller
                self.powerSupply.value = True
        except IOError:
            # FIXME: needs to be handled directly by PMTControl (at least automatic reconnect)
            raise HwError("PMT Control Unit connection timeout. "
                          "Please turn off and on the power to the box and "
                          "then restart Odemis.")

        # Protection VA should be available anyway
        if not (hasattr(ctrl, "protection")
                and isinstance(ctrl.protection, model.VigilantAttributeBase)):
            raise IOError(
                "Given component appears to be neither a PMT control "
                "unit or a spectrograph since protection VA is not "
                "available.")
Beispiel #13
0
    def __init__(self, name, role, children, **kwargs):
        '''
        children (dict string->model.HwComponent): the children
            There must be exactly two children "spectrograph" and "detector". The
            first dimension of the CCD is supposed to be along the wavelength,
            with the first pixels representing the lowest wavelengths.
        Raise:
          ValueError: if the children are not compatible
        '''
        # we will fill the set of children with Components later in ._children
        model.Detector.__init__(self, name, role, **kwargs)

        # Check the children
        dt = children["detector"]
        if not isinstance(dt, ComponentBase):
            raise ValueError("Child detector is not a component.")
        if ((not hasattr(dt, "shape") or not isinstance(dt.shape, tuple)) or
            not model.hasVA(dt, "pixelSize")):
            raise ValueError("Child detector is not a Detector component.")
        if not hasattr(dt, "data") or not isinstance(dt.data, DataFlowBase):
            raise ValueError("Child detector has not .data DataFlow.")
        self._detector = dt
        self.children.value.add(dt)

        sp = children["spectrograph"]
        if not isinstance(sp, ComponentBase):
            raise ValueError("Child spectrograph is not a component.")
        try:
            if "wavelength" not in sp.axes:
                raise ValueError("Child spectrograph has no 'wavelength' axis.")
        except Exception:
            raise ValueError("Child spectrograph is not an Actuator.")
        self._spectrograph = sp
        self.children.value.add(sp)

        # set up the detector part
        # check that the shape is "horizontal"
        if dt.shape[0] <= 1:
            raise ValueError("Child detector must have at least 2 pixels horizontally")
        if dt.shape[0] < dt.shape[1]:
            logging.warning("Child detector is shaped vertically (%dx%d), "
                            "this is probably incorrect, as wavelengths are "
                            "expected to be along the horizontal axis",
                            dt.shape[0], dt.shape[1])
        # shape is same as detector (raw sensor), but the max resolution is always flat
        self._shape = tuple(dt.shape) # duplicate

        # Wrapper for the dataflow
        self.data = SpecDataFlow(self, dt.data)

        # The resolution and binning are derived from the detector, but with
        # settings set so that there is only one horizontal line.

        # TODO: give a init parameter or VA to specify a smaller window height
        # than the entire CCD (some spectrometers have only noise on the top and
        # bottom)
        if dt.binning.range[1][1] < dt.resolution.range[1][1]:
            # without software binning, we are stuck to the max binning
            # TODO: support software binning by rolling up our own dataflow that
            # does data merging
            logging.info("Spectrometer %s will only use a %d px band of the %d "
                         "px of the sensor", name, dt.binning.range[1][1],
                         dt.resolution.range[1][1])

        assert dt.resolution.range[0][1] == 1
        resolution = (dt.resolution.range[1][0], 1)  # max,1
        min_res = (dt.resolution.range[0][0], 1)
        max_res = (dt.resolution.range[1][0], 1)
        self.resolution = model.ResolutionVA(resolution, (min_res, max_res),
                                             setter=self._setResolution)
        # 2D binning is like a "small resolution"
        # Initial binning is minimum binning horizontally, and maximum vertically
        self._binning = (1, min(dt.binning.range[1][1], dt.resolution.range[1][1]))
        self.binning = model.ResolutionVA(self._binning, dt.binning.range,
                                          setter=self._setBinning)

        self._setBinning(self._binning) # will also update the resolution

        # TODO: also wrap translation, if it exists?

        # duplicate every other VA and Event from the detector
        # that includes required VAs like .pixelSize and .exposureTime
        for aname, value in model.getVAs(dt).items() + model.getEvents(dt).items():
            if not hasattr(self, aname):
                setattr(self, aname, value)
            else:
                logging.debug("skipping duplication of already existing VA '%s'", aname)

        assert hasattr(self, "pixelSize")
        if not model.hasVA(self, "exposureTime"):
            logging.warning("Spectrometer %s has no exposureTime VA", name)

        sp.position.subscribe(self._onPositionUpdate)
        self.resolution.subscribe(self._onResBinning)
        self.binning.subscribe(self._onResBinning)
        self._updateWavelengthList()