Exemplo n.º 1
0
 def _acq_wait_data(self, timeout=0):
     """
     Block until a data is received, or a stop message.
     Note: it expects that the acquisition is running.
     timeout (0<=float): how long to wait to check (use 0 to not wait)
     return (bool): True if needs to stop, False if data is ready
     raise TerminationRequested: if a terminate message was received
     """
     tend = time.time() + timeout
     while True:
         left = max(0, tend - time.time())
         try:
             msg = self._get_acq_msg(timeout=left)
         except queue.Empty:
             raise TimeoutError("No data message received within %s s" % (timeout,))
         if msg == GEN_DATA:
             return False
         elif msg == GEN_TERM:
             raise TerminationRequested()
         elif msg == GEN_STOP:
             return True
         elif isinstance(msg, float):  # trigger
             # received trigger too early => store it for later
             self._old_triggers.insert(0, msg)
         else:  # Anything else shouldn't really happen
             logging.warning("Skipped message %s as acquisition is waiting for trigger", msg)
Exemplo n.º 2
0
    def _waitTemperatureReached(self, comp, timeout=None):
        """
        Wait until the current temperature of the component has reached the
          target temperature (within some margin).
        comp (Component)
        timeout (0<float or None): maximum time to wait (in s)
        raises:
            TimeoutError: if time-out reached
        """
        tstart = time.time()
        while timeout is None or time.time() < tstart + timeout:
            # TODO: adjust the timeout depending on whether the temperature
            # gets closer to the target over time or not.
            ttemp = comp.targetTemperature.value
            atemp = comp.temperature.value
            if atemp < ttemp + TEMP_EPSILON:
                return
            else:
                logging.debug(
                    u"Waiting for temperature to reach %g °C (currently at %g °C)",
                    ttemp, atemp)
                time.sleep(1)

        raise TimeoutError("Target temperature (%g C) not reached after %g s" %
                           (comp.targetTemperature.value, timeout))
Exemplo n.º 3
0
    def _doWholeAcquisition(self, electron_coordinates, scale):
        """
        Perform acquisition with one optical image for all the spots.
        It's faster, but it's harder to separate the spots.
        """
        escan = self.escan
        ccd = self.ccd
        detector = self.detector
        dwell_time = self.dwell_time

        # order matters
        escan.scale.value = scale
        escan.resolution.value = self.repetitions
        escan.translation.value = (0, 0)

        # Scan at least 10 times, to avoids CCD/SEM synchronization problems
        sem_dt = escan.dwellTime.clip(dwell_time / 10)
        escan.dwellTime.value = sem_dt
        # For safety, ensure the exposure time is at least twice the time for a whole scan
        if dwell_time < 2 * sem_dt:
            dwell_time = 2 * sem_dt
            logging.info(
                "Increasing dwell time to %g s to avoid synchronization problems",
                dwell_time)

        # CCD setup
        ccd.binning.value = (1, 1)
        ccd.resolution.value = ccd.shape[0:2]
        et = numpy.prod(self.repetitions) * dwell_time
        ccd.exposureTime.value = et  # s
        readout = numpy.prod(ccd.resolution.value) / ccd.readoutRate.value
        tot_time = et + readout + 0.05

        try:
            if self._acq_state == CANCELLED:
                raise CancelledError()

            if self.bgsub:
                self.bg_image = ccd.data.get(asap=False)
            detector.data.subscribe(self._discard_data)
            self._min_acq_time = time.time()
            ccd.data.subscribe(self._onCCDImage)
            logging.debug("Scanning spot grid...")

            # Wait for CCD to capture the image
            if not self._ccd_done.wait(2 * tot_time + 4):
                raise TimeoutError("Acquisition of CCD timed out")

            with self._acq_lock:
                if self._acq_state == CANCELLED:
                    raise CancelledError()
                logging.debug("Scan done.")
                self._acq_state = FINISHED
        finally:
            detector.data.unsubscribe(self._discard_data)
            ccd.data.unsubscribe(self._onCCDImage)

        return self._optical_image, electron_coordinates, scale
