Beispiel #1
0
    def addSettings(self, objWithVA, conf=None):
        """
        Adds settings as one widget on a line for each VigilantAttribute (VA) in
         the given object. Each setting entry created is added to .entries.
        objWithVA (object): an object with VAs.

        conf (None or dict of str->config): allows to override the automatic
          selection of the VA widget. See odemis.gui.conf.data for documentation.

        raise:
            LookupError: if no VA is found on the objWithVA

        """
        vas = getVAs(objWithVA)
        if not vas:
            raise LookupError("No VAs found!")

        if not conf:
            conf = {}
        vas_names = util.sorted_according_to(vas.keys(), conf.keys())

        for name in vas_names:
            va = vas[name]
            self.setting_controller.add_setting_entry(name,
                                                      va,
                                                      None,
                                                      conf=conf.get(
                                                          name, None))
Beispiel #2
0
    def add_hw_component(self, hw_comp, setting_controller, hidden=None):
        """ Add setting entries for the given hardware component

        hidden (None or set of str): name of VAs to not show

        """

        hidden = HIDDEN_VAS | (hidden or set())
        self.setting_controllers.append(setting_controller)

        vas_comp = getVAs(hw_comp)
        vas_config = get_hw_config(
            hw_comp, self._hw_settings_config)  # OrderedDict or dict

        # Re-order the VAs of the component in the same order as in the config
        vas_names = util.sorted_according_to(list(vas_comp.keys()),
                                             list(vas_config.keys()))

        for name in vas_names:
            try:
                if name in hidden:
                    continue
                elif name in vas_config:
                    va_conf = vas_config[name]
                else:
                    logging.debug("No config found for %s: %s", hw_comp.role,
                                  name)
                    va_conf = None
                va = vas_comp[name]
                setting_controller.add_setting_entry(name, va, hw_comp,
                                                     va_conf)
            except TypeError:
                msg = "Error adding %s setting for: %s"
                logging.exception(msg, hw_comp.name, name)
Beispiel #3
0
def get_local_vas(hw_comp, hw_settings):
    """
    Find all the VAs of a component which are worthy to become local VAs.

    hw_comp (HwComponent): The component to look at
    hw_settings (dict): the hardware settings, as received from get_hw_settings_config()

    return (set of str): all the names for the given comp
    """
    comp_vas = getVAs(hw_comp)
    config_vas = get_hw_config(hw_comp, hw_settings)

    settings = set()
    for name, va in comp_vas.items():
        # Take all VAs that  would be displayed on the stream panel
        if name in HIDDEN_VAS or va.readonly:
            continue
        try:
            ctyp = config_vas[name]["control_type"]
            if ctyp == odemis.gui.CONTROL_NONE:
                continue
        except KeyError:
            # not in config => it'll be displayed
            pass

        settings.add(name)

    return settings
Beispiel #4
0
def get_local_vas(hw_comp, hw_settings):
    """
    Find all the VAs of a component which are worthy to become local VAs.

    hw_comp (HwComponent): The component to look at
    hw_settings (dict): the hardware settings, as received from get_hw_settings_config()

    return (set of str): all the names for the given comp
    """
    comp_vas = getVAs(hw_comp)
    config_vas = get_hw_config(hw_comp, hw_settings)

    settings = set()
    for name, va in comp_vas.items():
        # Take all VAs that  would be displayed on the stream panel
        if name in HIDDEN_VAS or va.readonly:
            continue
        try:
            ctyp = config_vas[name]["control_type"]
            if ctyp == odemis.gui.CONTROL_NONE:
                continue
        except KeyError:
            # not in config => it'll be displayed
            pass

        settings.add(name)

    return settings
Beispiel #5
0
def get_local_vas(hw_comp, hidden=None):
    """
    Find all the VAs of a component which are worthy to become local VAs.

    :param hw_comp: (HwComponent)
    :param hidden: (set) Name of VAs to ignore

    return (set of str): all the names for the given comp
    """

    config = get_hw_settings_config()
    hidden_vas = HIDDEN_VAS | (hidden or set())

    comp_vas = getVAs(hw_comp)
    config_vas = config.get(hw_comp.role, {}) # OrderedDict or dict

    settings = set()
    for name, va in comp_vas.items():
        # Take all VAs that  would be displayed on the stream panel
        if name in hidden_vas or va.readonly:
            continue
        try:
            ctyp = config_vas[name]["control_type"]
            if ctyp == odemis.gui.CONTROL_NONE:
                continue
        except KeyError:
            # not in config => it'll be displayed
            pass

        settings.add(name)

    return settings
