def test_ab_rotation(self): """ Test typical rotation stage for the SECOM v1 A/B alignment """ child = simulated.Stage("stage", "test", axes=["a", "b"]) stage = ConvertStage("inclined", "align", {"orig": child}, axes=["b", "a"], rotation=math.radians(-135)) f = stage.moveRel({"x":1e-06, "y":2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x":1e-06, "y":2e-06}) self.assertPosAlmostEqual(child.position.value, {"a":-2.1213203435596424e-06, "b":7.071067811865477e-07}) f = stage.moveRel({"x":-1e-06, "y":-2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x":0, "y":0}) self.assertPosAlmostEqual(child.position.value, {"a":0, "b":0})
def setUpClass(cls): try: test.start_backend(SECOM_LENS_CONFIG) except LookupError: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return except IOError as exp: logging.error(str(exp)) raise # find components by their role cls.ebeam = model.getComponent(role="e-beam") cls.sed = model.getComponent(role="se-detector") cls.ccd = model.getComponent(role="ccd") cls.focus = model.getComponent(role="focus") cls.align = model.getComponent(role="align") cls.light = model.getComponent(role="light") cls.light_filter = model.getComponent(role="filter") # Used for OBJECTIVE_MOVE type cls.aligner_xy = ConvertStage("converter-ab", "stage", dependencies={"orig": cls.align}, axes=["b", "a"], rotation=math.radians(45))
def test_ab_rotation(self): """ Test typical rotation stage for the SECOM v1 A/B alignment """ child = simulated.Stage("stage", "test", axes=["a", "b"]) stage = ConvertStage("inclined", "align", {"orig": child}, axes=["b", "a"], rotation=math.radians(-135)) f = stage.moveRel({"x": 1e-06, "y": 2e-06}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 1e-06, "y": 2e-06}) test.assert_pos_almost_equal(child.position.value, {"a":-2.1213203435596424e-06, "b": 7.071067811865477e-07}) f = stage.moveRel({"x": -1e-06, "y": -2e-06}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) test.assert_pos_almost_equal(child.position.value, {"a": 0, "b": 0})
def test_move_abs(self): child = simulated.Stage("stage", "test", axes=["x", "y"]) # no transformation stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"]) self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) f = stage.moveAbs({"x": 1e-06, "y": 2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, { "x": 1e-06, "y": 2e-06 }) self.assertPosAlmostEqual(child.position.value, { "x": 1e-06, "y": 2e-06 }) f = stage.moveAbs({"x": 0, "y": 0}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0}) # scaling stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], scale=(10, 10)) self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) f = stage.moveAbs({"x": 1e-06, "y": 2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, { "x": 1e-06, "y": 2e-06 }) self.assertPosAlmostEqual(child.position.value, { "x": 1e-05, "y": 2e-05 }) f = stage.moveAbs({"x": 0, "y": 0}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0}) # rotation stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], rotation=math.pi / 2) self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) f = stage.moveAbs({"x": 1e-06, "y": 2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, { "x": 1e-06, "y": 2e-06 }) self.assertPosAlmostEqual(child.position.value, { "x": -2e-06, "y": 1e-06 }) f = stage.moveAbs({"x": 0, "y": 0}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0}) # offset stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], translation=(1e-06, 2e-06)) self.assertPosAlmostEqual(stage.position.value, { "x": -1e-06, "y": -2e-06 }) f = stage.moveAbs({"x": 0, "y": 0}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, { "x": 1e-06, "y": 2e-06 }) f = stage.moveAbs({"x": -1e-06, "y": -2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, { "x": -1e-06, "y": -2e-06 }) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0}) # offset + scaling stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], translation=(1e-06, 2e-06), scale=(10, 10)) self.assertPosAlmostEqual(stage.position.value, { "x": -1e-06, "y": -2e-06 }) f = stage.moveAbs({"x": 0, "y": 0}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, { "x": 1e-05, "y": 2e-05 })
def test_move_abs(self): child = simulated.Stage("stage", "test", axes=["x", "y"]) # no transformation stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"]) test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) f = stage.moveAbs({"x": 1e-06, "y": 2e-06}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 1e-06, "y": 2e-06}) test.assert_pos_almost_equal(child.position.value, {"x": 1e-06, "y": 2e-06}) f = stage.moveAbs({"x": 0, "y": 0}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) test.assert_pos_almost_equal(child.position.value, {"x": 0, "y": 0}) # scaling stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], scale=(10, 10)) test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) f = stage.moveAbs({"x": 1e-06, "y": 2e-06}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 1e-06, "y": 2e-06}) test.assert_pos_almost_equal(child.position.value, {"x": 1e-05, "y": 2e-05}) f = stage.moveAbs({"x": 0, "y": 0}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) test.assert_pos_almost_equal(child.position.value, {"x": 0, "y": 0}) # rotation stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], rotation=math.pi / 2) test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) f = stage.moveAbs({"x": 1e-06, "y": 2e-06}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 1e-06, "y": 2e-06}) test.assert_pos_almost_equal(child.position.value, {"x":-2e-06, "y": 1e-06}) f = stage.moveAbs({"x": 0, "y": 0}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) test.assert_pos_almost_equal(child.position.value, {"x": 0, "y": 0}) # offset stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], translation=(1e-06, 2e-06)) test.assert_pos_almost_equal(stage.position.value, {"x":-1e-06, "y":-2e-06}) f = stage.moveAbs({"x": 0, "y": 0}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) test.assert_pos_almost_equal(child.position.value, {"x": 1e-06, "y": 2e-06}) f = stage.moveAbs({"x": -1e-06, "y": -2e-06}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x":-1e-06, "y":-2e-06}) test.assert_pos_almost_equal(child.position.value, {"x": 0, "y": 0}) # offset + scaling stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], translation=(1e-06, 2e-06), scale=(10, 10)) test.assert_pos_almost_equal(stage.position.value, {"x":-1e-06, "y":-2e-06}) f = stage.moveAbs({"x": 0, "y": 0}) f.result() test.assert_pos_almost_equal(stage.position.value, {"x": 0, "y": 0}) test.assert_pos_almost_equal(child.position.value, {"x": 1e-05, "y": 2e-05})
def test_move_rel(self): child = simulated.Stage("stage", "test", axes=["x", "y"]) # no transformation stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"]) self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) f = stage.moveRel({"x": 1e-06, "y": 2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 1e-06, "y": 2e-06}) self.assertPosAlmostEqual(child.position.value, {"x": 1e-06, "y": 2e-06}) f = stage.moveRel({"x": -1e-06, "y": -2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0}) # scaling stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], scale=(10, 10)) self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) f = stage.moveRel({"x": 1e-06, "y": 2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 1e-06, "y": 2e-06}) self.assertPosAlmostEqual(child.position.value, {"x": 10e-06, "y": 20e-06}) f = stage.moveRel({"x": -1e-06, "y": -2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0}) # rotation stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], rotation=math.pi / 2) self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) f = stage.moveRel({"x": 1e-06, "y": 2e-06}) f.result() self.assertEqual(stage.position.value, {"x": 1e-06, "y": 2e-06}) self.assertPosAlmostEqual(child.position.value, {"x": -2e-06, "y": 1e-06}) f = stage.moveRel({"x": -1e-06, "y": -2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0}) # offset stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], translation=(1e-06, 2e-06)) self.assertPosAlmostEqual(stage.position.value, {"x": -1e-06, "y": -2e-06}) f = stage.moveRel({"x": 1e-06, "y": 2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, {"x": 1e-06, "y": 2e-06}) f = stage.moveRel({"x": -1e-06, "y": -2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": -1e-06, "y": -2e-06}) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0}) # offset + scaling stage = ConvertStage("conv", "align", {"orig": child}, axes=["x", "y"], translation=(1e-06, 2e-06), scale=(10, 10)) self.assertPosAlmostEqual(stage.position.value, {"x": -1e-06, "y": -2e-06}) f = stage.moveRel({"x": 1e-06, "y": 2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": 0, "y": 0}) self.assertPosAlmostEqual(child.position.value, {"x": 10e-06, "y": 20e-06}) f = stage.moveRel({"x": -1e-06, "y": -2e-06}) f.result() self.assertPosAlmostEqual(stage.position.value, {"x": -1e-06, "y": -2e-06}) self.assertPosAlmostEqual(child.position.value, {"x": 0, "y": 0})
def _DoCenterSpot(future, ccd, stage, escan, mx_steps, type, background, dataflow): """ Iteratively acquires an optical image, finds the coordinates of the spot (center) and moves the stage to this position. Repeats until the found coordinates are at the center of the optical image or a maximum number of steps is reached. future (model.ProgressiveFuture): Progressive future provided by the wrapper ccd (model.DigitalCamera): The CCD stage (model.Actuator): The stage escan (model.Emitter): The e-beam scanner mx_steps (int): Maximum number of steps to reach the center type (string): Type of move in order to align returns (float or None): Final distance to the center #m raises: CancelledError() if cancelled """ try: logging.debug("Aligning spot...") if type == OBJECTIVE_MOVE: stage_ab = ConvertStage("converter-ab", "stage", children={"orig": stage}, axes=["b", "a"], rotation=math.radians(-135)) image = ccd.data.get(asap=False) # Center of optical image pixelSize = image.metadata[model.MD_PIXEL_SIZE] center_pxs = (image.shape[1] / 2, image.shape[0] / 2) # Epsilon distance below which the lens is considered centered. The worse of: # * 1.5 pixels (because the CCD resolution cannot give us better) # * 1 µm (because that's the best resolution of our actuators) err_mrg = max(1.5 * pixelSize[0], 1e-06) # m steps = 0 # Stop once spot is found on the center of the optical image dist = None while True: if future._spot_center_state == CANCELLED: raise CancelledError() # Or once max number of steps is reached if steps >= mx_steps: break # Wait to make sure no previous spot is detected image = SubstractBackground(ccd, dataflow) try: spot_pxs = FindSpot(image) except ValueError: return None, None tab_pxs = [a - b for a, b in zip(spot_pxs, center_pxs)] tab = (tab_pxs[0] * pixelSize[0], tab_pxs[1] * pixelSize[1]) dist = math.hypot(*tab) # If we are already there, stop if dist <= err_mrg: break # Move to the found spot if type == OBJECTIVE_MOVE: f = stage_ab.moveRel({"x": tab[0], "y": -tab[1]}) f.result() elif type == STAGE_MOVE: f = stage.moveRel({"x": -tab[0], "y": tab[1]}) f.result() else: escan.translation.value = (-tab_pxs[0], -tab_pxs[1]) steps += 1 # Update progress of the future future.set_end_time( time.time() + estimateCenterTime(ccd.exposureTime.value, dist)) return dist, tab finally: with future._center_lock: if future._spot_center_state == CANCELLED: raise CancelledError() future._spot_center_state = FINISHED
def _DoCenterSpot(future, ccd, stage, escan, mx_steps, type, background, dataflow): """ Iteratively acquires an optical image, finds the coordinates of the spot (center) and moves the stage to this position. Repeats until the found coordinates are at the center of the optical image or a maximum number of steps is reached. future (model.ProgressiveFuture): Progressive future provided by the wrapper ccd (model.DigitalCamera): The CCD stage (model.Actuator): The stage escan (model.Emitter): The e-beam scanner mx_steps (int): Maximum number of steps to reach the center type (string): Type of move in order to align returns (float or None): Final distance to the center #m raises: CancelledError() if cancelled """ try: logging.debug("Aligning spot...") if type == OBJECTIVE_MOVE: stage_ab = ConvertStage("converter-ab", "stage", children={"orig": stage}, axes=["b", "a"], rotation=math.radians(-135)) image = ccd.data.get(asap=False) # Center of optical image pixelSize = image.metadata[model.MD_PIXEL_SIZE] center_pxs = (image.shape[1] / 2, image.shape[0] / 2) # Epsilon distance below which the lens is considered centered. The worse of: # * 1.5 pixels (because the CCD resolution cannot give us better) # * 1 µm (because that's the best resolution of our actuators) err_mrg = max(1.5 * pixelSize[0], 1e-06) # m steps = 0 # Stop once spot is found on the center of the optical image dist = None while True: if future._spot_center_state == CANCELLED: raise CancelledError() # Or once max number of steps is reached if steps >= mx_steps: break # Wait to make sure no previous spot is detected image = SubstractBackground(ccd, dataflow) try: spot_pxs = FindSpot(image) except ValueError: return None, None tab_pxs = [a - b for a, b in zip(spot_pxs, center_pxs)] tab = (tab_pxs[0] * pixelSize[0], tab_pxs[1] * pixelSize[1]) dist = math.hypot(*tab) # If we are already there, stop if dist <= err_mrg: break # Move to the found spot if type == OBJECTIVE_MOVE: f = stage_ab.moveRel({"x":tab[0], "y":-tab[1]}) f.result() elif type == STAGE_MOVE: f = stage.moveRel({"x":-tab[0], "y":tab[1]}) f.result() else: escan.translation.value = (-tab_pxs[0], -tab_pxs[1]) steps += 1 # Update progress of the future future.set_end_time(time.time() + estimateCenterTime(ccd.exposureTime.value, dist)) return dist, tab finally: with future._center_lock: if future._spot_center_state == CANCELLED: raise CancelledError() future._spot_center_state = FINISHED