Exemplo n.º 4
0
def _DoAcquisition(future, escan, ccd, detector, light):
    _sem_done.clear()

    try:
        if future._acq_state == CANCELLED:
            raise CancelledError()

        logging.debug("Acquiring CCD images...")

        # Turn on light for CCD acquisition
        intensities = [1, 0, 0, 0, 0, 0, 0]
        light.power.value = [
            ints * pw for ints, pw in zip(intensities, light.power.range[1])
        ]

        optical_image_1 = ccd.data.get()

        intensities = [0, 1, 0, 0, 0, 0, 0]
        light.power.value = [
            ints * pw for ints, pw in zip(intensities, light.power.range[1])
        ]

        optical_image_2 = ccd.data.get()

        intensities = [0, 0, 1, 0, 0, 0, 0]
        light.power.value = [
            ints * pw for ints, pw in zip(intensities, light.power.range[1])
        ]

        optical_image_3 = ccd.data.get()

        with _acq_lock:
            if future._acq_state == CANCELLED:
                raise CancelledError()
            logging.debug("Acquisition done.")
            future._acq_state = FINISHED

        # Turn off light for CCD acquisition
        light.power.value = light.power.range[0]

        logging.debug("Acquiring SEM image...")

        detector.data.subscribe(_ssOnSEMImage)
        # Wait for SEM to capture the image
        if not _sem_done.wait(2 * numpy.prod(escan.resolution.value) *
                              escan.dwellTime.value + 4):
            raise TimeoutError("Acquisition of SEM timed out")

        detector.data.unsubscribe(_ssOnSEMImage)

    finally:
        detector.data.unsubscribe(_ssOnSEMImage)

    return optical_image_1, optical_image_2, optical_image_3, detector.data._electron_image
Exemplo n.º 5
0
    def acquire_roa(self, dataflow):
        """
        Acquire the single field images that resemble the region of acquisition (ROA, megafield image).
        :param dataflow: (model.DataFlow) The dataflow on the detector.
        """

        total_field_time = self._detector.frameDuration.value + 1.5  # there is about 1.5 seconds overhead per field
        # The first field is acquired twice, so the timeout must be at least twice the total field time.
        # Use 5 times the total field time to have a wide margin.
        timeout = 5 * total_field_time + 2

        # Acquire all single field images, which are automatically offloaded to the external storage.
        for field_idx in self._roa.field_indices:
            # Reset the event that waits for the image being received (puts flag to false).
            self._data_received.clear()
            self.field_idx = field_idx
            logging.debug("Acquiring field with index: %s", field_idx)

            self.move_stage_to_next_tile(
            )  # move stage to next field image position

            self.correct_beam_shift(
            )  # correct the shift of the beams caused by the parasitic magnetic field.

            dataflow.next(field_idx)  # acquire the next field image.

            # Wait until single field image data has been received (image_received sets flag to True).
            if not self._data_received.wait(timeout):
                # TODO here we often timeout when actually just the offload queue is full
                #  need to handle offload queue error differently to just wait a bit instead of timing out
                #   -> check if finish megafield is called in finally when hitting here
                raise TimeoutError("Timeout while waiting for field image.")

            self._fields_remaining.discard(field_idx)

            # In case the acquisition was cancelled by a client, before the future returned, raise cancellation error.
            # Note: The acquisition of the current single field image (tile) is still finished though.
            if self._cancelled:
                raise CancelledError()

            # Update the time left for the acquisition.
            expected_time = len(self._fields_remaining) * total_field_time
            self._future.set_progress(start=time.time(),
                                      end=time.time() + expected_time)

        logging.debug("Successfully acquired all fields of ROA.")
Exemplo n.º 6
0
    def acquire_roa(self, dataflow):
        """
        Acquire the single field images that resemble the region of acquisition (ROA, megafield image).
        :param dataflow: (model.DataFlow) The dataflow on the detector.
        :return: (list of DataArrays): A list of the raw image data. Each data array (entire field, thumbnail,
                                       or zero array) represents one single field image within the ROA (megafield).
        """

        total_field_time = self._detector.frameDuration.value
        timeout = total_field_time + 5  # TODO what margin should be used?

        # Acquire all single field images, which are automatically offloaded to the external storage.
        for field_idx in self._roa.field_indices:
            # Reset the event that waits for the image being received (puts flag to false).
            self._data_received.clear()
            self.field_idx = field_idx
            logging.debug("Acquiring field with index: %s", field_idx)

            self.move_stage_to_next_tile(
            )  # move stage to next field image position

            if field_idx != (0, 0):
                self.correct_beam_shift(
                )  # correct the shift of the beams caused by the parasitic magnetic field.

            dataflow.next(field_idx)  # acquire the next field image.

            # Wait until single field image data has been received (image_received sets flag to True).
            if not self._data_received.wait(timeout):
                # TODO here we often timeout when actually just the offload queue is full
                #  need to handle offload queue error differently to just wait a bit instead of timing out
                #   -> check if finish megafield is called in finally when hitting here
                raise TimeoutError("Timeout while waiting for field image.")

            self._fields_remaining.discard(field_idx)

            # In case the acquisition was cancelled by a client, before the future returned, raise cancellation error.
            # Note: The acquisition of the current single field image (tile) is still finished though.
            if self._cancelled:
                raise CancelledError()

            # Update the time left for the acquisition.
            expected_time = len(self._fields_remaining) * total_field_time
            self._future.set_progress(end=time.time() + expected_time)

        return self.megafield
