コード例 #1
0
 def get_scan_data(self, frame_parameters, channel) -> numpy.ndarray:
     size = Geometry.IntSize.make(frame_parameters.subscan_pixel_size if frame_parameters.subscan_pixel_size else frame_parameters.size)
     offset_m = self.stage_position_m - self.GetVal2D("beam_shift_m")
     fov_size_nm = Geometry.FloatSize.make(frame_parameters.fov_size_nm) if frame_parameters.fov_size_nm else Geometry.FloatSize(frame_parameters.fov_nm, frame_parameters.fov_nm)
     if frame_parameters.subscan_fractional_size:
         subscan_fractional_size = Geometry.FloatSize.make(frame_parameters.subscan_fractional_size)
         used_fov_size_nm = Geometry.FloatSize(height=fov_size_nm.height * subscan_fractional_size.height,
                                               width=fov_size_nm.width * subscan_fractional_size.width)
     else:
         used_fov_size_nm = fov_size_nm
     center_nm = Geometry.FloatPoint.make(frame_parameters.center_nm)
     if frame_parameters.subscan_fractional_center:
         subscan_fractional_center = Geometry.FloatPoint.make(frame_parameters.subscan_fractional_center)
         center_nm += Geometry.FloatPoint(y=(subscan_fractional_center.y - 0.5) * fov_size_nm.height,
                                          x=(subscan_fractional_center.x - 0.5) * fov_size_nm.width)
     extra = int(math.ceil(max(size.height * math.sqrt(2) - size.height, size.width * math.sqrt(2) - size.width)))
     extra_nm = Geometry.FloatPoint(y=(extra / size.height) * used_fov_size_nm[0], x=(extra / size.width) * used_fov_size_nm[1])
     used_size = size + Geometry.IntSize(height=extra, width=extra)
     data = numpy.zeros((used_size.height, used_size.width), numpy.float32)
     self.sample.plot_features(data, offset_m, used_fov_size_nm, extra_nm, center_nm, used_size)
     noise_factor = 0.3
     if frame_parameters.rotation_rad != 0:
         inner_height = size.height / used_size.height
         inner_width = size.width / used_size.width
         inner_bounds = ((1.0 - inner_height) * 0.5, (1.0 - inner_width) * 0.5), (inner_height, inner_width)
         data = Core.function_crop_rotated(DataAndMetadata.new_data_and_metadata(data), inner_bounds, -frame_parameters.rotation_rad).data
         # TODO: data is not always the correct size
     else:
         data = data[extra // 2:extra // 2 + size.height, extra // 2:extra // 2 + size.width]
     return (data + numpy.random.randn(size.height, size.width) * noise_factor) * frame_parameters.pixel_time_us
コード例 #2
0
 def test_eels_data_thickness_is_consistent(self):
     instrument = InstrumentDevice.Instrument("usim_stem_controller")
     # use the flake sample
     instrument.sample_index = 0
     # set up the scan context; these are here temporarily until the scan context architecture is fully implemented
     instrument._update_scan_context(Geometry.IntSize(256, 256),
                                     Geometry.FloatPoint(), 10, 0.0)
     instrument._set_scan_context_probe_position(
         instrument.scan_context, Geometry.FloatPoint(0.5, 0.5))
     # grab scan data
     instrument.get_scan_data(
         scan_base.ScanFrameParameters({
             "size": (256, 256),
             "pixel_time_us": 1,
             "fov_nm": 10
         }), 0)
     instrument.validate_probe_position()
     camera = instrument._get_camera_simulator("eels")
     camera_size = camera._camera_shape
     camera.noise.enabled = False
     readout_area = Geometry.IntRect(origin=Geometry.IntPoint(),
                                     size=camera_size)
     binning_shape = Geometry.IntSize(1, 1)
     # get the value at 200eV and ZLP offset of 0
     instrument.ZLPoffset = -20
     d = xd.sum(instrument.get_camera_data("eels", readout_area,
                                           binning_shape, 0.01),
                axis=0).data
     # confirm it is a reasonable value
     # print(measure_thickness(d))
     self.assertTrue(0.40 < measure_thickness(d) < 1.00)
コード例 #3
0
 def test_eels_data_camera_current_is_consistent(self):
     instrument = InstrumentDevice.Instrument("usim_stem_controller")
     # set up the scan context; these are here temporarily until the scan context architecture is fully implemented
     instrument._update_scan_context(Geometry.IntSize(256, 256),
                                     Geometry.FloatPoint(), 10, 0.0)
     instrument._set_scan_context_probe_position(
         instrument.scan_context, Geometry.FloatPoint(0.5, 0.5))
     # grab scan data
     instrument.get_scan_data(
         scan_base.ScanFrameParameters({
             "size": (256, 256),
             "pixel_time_us": 1,
             "fov_nm": 10
         }), 0)
     instrument.validate_probe_position()
     camera = instrument._get_camera_simulator("eels")
     camera_size = camera._camera_shape
     camera.noise.enabled = False
     readout_area = Geometry.IntRect(origin=Geometry.IntPoint(),
                                     size=camera_size)
     binning_shape = Geometry.IntSize(1, 1)
     # get the value at 200eV and ZLP offset of 0
     instrument.ZLPoffset = -20
     exposure_s = 0.01
     d = xd.sum(instrument.get_camera_data("eels", readout_area,
                                           binning_shape, exposure_s),
                axis=0).data
     # confirm it is a reasonable value
     camera_current_pA = numpy.sum(
         d) / exposure_s / instrument.counts_per_electron / 6.242e18 * 1e12
     # print(f"current {camera_current_pA :#.2f}pA")
     self.assertTrue(190 < camera_current_pA < 210)
コード例 #4
0
    def _get_stem_controller_and_camera(self,
                                        initialize: bool = True,
                                        is_eels: bool = False):
        # this is simulator specific. replace this code but be sure to set up self.exposure and blanked and positioned
        # initial settings.
        self.exposure = 0.04

        instrument = InstrumentDevice.Instrument("usim_stem_controller")
        Registry.register_component(instrument, {"stem_controller"})

        camera_id = "usim_ronchigram_camera" if not is_eels else "usim_eels_camera"
        camera_type = "ronchigram" if not is_eels else "eels"
        camera_name = "uSim Camera"
        camera_settings = CameraDevice.CameraSettings(camera_id)
        camera_device = CameraDevice.Camera(camera_id, camera_type,
                                            camera_name, instrument)
        camera_hardware_source = camera_base.CameraHardwareSource(
            "usim_stem_controller", camera_device, camera_settings, None, None)
        if is_eels:
            camera_hardware_source.features["is_eels_camera"] = True
            camera_hardware_source.add_channel_processor(
                0, HardwareSource.SumProcessor(((0.25, 0.0), (0.5, 1.0))))
            # EELS camera only produces data if a valid scan context is presend in intrument controller, so set one up here
            scan_context = stem_controller.ScanContext()
            scan_context.update(Geometry.IntSize(128, 128),
                                Geometry.FloatPoint(), 10.0, 0.0)
            # This is the only method that allows access to the scan context
            instrument._set_scan_context_probe_position(
                scan_context, Geometry.FloatPoint(0.5, 0.5))
        camera_hardware_source.set_frame_parameters(
            0,
            camera_base.CameraFrameParameters({
                "exposure_ms": self.exposure * 1000,
                "binning": 2
            }))
        camera_hardware_source.set_frame_parameters(
            1,
            camera_base.CameraFrameParameters({
                "exposure_ms": self.exposure * 1000,
                "binning": 2
            }))
        camera_hardware_source.set_frame_parameters(
            2,
            camera_base.CameraFrameParameters({
                "exposure_ms":
                self.exposure * 1000 * 2,
                "binning":
                1
            }))
        camera_hardware_source.set_selected_profile_index(0)

        HardwareSource.HardwareSourceManager().register_hardware_source(
            camera_hardware_source)

        return instrument, camera_hardware_source
コード例 #5
0
 def get_scan_data(self, frame_parameters, channel) -> numpy.ndarray:
     size = Geometry.IntSize.make(
         frame_parameters.subscan_pixel_size if frame_parameters.
         subscan_pixel_size else frame_parameters.size)
     offset_m = self.actual_offset_m  # stage position - beam shift + drift
     fov_size_nm = Geometry.FloatSize.make(
         frame_parameters.fov_size_nm
     ) if frame_parameters.fov_size_nm else Geometry.FloatSize(
         frame_parameters.fov_nm, frame_parameters.fov_nm)
     if frame_parameters.subscan_fractional_size:
         subscan_fractional_size = Geometry.FloatSize.make(
             frame_parameters.subscan_fractional_size)
         used_fov_size_nm = Geometry.FloatSize(
             height=fov_size_nm.height * subscan_fractional_size.height,
             width=fov_size_nm.width * subscan_fractional_size.width)
     else:
         used_fov_size_nm = fov_size_nm
     center_nm = Geometry.FloatPoint.make(frame_parameters.center_nm)
     if frame_parameters.subscan_fractional_center:
         subscan_fractional_center = Geometry.FloatPoint.make(
             frame_parameters.subscan_fractional_center
         ) - Geometry.FloatPoint(y=0.5, x=0.5)
         fc = subscan_fractional_center.rotate(
             frame_parameters.rotation_rad)
         center_nm += Geometry.FloatPoint(y=fc.y * fov_size_nm.height,
                                          x=fc.x * fov_size_nm.width)
     extra = int(
         math.ceil(
             max(size.height * math.sqrt(2) - size.height,
                 size.width * math.sqrt(2) - size.width)))
     extra_nm = Geometry.FloatPoint(
         y=(extra / size.height) * used_fov_size_nm[0],
         x=(extra / size.width) * used_fov_size_nm[1])
     used_size = size + Geometry.IntSize(height=extra, width=extra)
     data = numpy.zeros((used_size.height, used_size.width), numpy.float32)
     self.sample.plot_features(data, offset_m, used_fov_size_nm, extra_nm,
                               center_nm, used_size)
     noise_factor = 0.3
     total_rotation = frame_parameters.rotation_rad
     if frame_parameters.subscan_rotation:
         total_rotation -= frame_parameters.subscan_rotation
     if total_rotation != 0:
         inner_height = size.height / used_size.height
         inner_width = size.width / used_size.width
         inner_bounds = ((1.0 - inner_height) * 0.5,
                         (1.0 - inner_width) * 0.5), (inner_height,
                                                      inner_width)
         data = Core.function_crop_rotated(
             DataAndMetadata.new_data_and_metadata(data), inner_bounds,
             -total_rotation).data
     else:
         data = data[extra // 2:extra // 2 + size.height,
                     extra // 2:extra // 2 + size.width]
     return (data + numpy.random.randn(size.height, size.width) *
             noise_factor) * frame_parameters.pixel_time_us
コード例 #6
0
 def test_setting_and_getting_attribute_values_and_2D_values_works(self):
     instrument = InstrumentDevice.Instrument("usim_stem_controller")
     C12 = Geometry.FloatPoint(x=-1e-5, y=-3e-5)
     instrument.SetVal("C12.x", C12.x)
     instrument.SetVal("C12.y", C12.y)
     self.assertAlmostEqual(C12.x, instrument.GetVal("C12.x"))
     self.assertAlmostEqual(C12.x, instrument.GetVal2D("C12").x)
     self.assertAlmostEqual(C12.y, instrument.GetVal("C12.y"))
     self.assertAlmostEqual(C12.y, instrument.GetVal2D("C12").y)
     C12 = Geometry.FloatPoint(y=-2e5, x=1e5)
     instrument.SetVal2D("C12", C12)
     self.assertAlmostEqual(C12.x, instrument.GetVal("C12.x"))
     self.assertAlmostEqual(C12.x, instrument.GetVal2D("C12").x)
     self.assertAlmostEqual(C12.y, instrument.GetVal("C12.y"))
     self.assertAlmostEqual(C12.y, instrument.GetVal2D("C12").y)
コード例 #7
0
 def intersects(self, offset_m: Geometry.FloatPoint, fov_nm: Geometry.FloatSize, center_nm: Geometry.FloatPoint,
                probe_position: Geometry.FloatPoint) -> bool:
     scan_rect_m = self.get_scan_rect_m(offset_m, fov_nm, center_nm)
     feature_rect_m = self.get_feature_rect_m()
     probe_position_m = Geometry.FloatPoint(y=probe_position.y * scan_rect_m.height + scan_rect_m.top,
                                            x=probe_position.x * scan_rect_m.width + scan_rect_m.left)
     return scan_rect_m.intersects_rect(feature_rect_m) and feature_rect_m.contains_point(probe_position_m)
コード例 #8
0
 def GetVal2D(self, s: str, default_value: Geometry.FloatPoint=None, *, axis: stem_controller.AxisType=None) -> Geometry.FloatPoint:
     control = self.__controls.get(s)
     if isinstance(control, Control2D):
         axis = axis if axis is not None else control.native_axis
         return Geometry.FloatPoint(getattr(control, axis[1]).output_value, getattr(control, axis[0]).output_value)
     if default_value is None:
         raise Exception(f"No 2D element named '{s}' exists! Cannot get value.")
     else:
         return default_value
コード例 #9
0
 def probe_position(self, value: typing.Optional[Geometry.FloatPoint]) -> None:
     if value is not None:
         # convert the probe position to a FloatPoint and limit it to the 0.0 to 1.0 range in both axes.
         value = Geometry.FloatPoint(y=max(min(value.y, 1.0), 0.0), x=max(min(value.x, 1.0), 0.0))
     if self.probe_position != value:
         self.__probe_position = value
         self.notify_property_changed("probe_position")
         # update the probe position for listeners and also explicitly update for probe_graphic_connections.
         self.probe_state_changed_event.fire(self.probe_state, self.probe_position)
コード例 #10
0
 def start_frame(self, is_continuous: bool) -> int:
     """Start acquiring. Return the frame number."""
     if not self.__is_scanning:
         self.__buffer = list()
         self.__start_next_frame()
         self.__is_scanning = True
         self.__instrument.live_probe_position = Geometry.FloatPoint(
         ) if self.__frame_parameters.external_clock_mode != 0 else None
     return self.__frame_number
コード例 #11
0
 def __property_changed(self, property_name):
     if property_name == self.__graphic_property_name:
         old_property_value = getattr(self.source, self.__region_property_name)
         # to prevent message loops, check to make sure it changed
         property_value = getattr(self.__graphic, property_name)
         if property_value is not None:
             property_value = Geometry.FloatPoint(y=property_value[0], x=property_value[1])
         if property_value != old_property_value:
             self.update_source(property_value)
コード例 #12
0
 def offset_m(self) -> Geometry.FloatPoint:
     # note: positive values will move data down/right
     max_drift_y_m = 15 * 1E-9 * 0
     max_drift_x_m = 10 * 1E-9 * 0
     period_y_s = 60 * 4
     period_x_s = 90 * 4
     phase_y_rad = 7
     phase_x_rad = 10
     return Geometry.FloatPoint(y=max_drift_y_m * math.cos((time.time() - self.__start_time - phase_y_rad) * 2 * math.pi / period_y_s),
                                x=max_drift_x_m * math.sin((time.time() - self.__start_time + phase_x_rad) * 2 * math.pi / period_x_s))
コード例 #13
0
 def mouse_position_changed(
         self, x: int, y: int,
         modifiers: UserInterface.KeyboardModifiers) -> bool:
     if self.__drag_start is not None and Geometry.distance(
             Geometry.FloatPoint(y, x),
             self.__drag_start.to_float_point()) > 2:
         self.__drag_start = None
         on_drag_pressed = self.on_drag_pressed
         if on_drag_pressed:
             on_drag_pressed(x, y, modifiers)
             return True
     return False
コード例 #14
0
ファイル: Panel.py プロジェクト: cmeyer/nionswift
 def mouse_position_changed(
         self, x: int, y: int,
         modifiers: UserInterface.KeyboardModifiers) -> bool:
     pt = Geometry.FloatPoint(y=y, x=x)
     mouse_pressed_pos = self.__mouse_pressed_position
     if mouse_pressed_pos and Geometry.distance(
             mouse_pressed_pos.to_float_point(), pt) > 12:
         on_drag_pressed = self.on_drag_pressed
         if callable(on_drag_pressed):
             self.__mouse_pressed_position = None
             on_drag_pressed()
             return True
     return False
コード例 #15
0
 def test_setting_and_getting_attribute_values_and_2D_values_works_on_custom_2d_control(
         self):
     instrument = InstrumentDevice.Instrument("usim_stem_controller")
     control = instrument.create_2d_control("custom_control", ("x", "y"))
     instrument.add_control(control)
     value = Geometry.FloatPoint(x=-1e-5, y=-3e-5)
     instrument.SetVal("custom_control.x", value.x)
     instrument.SetVal("custom_control.y", value.y)
     self.assertAlmostEqual(value.x, instrument.GetVal("custom_control.x"))
     self.assertAlmostEqual(value.x,
                            instrument.GetVal2D("custom_control").x)
     self.assertAlmostEqual(value.y, instrument.GetVal("custom_control.y"))
     self.assertAlmostEqual(value.y,
                            instrument.GetVal2D("custom_control").y)
     value = Geometry.FloatPoint(y=-2e5, x=1e5)
     instrument.SetVal2D("custom_control", value)
     self.assertAlmostEqual(value.x, instrument.GetVal("custom_control.x"))
     self.assertAlmostEqual(value.x,
                            instrument.GetVal2D("custom_control").x)
     self.assertAlmostEqual(value.y, instrument.GetVal("custom_control.y"))
     self.assertAlmostEqual(value.y,
                            instrument.GetVal2D("custom_control").y)
コード例 #16
0
    def test_setting_2d_control_triggers_event(self):
        instrument = InstrumentDevice.Instrument("usim_stem_controller")
        event_name = ""

        def listen(name):
            nonlocal event_name
            if name == "C12":
                event_name = name

        with contextlib.closing(
                instrument.property_changed_event.listen(listen)):
            instrument.SetVal2D("C12", Geometry.FloatPoint(y=-2e5, x=1e5))
        self.assertEqual(event_name, "C12")
コード例 #17
0
 def set_probe_position(self, new_probe_position):
     """ Set the probe position, in normalized coordinates with origin at top left. """
     if new_probe_position is not None:
         # convert the probe position to a FloatPoint and limit it to the 0.0 to 1.0 range in both axes.
         new_probe_position = Geometry.FloatPoint.make(new_probe_position)
         new_probe_position = Geometry.FloatPoint(y=max(min(new_probe_position.y, 1.0), 0.0),
                                                  x=max(min(new_probe_position.x, 1.0), 0.0))
     old_probe_position = self.__probe_position_value.value
     if ((old_probe_position is None) != (new_probe_position is None)) or (old_probe_position != new_probe_position):
         # this path is only taken if set_probe_position is not called as a result of the probe_position model
         # value changing.
         self.__probe_position_value.value = new_probe_position
     # update the probe position for listeners and also explicitly update for probe_graphic_connections.
     self.probe_state_changed_event.fire(self.probe_state, self.probe_position)
コード例 #18
0
    def __init__(self, instrument_id: str):
        super().__init__()
        self.priority = 20
        self.instrument_id = instrument_id
        self.property_changed_event = Event.Event()
        self.__camera_frame_event = threading.Event()
        self.__camera_frame_event_ack = threading.Event()

        # define the STEM geometry limits
        self.stage_size_nm = 1000
        self.max_defocus = 5000 / 1E9

        # define the samples
        self.__samples = [
            SampleSimulator.RectangleFlakeSample(self.stage_size_nm),
            SampleSimulator.AmorphousSample()
        ]
        self.__sample_index = 0

        self.__stage_position_m = Geometry.FloatPoint()
        self.__drift_controller = DriftController()
        self.__slit_in = False
        self.__energy_per_channel_eV = 0.5
        self.__beam_current = 200E-12  # 200 pA
        self.__blanked = False
        self.__ronchigram_shape = Geometry.IntSize(2048, 2048)
        self.__eels_shape = Geometry.IntSize(256, 1024)
        self.__scan_context = stem_controller.ScanContext()
        self.__probe_position = None
        self.__live_probe_position = None
        self.__sequence_progress = 0
        self._is_synchronized = False
        self.__lock = threading.Lock()
        self.__controls = dict()

        built_in_controls = self.__create_built_in_controls()
        for control in built_in_controls:
            self.add_control(control)
        # We need to set the expressions after adding the controls to InstrumentDevice
        self.__set_expressions()

        self.__cameras = {
            "ronchigram":
            RonchigramCameraSimulator.RonchigramCameraSimulator(
                self, self.__ronchigram_shape, self.counts_per_electron,
                self.stage_size_nm),
            "eels":
            EELSCameraSimulator.EELSCameraSimulator(self, self.__eels_shape,
                                                    self.counts_per_electron)
        }
コード例 #19
0
ファイル: ListCanvasItem.py プロジェクト: Brow71189/nionui
 def mouse_position_changed(self, x: int, y: int, modifiers: UserInterface.KeyboardModifiers) -> bool:
     if self.__mouse_pressed_for_dragging and self.__mouse_position and self.__mouse_index is not None:
         mouse_position_f = self.__mouse_position.to_float_point()
         point_f = Geometry.FloatPoint(y=y, x=x)
         if not self.__mouse_dragging and Geometry.distance(mouse_position_f, point_f) > 8:
             self.__mouse_dragging = True
             if self.__delegate:
                 root_container = self.root_container
                 if root_container:
                     root_container.bypass_request_focus()
                 self.__delegate.drag_started(self.__mouse_index, x, y, modifiers)
                 # once a drag starts, mouse release will not be called; call it here instead
                 self.__mouse_released(x, y, modifiers, False)
             return True
     return super().mouse_position_changed(x, y, modifiers)
コード例 #20
0
 def setup_camera_hardware_source(
         self, stem_controller: stem_controller.STEMController,
         camera_exposure: float,
         is_eels: bool) -> HardwareSource.HardwareSource:
     instrument = typing.cast(InstrumentDevice.Instrument, stem_controller)
     camera_id = "usim_ronchigram_camera" if not is_eels else "usim_eels_camera"
     camera_type = "ronchigram" if not is_eels else "eels"
     camera_name = "uSim Camera"
     camera_settings = CameraDevice.CameraSettings(camera_id)
     camera_device = CameraDevice.Camera(camera_id, camera_type,
                                         camera_name, instrument)
     if getattr(camera_device, "camera_version", 2) == 3:
         camera_hardware_source = camera_base.CameraHardwareSource3(
             "usim_stem_controller", camera_device, camera_settings, None,
             None)
     else:
         camera_hardware_source = camera_base.CameraHardwareSource2(
             "usim_stem_controller", camera_device, camera_settings, None,
             None)
     if is_eels:
         camera_hardware_source.features["is_eels_camera"] = True
         camera_hardware_source.add_channel_processor(
             0,
             HardwareSource.SumProcessor(
                 Geometry.FloatRect(Geometry.FloatPoint(0.25, 0.0),
                                    Geometry.FloatSize(0.5, 1.0))))
     camera_hardware_source.set_frame_parameters(
         0,
         camera_base.CameraFrameParameters({
             "exposure_ms": camera_exposure * 1000,
             "binning": 2
         }))
     camera_hardware_source.set_frame_parameters(
         1,
         camera_base.CameraFrameParameters({
             "exposure_ms": camera_exposure * 1000,
             "binning": 2
         }))
     camera_hardware_source.set_frame_parameters(
         2,
         camera_base.CameraFrameParameters({
             "exposure_ms":
             camera_exposure * 1000 * 2,
             "binning":
             1
         }))
     camera_hardware_source.set_selected_profile_index(0)
     return camera_hardware_source
コード例 #21
0
    def test_setting_value_on_custom_2d_control_triggers_property_changed_event(
            self):
        instrument = InstrumentDevice.Instrument("usim_stem_controller")
        control = instrument.create_2d_control("custom_control", ("x", "y"))
        instrument.add_control(control)
        event_name = ""

        def listen(name):
            nonlocal event_name
            if name == "custom_control":
                event_name = name

        with contextlib.closing(
                instrument.property_changed_event.listen(listen)):
            instrument.SetVal2D("custom_control",
                                Geometry.FloatPoint(y=-2e5, x=1e5))
        self.assertEqual(event_name, "custom_control")
コード例 #22
0
 def __init__(self, stage_size_nm: float):
     self.__features = list()
     sample_size_m = Geometry.FloatSize(height=20 * stage_size_nm / 100, width=20 * stage_size_nm / 100) / 1E9
     feature_percentage = 0.3
     random_state = random.getstate()
     random.seed(1)
     energies = [[(68, 30), (855, 50), (872, 50)], [(29, 15), (1217, 50), (1248, 50)],
                 [(1839, 5), (99, 50)]]  # Ni, Ge, Si
     plasmons = [20, 16.2, 16.8]
     for i in range(100):
         position_m = Geometry.FloatPoint(y=(2 * random.random() - 1.0) * sample_size_m.height,
                                          x=(2 * random.random() - 1.0) * sample_size_m.width)
         size_m = feature_percentage * Geometry.FloatSize(height=random.random() * sample_size_m.height,
                                                          width=random.random() * sample_size_m.width)
         self.__features.append(
             Feature(position_m, size_m, energies[i % len(energies)], plasmons[i % len(plasmons)], 4))
     random.setstate(random_state)
コード例 #23
0
 def prepare_section(
         self,
         *,
         utc_time: typing.Optional[datetime.datetime] = None) -> None:
     # this method must be thread safe
     # start with the context frame parameters and adjust for the drift region
     frame_parameters = copy.deepcopy(self.__scan_frame_parameters)
     context_size = frame_parameters.size.to_float_size()
     drift_channel_id = self.__scan_hardware_source.drift_channel_id
     drift_region = self.__scan_hardware_source.drift_region
     drift_rotation = self.__scan_hardware_source.drift_rotation
     if drift_channel_id is not None and drift_region is not None:
         drift_channel_index = self.__scan_hardware_source.get_channel_index(
             drift_channel_id)
         assert drift_channel_index is not None
         frame_parameters.subscan_pixel_size = Geometry.IntSize(
             int(context_size.height * drift_region.height * 4),
             int(context_size.width * drift_region.width * 4))
         if frame_parameters.subscan_pixel_size[
                 0] >= 8 or frame_parameters.subscan_pixel_size[1] >= 8:
             frame_parameters.subscan_fractional_size = Geometry.FloatSize(
                 drift_region.height, drift_region.width)
             frame_parameters.subscan_fractional_center = Geometry.FloatPoint(
                 drift_region.center.y, drift_region.center.x)
             frame_parameters.subscan_rotation = drift_rotation
             # attempt to keep drift area in roughly the same position by adding in the accumulated correction.
             drift_tracker = self.__scan_hardware_source.drift_tracker
             utc_time = utc_time or datetime.datetime.utcnow()
             delta_nm = drift_tracker.predict_drift(utc_time)
             frame_parameters.center_nm = frame_parameters.center_nm - delta_nm
             xdatas = self.__scan_hardware_source.record_immediate(
                 frame_parameters, [drift_channel_index])
             xdata0 = xdatas[0]
             if xdata0:
                 drift_tracker.submit_image(xdata0,
                                            drift_rotation,
                                            wait=True)
コード例 #24
0
        def add_line_profile(
                data_item: DataItem.DataItem,
                document_controller: DocumentController.DocumentController,
                display_panel_id: str,
                midpoint: float = 0.5,
                integration_width: float = 0.25) -> None:
            logging.debug("midpoint: {:.4f}".format(midpoint))
            logging.debug("width: {:.4f}".format(integration_width))

            # next, line profile through center of crop
            # please don't copy this bad example code!
            crop_region = Graphics.RectangleGraphic()
            crop_region.center = Geometry.FloatPoint(midpoint, 0.5)
            crop_region.size = Geometry.FloatSize(integration_width, 1)
            crop_region.is_bounds_constrained = True
            display_item = document_controller.document_model.get_display_item_for_data_item(
                data_item)
            assert display_item
            display_item.add_graphic(crop_region)
            display_data_item = display_item.data_item
            assert display_data_item
            eels_data_item = document_controller.document_model.get_projection_new(
                display_item, display_data_item, crop_region)
            if eels_data_item:
                eels_data_item.title = _("EELS Summed")
                eels_display_item = document_controller.document_model.get_display_item_for_data_item(
                    eels_data_item)
                assert eels_display_item
                document_controller.show_display_item(eels_display_item)
            else:
                eels_display_item = None

            workspace_controller = document_controller.workspace_controller
            if workspace_controller and eels_display_item:
                workspace_controller.display_display_item_in_display_panel(
                    eels_display_item, display_panel_id)
コード例 #25
0
 def test_property_attribute_binding_refcount(self) -> None:
     binding = Binding.PropertyAttributeBinding(
         Model.PropertyModel(Geometry.FloatPoint()), "value", "x")
     binding_ref = weakref.ref(binding)
     del binding
     self.assertIsNone(binding_ref())
コード例 #26
0
 def change_stage_position(self, *, dy: int = None, dx: int = None):
     """Shift the stage by dx, dy (meters). Do not wait for confirmation."""
     self.stage_position_m += Geometry.FloatPoint(y=-dy, x=-dx)
コード例 #27
0
    def get_frame_data(self, readout_area: Geometry.IntRect, binning_shape: Geometry.IntSize, exposure_s: float, scan_context, parked_probe_position):
        """
        Features at the probe position will add plasmons and edges in addition to a ZLP.

        There are two inputs to this model: the beam current and the T/l (thickness / mean free path).

        The sum of the spectrum data should add up to the beam current (using counts per electron and conversion from
        electrons to amps).

        The natural log of the ratio of the sum of the spectrum to the sum of the ZLP should equal thickness / mean free
        path.

        The strategy is to have low level routines for adding the shapes of the ZLP (gaussian normal) and plasmons and
        edges (power law multiplied by integrated gaussian normal) and then scaling these shapes such that they satisfy
        the conditions above.

        A complication of this is that the specified energy range may not include the ZLP. So two spectrums are built:
        the one for caller and the one for reference. The reference one is used for calculating the scaling of the ZLP
        and edges, which are then applied to the spectrum for the caller.

        If we define the following values:
            z = sum/integration of unscaled ZLP gaussian
            f = sum/integration of unscaled plasmons/edges
            P = target count such that P / counts_per_electron matches beam current
            T = thickness (nm)
            L = lambda (mean_free_path_nm)
            T/l = thickness / lambda (mean free path)
        then we can solve for two unknowns:
            A = scale of ZLP
            B = scale of plasmons/edges
        using the two equations:
            Az + Bf = P (beam current)
            ln(P / Az) = T/l => P / Az = exp(T/l) (thickness = natural log of ratio of total counts to ZLP counts)
        solving:
            A = P / exp(T/l) / z
            B = (P - Az) / f
        """

        # grab the probe position
        probe_position = Geometry.FloatPoint(0.5, 0.5)
        if self.instrument.is_blanked:
            probe_position = None
        elif self.instrument.probe_state == "scanning":
            probe_position = self.instrument.live_probe_position
        elif self.instrument.probe_state == "parked" and parked_probe_position is not None:
            probe_position = parked_probe_position
        probe_position = Geometry.FloatPoint.make(probe_position) if probe_position is not None else None

        # check if one of the arguments has changed since last call
        new_frame_settings = [readout_area, binning_shape, exposure_s, copy.deepcopy(scan_context), probe_position]
        if new_frame_settings != self._last_frame_settings:
            self._needs_recalculation = True
        self._last_frame_settings = new_frame_settings

        if self._needs_recalculation or self.__cached_frame is None:
            data = numpy.zeros(tuple(self._sensor_dimensions), numpy.float)
            slit_attenuation = 10 if self.instrument.is_slit_in else 1
            intensity_calibration = Calibration.Calibration(units="counts")
            dimensional_calibrations = self.get_dimensional_calibrations(readout_area, binning_shape)

            # typical thickness over mean free path (T/l) will be 0.5
            mean_free_path_nm = 100  # nm. (lambda values from back of Edgerton)
            thickness_per_layer_nm = 30  # nm

            # this is the number of pixel counts expected if the ZLP is visible in vacuum for the given exposure
            # and beam current (in get_total_counts).
            target_pixel_count = self.get_total_counts(exposure_s) / data.shape[0]

            # grab the specific calibration for the energy direction and offset by ZLPoffset
            used_calibration = dimensional_calibrations[1]
            used_calibration.offset = self.instrument.get_control("ZLPoffset").local_value

            if scan_context.is_valid and probe_position is not None:

                # make a buffer for the spectrum
                spectrum = numpy.zeros((data.shape[1], ), numpy.float)

                # configure a calibration for the reference spectrum. then plot the ZLP on the reference data. sum it to
                # get the zlp_pixel_count and the zlp_scale. this is the value to multiple zlp data by to scale it so
                # that it will produce the target pixel count. since we will be storing the spectra in a 2d array,
                # divide by the height of that array so that when it is summed, the value comes out correctly.
                zlp0_calibration = Calibration.Calibration(scale=used_calibration.scale, offset=-20)
                spectrum_ref = numpy.zeros((int(zlp0_calibration.convert_from_calibrated_value(-20 + 1000) - zlp0_calibration.convert_from_calibrated_value(-20)), ), numpy.float)
                plot_norm(spectrum_ref, 1.0, Calibration.Calibration(scale=used_calibration.scale, offset=-20), 0, 0.5 / slit_attenuation)
                zlp_ref_pixel_count = float(numpy.sum(spectrum_ref))

                # build the spectrum and reference spectrum by adding the features. the data is unscaled.
                spectrum_ref = numpy.zeros((int(zlp0_calibration.convert_from_calibrated_value(-20 + 1000) - zlp0_calibration.convert_from_calibrated_value(-20)), ), numpy.float)
                offset_m = self.instrument.stage_position_m - self.instrument.GetVal2D("beam_shift_m")  # get this from current values
                feature_layer_count = 0
                for index, feature in enumerate(self.instrument.sample.features):
                    if feature.intersects(offset_m, scan_context.fov_size_nm, scan_context.center_nm, probe_position):
                        plot_spectrum(feature, spectrum, 1.0, used_calibration)
                        plot_spectrum(feature, spectrum_ref, 1.0, zlp0_calibration)
                        feature_layer_count += 1
                feature_pixel_count = max(numpy.sum(spectrum_ref), 0.01)

                # make the calculations for A, B (zlp_scale and feature_scale).
                thickness_factor = feature_layer_count * thickness_per_layer_nm / mean_free_path_nm
                zlp_scale = target_pixel_count / math.exp(thickness_factor) / zlp_ref_pixel_count
                feature_scale = (target_pixel_count - (target_pixel_count / math.exp(thickness_factor))) / feature_pixel_count
                # print(f"thickness_factor {thickness_factor}")

                # apply the scaling. spectrum holds the features at this point, but not the ZLP. just multiple by
                # feature_scale to make the feature part of the spectrum final. then plot the ZLP scaled by zlp_scale.
                spectrum *= feature_scale
                # print(f"sum {numpy.sum(spectrum) * data.shape[0]}")
                # print(f"zlp_ref_pixel_count {zlp_ref_pixel_count} feature_pixel_count {feature_pixel_count}")
                # print(f"zlp_scale {zlp_scale} feature_scale {feature_scale}")
                plot_norm(spectrum, zlp_scale, used_calibration, 0, 0.5 / slit_attenuation)
                # print(f"sum {numpy.sum(spectrum) * data.shape[0]}")
                # print(f"target_pixel_count {target_pixel_count}")

                # finally, store the spectrum into each row of the data
                data[:, ...] = spectrum

                # spectrum_pixel_count = float(numpy.sum(spectrum)) * data.shape[0]
                # print(f"z0 {zlp_ref_pixel_count * data.shape[0]} / {used_calibration.offset}")
                # print(f"beam current {self.instrument.beam_current * 1e12}pA")
                # print(f"current {spectrum_pixel_count / exposure_s / self.instrument.counts_per_electron / 6.242e18 * 1e12:#.2f}pA")
                # print(f"target {target_pixel_count}  actual {spectrum_pixel_count}")
                # print(f"s {spectrum_pixel_count} z {zlp_ref_pixel_count * zlp_scale * data.shape[0]}")
                # print(f"{math.log(spectrum_pixel_count / (zlp_ref_pixel_count * zlp_scale * data.shape[0]))} {thickness_factor}")

            data = self._get_binned_data(data, binning_shape)

            self.__cached_frame = DataAndMetadata.new_data_and_metadata(data.astype(numpy.float32), intensity_calibration=intensity_calibration, dimensional_calibrations=dimensional_calibrations)
            self.__data_scale = self.get_total_counts(exposure_s) / target_pixel_count / slit_attenuation / self._sensor_dimensions[0]
            self._needs_recalculation = False

        self.noise.poisson_level = self.__data_scale
        return self.noise.apply(self.__cached_frame)
コード例 #28
0
    def read_partial(
        self, frame_number, pixels_to_skip
    ) -> (typing.Sequence[dict], bool, bool, tuple, int, int):
        """Read or continue reading a frame.

        The `frame_number` may be None, in which case a new frame should be read.

        The `frame_number` otherwise specifies which frame to continue reading.

        The `pixels_to_skip` specifies where to start reading the frame, if it is a continuation.

        Return values should be a list of dict's (one for each active channel) containing two keys: 'data' and
        'properties' (see below), followed by a boolean indicating whether the frame is complete, a boolean indicating
        whether the frame was bad, a tuple of the form (top, left), (height, width) indicating the valid sub-area
        of the data, the frame number, and the pixels to skip next time around if the frame is not complete.

        The 'data' keys in the list of dict's should contain a ndarray with the size of the full acquisition and each
        ndarray should be the same size. The 'properties' keys are dicts which must contain the frame parameters and
        a 'channel_id' indicating the index of the channel (may be an int or float).
        """

        if self.__frame is None:
            self.__start_next_frame()
        current_frame = self.__frame
        assert current_frame is not None
        frame_number = current_frame.frame_number

        frame_parameters = current_frame.frame_parameters
        size = Geometry.IntSize.make(
            frame_parameters.subscan_pixel_size if frame_parameters.
            subscan_pixel_size else frame_parameters.size)
        total_pixels = size.height * size.width
        time_slice = 0.005  # 5ms

        if current_frame.scan_data is None:
            scan_data = list()
            for channel in current_frame.channels:
                scan_data.append(
                    self.__instrument.get_scan_data(
                        current_frame.frame_parameters, channel))
            current_frame.scan_data = scan_data

        is_synchronized_scan = frame_parameters.external_clock_mode != 0
        if current_frame.data_count == 0 and is_synchronized_scan:
            self.__instrument.live_probe_position = Geometry.FloatPoint()

        target_count = 0
        while self.__is_scanning and target_count <= current_frame.data_count:
            if is_synchronized_scan:
                # set the probe position
                h, w = current_frame.scan_data[0].shape
                # calculate relative position within sub-area
                ry, rx = current_frame.data_count // w / h - 0.5, current_frame.data_count % w / w - 0.5
                # now translate to context
                ss = Geometry.FloatSize.make(
                    frame_parameters.subscan_fractional_size
                ) if frame_parameters.subscan_fractional_size else Geometry.FloatSize(
                    h=1.0, w=1.0)
                oo = Geometry.FloatPoint.make(
                    frame_parameters.subscan_fractional_center
                ) - Geometry.FloatPoint(
                    y=0.5, x=0.5
                ) if frame_parameters.subscan_fractional_center else Geometry.FloatPoint(
                )
                oo += Geometry.FloatSize(
                    h=frame_parameters.center_nm[0] / frame_parameters.fov_nm,
                    w=frame_parameters.center_nm[1] / frame_parameters.fov_nm)
                pt = Geometry.FloatPoint(y=ry * ss.height + oo.y,
                                         x=rx * ss.width + oo.x)
                pt = pt.rotate(frame_parameters.rotation_rad)
                if frame_parameters.subscan_rotation:
                    pt = pt.rotate(-frame_parameters.subscan_rotation, oo)
                # print(f"{x}, {y} = ({ry} - 0.5) * {ss.height} + {oo.y} / (ry - 0.5) * ss.height + oo.y")
                # >>> def f(s, c, x): return (x - 0.5) * s + c # |------<---c--->------------|
                self.__instrument.live_probe_position = pt + Geometry.FloatPoint(
                    y=0.5, x=0.5)
                # do a synchronized readout
                if current_frame.data_count % size.width == 0:
                    # throw away two flyback images at beginning of line
                    if not self.__is_scanning or not self.__instrument.wait_for_camera_frame(
                            frame_parameters.external_clock_wait_time_ms /
                            1000):
                        current_frame.bad = True
                        current_frame.complete = True
                    if not self.__is_scanning or not self.__instrument.wait_for_camera_frame(
                            frame_parameters.external_clock_wait_time_ms /
                            1000):
                        current_frame.bad = True
                        current_frame.complete = True
                if not self.__is_scanning or not self.__instrument.wait_for_camera_frame(
                        frame_parameters.external_clock_wait_time_ms / 1000):
                    current_frame.bad = True
                    current_frame.complete = True
                target_count = current_frame.data_count + 1
            else:
                pixels_remaining = total_pixels - current_frame.data_count
                pixel_wait = min(
                    pixels_remaining * frame_parameters.pixel_time_us / 1E6,
                    time_slice)
                time.sleep(pixel_wait)
                target_count = min(
                    int((time.time() - current_frame.start_time) /
                        (frame_parameters.pixel_time_us / 1E6)), total_pixels)

        if self.__is_scanning and target_count > current_frame.data_count:
            for channel_index, channel in enumerate(current_frame.channels):
                scan_data_flat = current_frame.scan_data[
                    channel_index].reshape((total_pixels, ))
                channel_data_flat = channel.data.reshape((total_pixels, ))
                channel_data_flat[current_frame.
                                  data_count:target_count] = scan_data_flat[
                                      current_frame.data_count:target_count]
            current_frame.data_count = target_count
            current_frame.complete = current_frame.data_count == total_pixels
        else:
            assert not self.__is_scanning
            current_frame.data_count = total_pixels
            current_frame.complete = True

        data_elements = list()

        for channel in current_frame.channels:
            data_element = dict()
            data_element["data"] = channel.data
            properties = current_frame.frame_parameters.as_dict()
            properties[
                "center_x_nm"] = current_frame.frame_parameters.center_nm[1]
            properties[
                "center_y_nm"] = current_frame.frame_parameters.center_nm[0]
            properties["rotation_deg"] = math.degrees(
                current_frame.frame_parameters.rotation_rad)
            properties["channel_id"] = channel.channel_id
            data_element["properties"] = properties
            data_elements.append(data_element)

        current_rows_read = current_frame.data_count // size.width

        if current_frame.complete:
            sub_area = ((0, 0), size)
            pixels_to_skip = 0
            self.__frame = None
        else:
            sub_area = ((pixels_to_skip // size.width, 0),
                        (current_rows_read - pixels_to_skip // size.width,
                         size.width))
            pixels_to_skip = size.width * current_rows_read

        complete = current_frame.complete
        bad_frame = False

        if complete:
            if len(self.__buffer) > 0 and len(
                    self.__buffer[-1]) != len(data_elements):
                self.__buffer = list()
            self.__buffer.append(data_elements)
            while len(self.__buffer) > 100:
                del self.__buffer[0]
            self.__is_scanning = False

        return data_elements, complete, bad_frame, sub_area, frame_number, pixels_to_skip
コード例 #29
0
    def validate_probe_position(self):
        """Validate the probe position.

        This is called when the user switches from not controlling to controlling the position."""
        self.set_probe_position(Geometry.FloatPoint(y=0.5, x=0.5))
コード例 #30
0
    def get_frame_data(self, readout_area: Geometry.IntRect, binning_shape: Geometry.IntSize, exposure_s: float, scan_context, parked_probe_position) -> DataAndMetadata.DataAndMetadata:
        # check if one of the arguments has changed since last call
        new_frame_settings = [readout_area, binning_shape, exposure_s, copy.deepcopy(scan_context)]
        if new_frame_settings != self._last_frame_settings:
            self._needs_recalculation = True
        if self.instrument.sample != self.__last_sample:
            self._needs_recalculation = True
        self._last_frame_settings = new_frame_settings

        if self._needs_recalculation or self.__cached_frame is None:
            #print("recalculating frame")
            height = readout_area.height
            width = readout_area.width
            offset_m = self.instrument.stage_position_m
            full_fov_nm = self.__stage_size_nm
            fov_size_nm = Geometry.FloatSize(full_fov_nm * height / self._sensor_dimensions.height, full_fov_nm * width / self._sensor_dimensions.width)
            center_nm = Geometry.FloatPoint(full_fov_nm * (readout_area.center.y / self._sensor_dimensions.height- 0.5), full_fov_nm * (readout_area.center.x / self._sensor_dimensions.width - 0.5))
            size = Geometry.IntSize(height, width)
            data = numpy.zeros((height, width), numpy.float32)
            # features will be positive values; thickness can be simulated by subtracting the features from the
            # vacuum value. the higher the vacuum value, the thinner (i.e. less contribution from features).
            thickness_param = 100
            if not self.instrument.is_blanked:
                self.instrument.sample.plot_features(data, offset_m, fov_size_nm, Geometry.FloatPoint(), center_nm, size)
                data = thickness_param - data
            data = self._get_binned_data(data, binning_shape)
            self.__last_sample = self.instrument.sample

            if not self.instrument.is_blanked:
                probe_position = Geometry.FloatPoint(0.5, 0.5)
                if self.instrument.probe_state == "scanning":
                    probe_position = self.instrument.live_probe_position
                elif self.instrument.probe_state == "parked" and parked_probe_position is not None:
                    probe_position = parked_probe_position

                scan_offset = Geometry.FloatPoint()
                if scan_context.is_valid and probe_position is not None:
                    scan_offset = Geometry.FloatPoint(
                        y=probe_position[0] * scan_context.fov_size_nm[0] - scan_context.fov_size_nm[0] / 2,
                        x=probe_position[1] * scan_context.fov_size_nm[1] - scan_context.fov_size_nm[1] / 2)
                    scan_offset = scan_offset*1e-9

                theta = self.__tv_pixel_angle * self._sensor_dimensions.height / 2  # half angle on camera
                aberrations = dict()
                aberrations["height"] = data.shape[0]
                aberrations["width"] = data.shape[1]
                aberrations["theta"] = theta
                aberrations["c0a"] = self.instrument.GetVal2D("beam_shift_m").x + scan_offset[1]
                aberrations["c0b"] = self.instrument.GetVal2D("beam_shift_m").y + scan_offset[0]
                aberrations["c10"] = self.instrument.GetVal("C10Control")
                aberrations["c12a"] = self.instrument.GetVal2D("C12Control").x
                aberrations["c12b"] = self.instrument.GetVal2D("C12Control").y
                aberrations["c21a"] = self.instrument.GetVal2D("C21Control").x
                aberrations["c21b"] = self.instrument.GetVal2D("C21Control").y
                aberrations["c23a"] = self.instrument.GetVal2D("C23Control").x
                aberrations["c23b"] = self.instrument.GetVal2D("C23Control").y
                aberrations["c30"] = self.instrument.GetVal("C30Control")
                aberrations["c32a"] = self.instrument.GetVal2D("C32Control").x
                aberrations["c32b"] = self.instrument.GetVal2D("C32Control").y
                aberrations["c34a"] = self.instrument.GetVal2D("C34Control").x
                aberrations["c34b"] = self.instrument.GetVal2D("C34Control").y
                data = self.__aberrations_controller.apply(aberrations, data)
                if self.instrument.GetVal("S_VOA") > 0:
                    self._draw_aperture(data, binning_shape)
                elif self.instrument.GetVal("S_MOA") > 0:
                    self._draw_aperture(data, binning_shape, enlarge_by=0.1)

            intensity_calibration = Calibration.Calibration(units="counts")
            dimensional_calibrations = self.get_dimensional_calibrations(readout_area, binning_shape)

            self.__cached_frame = DataAndMetadata.new_data_and_metadata(data.astype(numpy.float32), intensity_calibration=intensity_calibration, dimensional_calibrations=dimensional_calibrations)
            self.__data_scale = self.get_total_counts(exposure_s) / (data.shape[0] * data.shape[1] * thickness_param)
            self._needs_recalculation = False

        self.noise.poisson_level = self.__data_scale
        return self.noise.apply(self.__cached_frame * self.__data_scale)