Beispiel #6
0
    def __init__(self, components):
        """
        components (set of HwComponents): component which should be observed
        """
        self._all_settings = {}
        self._components = components  # keep a reference to the components, so they are not garbage collected
        self._va_updaters = [
        ]  # keep a reference to the subscribers so they are not garbage collected

        for comp in components:
            self._all_settings[comp.name] = {}
            vas = model.getVAs(comp).items()
            prepare_to_listen_to_more_vas(len(vas))

            for va_name, va in vas:
                if va_name in HIDDEN_VAS:
                    continue
                # Store current value of VA (calling .value might take some time)
                self._all_settings[comp.name][va_name] = [va.value, va.unit]

                # Subscribe to VA, update dictionary on callback
                def update_settings(value,
                                    comp_name=comp.name,
                                    va_name=va_name):
                    self._all_settings[comp_name][va_name][0] = value

                self._va_updaters.append(update_settings)
                va.subscribe(update_settings)
Beispiel #7
0
    def __init__(self, btn_ctrl, va, main_data):
        """
        :type btn_ctrl: odemis.gui.comp.buttons.ImageTextToggleButton

        """
        super(ChamberButtonController, self).__init__(btn_ctrl, va, main_data)
        self.main_data = main_data

        # If pressure information, assume it is a complete SEM chamber,
        # otherwise assume it uses a sample loader like the Phenom or Delphi.
        if 'pressure' in getVAs(main_data.chamber):
            main_data.chamber.pressure.subscribe(self._update_label, init=True)
            self._btn_faces = {
                'normal': {
                    'normal': imgdata.btn_press.Bitmap,
                    'hover': imgdata.btn_press_h.Bitmap,
                    'active': imgdata.btn_press_a.Bitmap,
                },
                'working': {
                    'normal': imgdata.btn_press_orange.Bitmap,
                    'hover': imgdata.btn_press_orange_h.Bitmap,
                    'active': imgdata.btn_press_orange_a.Bitmap,
                },
                'vacuum': {
                    'normal': imgdata.btn_press_green.Bitmap,
                    'hover': imgdata.btn_press_green_h.Bitmap,
                    'active': imgdata.btn_press_green_a.Bitmap,
                }
            }
            self._tooltips = {
                CHAMBER_PUMPING: "Pumping...",
                CHAMBER_VENTING: "Venting...",
                CHAMBER_VENTED: "Pump the chamber",
                CHAMBER_VACUUM: "Vent the chamber",
            }
        else:
            self.btn.SetLabel("CHAMBER")
            self._btn_faces = {
                'normal': {
                    'normal': imgdata.btn_eject.Bitmap,
                    'hover': imgdata.btn_eject_h.Bitmap,
                    'active': imgdata.btn_eject_a.Bitmap,
                },
                'working': {
                    'normal': imgdata.btn_eject_orange.Bitmap,
                    'hover': imgdata.btn_eject_orange_h.Bitmap,
                    'active': imgdata.btn_eject_orange_a.Bitmap,
                },
                'vacuum': {
                    'normal': imgdata.btn_eject_green.Bitmap,
                    'hover': imgdata.btn_eject_green_h.Bitmap,
                    'active': imgdata.btn_eject_green_a.Bitmap,
                }
            }
            self._tooltips = {
                CHAMBER_PUMPING: "Loading...",
                CHAMBER_VENTING: "Ejecting...",
                CHAMBER_VENTED: "Load the sample",
                CHAMBER_VACUUM: "Eject the sample",
            }
Beispiel #8
0
    def add_hw_component(self, hw_comp, setting_controller, hidden=None):
        """ Add setting entries for the given hardware component

        hidden (None or set of str): name of VAs to not show

        """

        hidden = HIDDEN_VAS | (hidden or set())
        self.setting_controllers.append(setting_controller)

        vas_comp = getVAs(hw_comp)
        vas_config = get_hw_config(hw_comp, self._hw_settings_config)  # OrderedDict or dict

        # Re-order the VAs of the component in the same order as in the config
        vas_names = util.sorted_according_to(vas_comp.keys(), vas_config.keys())

        for name in vas_names:
            try:
                if name in hidden:
                    continue
                elif name in vas_config:
                    va_conf = vas_config[name]
                else:
                    logging.debug("No config found for %s: %s", hw_comp.role, name)
                    va_conf = None
                va = vas_comp[name]
                setting_controller.add_setting_entry(name, va, hw_comp, va_conf)
            except TypeError:
                msg = "Error adding %s setting for: %s"
                logging.exception(msg, hw_comp.name, name)
