def print_events(component, pretty): # find all Events for name, value in model.getEvents(component).items(): print_event(name, value, pretty)
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)
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)
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()
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()
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.")
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)
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)
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()
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)
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.")
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()