Exemplo n.º 7
0
    def move_stage_to_next_tile(self):
        """Move the stage to the next tile (field image) position."""

        pos_hor, pos_vert = self.get_abs_stage_movement(
        )  # get the absolute position for the new tile
        f = self._stage.moveAbs({
            'x': pos_hor,
            'y': pos_vert
        })  # move the stage
        timeout = 100
        try:
            f.result(timeout=timeout)  # don't wait forever
            logging.debug("Moved to stage position %s" %
                          (self._stage.position.value, ))
        except TimeoutError:
            raise TimeoutError(
                "Stage movement to position (%s, %s) timed out after %s s." %
                (timeout, pos_hor, pos_vert))
Exemplo n.º 8
0
    def _changePressure(self, p):
        """
        Synchronous change of the pressure
        p (float): target pressure
        """
        if p["pressure"] == PRESSURE_VENTED:
            self.parent._device.VacVent()
        else:
            self.parent._device.VacPump()

        start = time.time()
        while not self.GetStatus() == 0:
            if (time.time() - start) >= VACUUM_TIMEOUT:
                raise TimeoutError("Vacuum action timed out")
            # Update chamber pressure until pumping/venting process is done
            self._updatePosition()
        self._position = p
        self._updatePosition()
Exemplo n.º 9
0
    def _acq_wait_data(self, exp_tend, timeout=0):
        """
        Block until a data is received, or a stop message.
        Note: it expects that the acquisition is running.
        exp_tend (float): expected time the acquisition message is received
        timeout (0<=float): how long to wait to check (use 0 to not wait)
        return (bool): True if needs to stop, False if data is ready
        raise TerminationRequested: if a terminate message was received
        """
        now = time.time()
        ttimeout = now + timeout
        while now <= ttimeout:
            twait = max(1e-3, (exp_tend - now) / 2)
            logging.debug("Waiting for %g s", twait)
            if self._acq_should_stop(twait):
                return True

            # Is the data ready?
            if self.CTCStatus():
                logging.debug("Acq complete")
                return False
            now = time.time()

        raise TimeoutError("Acquisition timeout after %g s")
Exemplo n.º 10
0
    def _doSpotAcquisition(self, electron_coordinates, scale):
        """
        Perform acquisition spot per spot.
        Slow, but works even if SEM FoV is small
        """
        escan = self.escan
        ccd = self.ccd
        detector = self.detector
        dwell_time = self.dwell_time
        escan.scale.value = (1, 1)
        escan.resolution.value = (1, 1)

        # Set dt large enough so we unsubscribe before we even get an SEM
        # image (just to discard it) and start a second scan which would
        # cost in time.
        sem_dt = 2 * dwell_time
        escan.dwellTime.value = escan.dwellTime.clip(sem_dt)

        # CCD setup
        sem_shape = escan.shape[0:2]
        # sem ROI is ltrb
        sem_roi = (electron_coordinates[0][0] / sem_shape[0] + 0.5,
                   electron_coordinates[0][1] / sem_shape[1] + 0.5,
                   electron_coordinates[-1][0] / sem_shape[0] + 0.5,
                   electron_coordinates[-1][1] / sem_shape[1] + 0.5)
        ccd_roi = self.sem_roi_to_ccd(sem_roi)
        self.configure_ccd(ccd_roi)

        if self.bgsub:
            _set_blanker(self.escan, True)
            self.bg_image = ccd.data.get(asap=False)
            _set_blanker(self.escan, False)

        et = dwell_time
        ccd.exposureTime.value = et  # s
        readout = numpy.prod(ccd.resolution.value) / ccd.readoutRate.value
        tot_time = et + readout + 0.05
        logging.debug("Scanning spot grid with image per spot procedure...")

        self._spot_images = []
        for spot in electron_coordinates:
            self._ccd_done.clear()
            escan.translation.value = spot
            logging.debug("Scanning spot %s", escan.translation.value)
            try:
                if self._acq_state == CANCELLED:
                    raise CancelledError()
                detector.data.subscribe(self._discard_data)
                ccd.data.subscribe(self._onSpotImage)

                # Wait for CCD to capture the image
                if not self._ccd_done.wait(2 * tot_time + 4):
                    raise TimeoutError("Acquisition of CCD timed out")

            finally:
                detector.data.unsubscribe(self._discard_data)
                ccd.data.unsubscribe(self._onSpotImage)

        with self._acq_lock:
            if self._acq_state == CANCELLED:
                raise CancelledError()
            logging.debug("Scan done.")
            self._acq_state = FINISHED

        return self._spot_images, electron_coordinates, scale