Beispiel #9
0
def get_local_vas(hw_comp, hidden=None):
    """
    Find all the VAs of a component which are worthy to become local VAs.

    :param hw_comp: (HwComponent)
    :param hidden: (set) Name of VAs to ignore

    return (set of str): all the names for the given comp
    """

    config = get_hw_settings_config()
    hidden_vas = HIDDEN_VAS | (hidden or set())

    comp_vas = getVAs(hw_comp)
    config_vas = config.get(hw_comp.role, {})  # OrderedDict or dict

    settings = set()
    for name, va in comp_vas.items():
        # Take all VAs that  would be displayed on the stream panel
        if name in hidden_vas or va.readonly:
            continue
        try:
            ctyp = config_vas[name]["control_type"]
            if ctyp == odemis.gui.CONTROL_NONE:
                continue
        except KeyError:
            # not in config => it'll be displayed
            pass

        settings.add(name)

    return settings
Beispiel #10
0
    def addSettings(self, objWithVA, conf=None):
        """
        Adds settings as one widget on a line for each VigilantAttribute (VA) in
         the given object. Each setting entry created is added to .entries.
        objWithVA (object): an object with VAs.

        conf (None or dict of str->config): allows to override the automatic
          selection of the VA widget. See odemis.gui.conf.data for documentation.

        raise:
            LookupError: if no VA is found on the objWithVA

        """
        vas = getVAs(objWithVA)
        if not vas:
            raise LookupError("No VAs found!")

        if not conf:
            conf = {}
        vas_names = util.sorted_according_to(vas.keys(), conf.keys())

        for name in vas_names:
            va = vas[name]
            self.setting_controller.add_setting_entry(name, va, None,
                                                      conf=conf.get(name, None))
Beispiel #11
0
 def _get_settings_vas(self, stream):
     """
     Find all the VAs of a stream which can potentially affect the acquisition time
     return (set of VAs)
     """
     nvas = model.getVAs(stream) # name -> va
     vas = set()
     # remove some VAs known to not affect the acquisition time
     for n, va in nvas.items():
         if n not in self.VAS_NO_ACQUSITION_EFFECT:
             vas.add(va)
     return vas
Beispiel #12
0
 def _get_settings_vas(self, stream):
     """
     Find all the VAs of a stream which can potentially affect the acquisition time
     return (set of VAs)
     """
     nvas = model.getVAs(stream)  # name -> va
     vas = set()
     # remove some VAs known to not affect the acquisition time
     for n, va in nvas.items():
         if n not in self.VAS_NO_ACQUSITION_EFFECT:
             vas.add(va)
     return vas
Beispiel #13
0
def _speedUpPyroVAConnect(comp):
    """
    Ensures that all the VAs of the component will be quick to access
    comp (Component)
    """
    # Force the creation of the connection
    # If the connection already exists it's very fast, otherwise, we wait
    # for the connection to be created in a separate thread

    for name, va in model.getVAs(comp).items():
        t = threading.Thread(name="Connection to VA %s.%s" % (comp.name, name),
                             target=va._pyroBind)
        t.daemon = True
        t.start()
Beispiel #14
0
def _speedUpPyroVAConnect(comp):
    """
    Ensures that all the VAs of the component will be quick to access
    comp (Component)
    """
    # Force the creation of the connection
    # If the connection already exists it's very fast, otherwise, we wait
    # for the connection to be created in a separate thread

    for name, va in model.getVAs(comp).items():
        t = threading.Thread(name="Connection to VA %s.%s" % (comp.name, name),
                             target=va._pyroBind)
        t.daemon = True
        t.start()
Beispiel #15
0
    def _set_fan(self, enable):
        """
        Turn on/off the fan of the CCD
        enable (boolean): True to turn on/restore the fan, and False to turn if off
        """
        main_data = self._tab_data_model.main
        if "fanSpeed" not in model.getVAs(main_data.ccd):
            return

        fs = main_data.ccd.fanSpeed
        if enable:
            if self._orig_fan_speed is not None:
                fs.value = max(fs.value, self._orig_fan_speed)
        else:
            self._orig_fan_speed = fs.value
            fs.value = 0
Beispiel #16
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 #17
0
def print_vattributes(component, pretty):
    for name, value in model.getVAs(component).items():
        print_vattribute(name, value, pretty)
Beispiel #18
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 #19
0
def print_vattributes(component, pretty):
    for name, value in model.getVAs(component).items():
        if name in special_va_names:
            continue
        print_vattribute(name, value, pretty)
Beispiel #20
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 #21
0
def print_vattributes(component, pretty):
    for name, value in model.getVAs(component).items():
        print_vattribute(name, value, pretty)
Beispiel #22
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 #23
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 #24
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 #25
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 #26
0
def print_vattributes(component, pretty):
    for name, value in model.getVAs(component).items():
        if name in VAS_HIDDEN:
            continue
        print_vattribute(name, value, pretty)
Beispiel #27
0
def print_vattributes(component, pretty):
    for name, value in model.getVAs(component).items():
        if name in special_va_names:
            continue
        print_vattribute(name, value, pretty)
Beispiel #28
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 #29
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 #30
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 #31
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()