pytribeam.stage
Stage Module
This module contains functions for managing and controlling the stage in the microscope, including setting coordinate systems, moving the stage, and checking stage positions.
Functions
coordinate_system(microscope: tbt.Microscope, mode: tbt.StageCoordinateSystem = tbt.StageCoordinateSystem.RAW) -> bool Set the stage coordinate system mode.
stop(microscope: tbt.Microscope) -> None Immediately stop all stage movement and exit.
encoder_to_user_position(pos: tbt.StagePositionEncoder) -> tbt.StagePositionUser Convert from encoder position (m/radian units) to user position (mm/deg units).
user_to_encoder_position(pos: tbt.StagePositionUser) -> tbt.StagePositionEncoder Convert from user position (mm/deg units) to encoder position (m/radian units).
rotation_side_adjustment(rotation_side: tbt.RotationSide, initial_position_m: float, delta_pos_m: float) -> float Adjust the translation stage destination based on the rotation side.
target_position(stage: tbt.StageSettings, slice_number: int, slice_thickness_um: float) -> tbt.StagePositionUser Calculate the target position for the stage movement.
safe(microscope: tbt.Microscope, position: tbt.StagePositionUser) -> bool Check if the target position is within the stage limits.
axis_translational_in_range(current_pos_mm: float, target_pos_mm: float, stage_tolerance_um: float) -> bool Determine whether the translation axis needs to be moved.
axis_angular_in_range(current_pos_deg: float, target_pos_deg: float, stage_tolerance_deg: float) -> bool Determine whether the angular axis needs to be moved.
axis_in_range(microscope: tbt.Microscope, axis: tbt.StageAxis, target_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance) -> bool Check whether the position of the specified axis is within the stage tolerance.
move_axis(microscope: tbt.Microscope, axis: tbt.StageAxis, target_position: tbt.StagePositionUser, num_attempts: int = cs.Constants.stage_move_attempts, stage_delay_s: float = cs.Constants.stage_move_delay_s) -> bool Move the specified stage axis to the requested user target position.
move_stage(microscope: tbt.Microscope, target_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance) -> bool Move the stage axes if they are outside of tolerance.
move_completed(microscope: tbt.Microscope, target_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance) -> bool Check whether the stage is at the target position.
home_stage(microscope: tbt.Microscope, stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance) -> bool Move the stage to the home position defined in pytribeam.constants.
move_to_position(microscope: tbt.Microscope, target_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance) -> bool Move the stage to the target position with error checking.
_bad_axes_message(target_position: tbt.StagePositionUser, current_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance) -> str Generate an error message for axes that are out of tolerance.
step_start_position(microscope: tbt.Microscope, slice_number: int, operation: tbt.Step, general_settings: tbt.GeneralSettings) -> bool Move the stage to the starting position for the step.
1#!/usr/bin/python3 2""" 3Stage Module 4============ 5 6This module contains functions for managing and controlling the stage in the microscope, including setting coordinate systems, moving the stage, and checking stage positions. 7 8Functions 9--------- 10coordinate_system(microscope: tbt.Microscope, mode: tbt.StageCoordinateSystem = tbt.StageCoordinateSystem.RAW) -> bool 11 Set the stage coordinate system mode. 12 13stop(microscope: tbt.Microscope) -> None 14 Immediately stop all stage movement and exit. 15 16encoder_to_user_position(pos: tbt.StagePositionEncoder) -> tbt.StagePositionUser 17 Convert from encoder position (m/radian units) to user position (mm/deg units). 18 19user_to_encoder_position(pos: tbt.StagePositionUser) -> tbt.StagePositionEncoder 20 Convert from user position (mm/deg units) to encoder position (m/radian units). 21 22rotation_side_adjustment(rotation_side: tbt.RotationSide, initial_position_m: float, delta_pos_m: float) -> float 23 Adjust the translation stage destination based on the rotation side. 24 25target_position(stage: tbt.StageSettings, slice_number: int, slice_thickness_um: float) -> tbt.StagePositionUser 26 Calculate the target position for the stage movement. 27 28safe(microscope: tbt.Microscope, position: tbt.StagePositionUser) -> bool 29 Check if the target position is within the stage limits. 30 31axis_translational_in_range(current_pos_mm: float, target_pos_mm: float, stage_tolerance_um: float) -> bool 32 Determine whether the translation axis needs to be moved. 33 34axis_angular_in_range(current_pos_deg: float, target_pos_deg: float, stage_tolerance_deg: float) -> bool 35 Determine whether the angular axis needs to be moved. 36 37axis_in_range(microscope: tbt.Microscope, axis: tbt.StageAxis, target_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance) -> bool 38 Check whether the position of the specified axis is within the stage tolerance. 39 40move_axis(microscope: tbt.Microscope, axis: tbt.StageAxis, target_position: tbt.StagePositionUser, num_attempts: int = cs.Constants.stage_move_attempts, stage_delay_s: float = cs.Constants.stage_move_delay_s) -> bool 41 Move the specified stage axis to the requested user target position. 42 43move_stage(microscope: tbt.Microscope, target_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance) -> bool 44 Move the stage axes if they are outside of tolerance. 45 46move_completed(microscope: tbt.Microscope, target_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance) -> bool 47 Check whether the stage is at the target position. 48 49home_stage(microscope: tbt.Microscope, stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance) -> bool 50 Move the stage to the home position defined in pytribeam.constants. 51 52move_to_position(microscope: tbt.Microscope, target_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance) -> bool 53 Move the stage to the target position with error checking. 54 55_bad_axes_message(target_position: tbt.StagePositionUser, current_position: tbt.StagePositionUser, stage_tolerance: tbt.StageTolerance) -> str 56 Generate an error message for axes that are out of tolerance. 57 58step_start_position(microscope: tbt.Microscope, slice_number: int, operation: tbt.Step, general_settings: tbt.GeneralSettings) -> bool 59 Move the stage to the starting position for the step. 60""" 61 62# Default python modules 63# from functools import singledispatch 64import os 65from pathlib import Path 66import time 67import warnings 68import math 69from typing import NamedTuple, List, Tuple 70import sys 71 72# Autoscript included modules 73import numpy as np 74from matplotlib import pyplot as plt 75 76# 3rd party module 77 78# Local scripts 79import pytribeam.constants as cs 80from pytribeam.constants import Conversions 81import pytribeam.insertable_devices as devices 82import pytribeam.factory as factory 83import pytribeam.types as tbt 84import pytribeam.utilities as ut 85 86 87def coordinate_system( 88 microscope: tbt.Microscope, 89 mode: tbt.StageCoordinateSystem = tbt.StageCoordinateSystem.RAW, 90) -> bool: 91 """ 92 Set the stage coordinate system mode. 93 94 This function sets the stage coordinate system mode. The default mode is "RAW", which is recommended. 95 96 Parameters 97 ---------- 98 microscope : tbt.Microscope 99 The microscope object for which to set the stage coordinate system mode. 100 mode : tbt.StageCoordinateSystem, optional 101 The stage coordinate system mode to set (default is tbt.StageCoordinateSystem.RAW). 102 103 Returns 104 ------- 105 bool 106 True if the coordinate system mode is set successfully. 107 """ 108 if mode != tbt.StageCoordinateSystem.RAW: 109 warnings.warn( 110 f"""Warning. {mode.value} coordinate system requested 111 instead of "RAW" stage coordinate system. This can lead 112 to inaccurate stage movements and increases collision risk.""" 113 ) 114 # TODO ask for yes no continue 115 microscope.specimen.stage.set_default_coordinate_system(mode.value) 116 return True 117 118 119def stop(microscope: tbt.Microscope) -> None: 120 """ 121 Immediately stop all stage movement and exit. 122 123 This function stops all stage movement and disconnects the microscope. 124 125 Parameters 126 ---------- 127 microscope : tbt.Microscope 128 The microscope object for which to stop stage movement. 129 130 Raises 131 ------ 132 SystemError 133 If the stage movement is halted. 134 """ 135 microscope.specimen.stage.stop() 136 microscope.disconnect() 137 138 raise SystemError("Microscope stage movement was halted.") 139 # sys.exit("Microscope stage movement was halted.") 140 141 142def encoder_to_user_position(pos: tbt.StagePositionEncoder) -> tbt.StagePositionUser: 143 """ 144 Convert from encoder position (m/radian units) to user position (mm/deg units). 145 146 This function converts a stage position from encoder units to user units. 147 148 Parameters 149 ---------- 150 pos : tbt.StagePositionEncoder 151 The stage position in encoder units. 152 153 Returns 154 ------- 155 tbt.StagePositionUser 156 The stage position in user units. 157 158 Raises 159 ------ 160 TypeError 161 If the provided position is not of type tbt.StagePositionEncoder. 162 """ 163 if not isinstance(pos, tbt.StagePositionEncoder): 164 raise TypeError( 165 f"provided position is not of type '<class pytribeam.types.StagePositionEncoder>', but instead of type '{type(pos)}'. Did you mean to use the function 'user_to_encoder_position'?" 166 ) 167 user_pos = tbt.StagePositionUser( 168 x_mm=round(pos.x * Conversions.M_TO_MM, 6), 169 y_mm=round(pos.y * Conversions.M_TO_MM, 6), 170 z_mm=round(pos.z * Conversions.M_TO_MM, 6), 171 r_deg=round(pos.r * Conversions.RAD_TO_DEG, 6), 172 t_deg=round(pos.t * Conversions.RAD_TO_DEG, 6), 173 coordinate_system=tbt.StageCoordinateSystem(pos.coordinate_system), 174 ) 175 return user_pos 176 177 178def user_to_encoder_position(pos: tbt.StagePositionUser) -> tbt.StagePositionEncoder: 179 """ 180 Convert from user position (mm/deg units) to encoder position (m/radian units). 181 182 This function converts a stage position from user units to encoder units. 183 184 Parameters 185 ---------- 186 pos : tbt.StagePositionUser 187 The stage position in user units. 188 189 Returns 190 ------- 191 tbt.StagePositionEncoder 192 The stage position in encoder units. 193 194 Raises 195 ------ 196 TypeError 197 If the provided position is not of type tbt.StagePositionUser. 198 """ 199 200 if not isinstance(pos, tbt.StagePositionUser): 201 raise TypeError( 202 f"Provided position is not of type '<class pytribeam.types.StagePositionUser>', but instead of type '{type(pos)}'. Did you mean to use the function 'encoder_to_user_position'?" 203 ) 204 encoder_pos = tbt.StagePositionEncoder( 205 x=pos.x_mm * Conversions.MM_TO_M, 206 y=pos.y_mm * Conversions.MM_TO_M, 207 z=pos.z_mm * Conversions.MM_TO_M, 208 r=pos.r_deg * Conversions.DEG_TO_RAD, 209 t=pos.t_deg * Conversions.DEG_TO_RAD, 210 coordinate_system=pos.coordinate_system.value, 211 ) 212 return encoder_pos 213 214 215# TODO not yet implemented for other sectioning axes, below example works on Z-sectioning 216def rotation_side_adjustment( 217 rotation_side: tbt.RotationSide, 218 initial_position_m: float, 219 delta_pos_m: float, 220) -> float: 221 """ 222 Adjust the translation stage destination based on the rotation side. 223 224 This function adjusts the translation stage destination based on the specified rotation side. 225 226 Parameters 227 ---------- 228 rotation_side : tbt.RotationSide 229 The rotation side to consider for the adjustment. 230 initial_position_m : float 231 The initial position in meters. 232 delta_pos_m : float 233 The change in position in meters. 234 235 Returns 236 ------- 237 float 238 The adjusted target position in meters. 239 240 Raises 241 ------ 242 NotImplementedError 243 If the rotation side is unsupported. 244 """ 245 if rotation_side == tbt.RotationSide.FSL_MILL: 246 target_m = ( 247 initial_position_m - delta_pos_m 248 ) # absolute negative direction (towards laser) 249 elif rotation_side == tbt.RotationSide.FIB_MILL: 250 target_m = ( 251 initial_position_m + delta_pos_m 252 ) # absolute positive direction (towards FIB) 253 elif rotation_side == tbt.RotationSide.EBEAM_NORMAL: 254 target_m = initial_position_m # no adjustment needed 255 else: 256 raise NotImplementedError( 257 f"Unsupported RotationSide enumeration of '{rotation_side}'" 258 ) 259 return target_m 260 261 262def target_position( 263 stage: tbt.StageSettings, 264 slice_number: int, 265 slice_thickness_um: float, 266) -> tbt.StagePositionUser: 267 """ 268 Calculate the target position for the stage movement. 269 270 This function calculates the target position for the stage movement based on the sectioning axis, slice number, and slice thickness. 271 272 For Z-axis sectioning: 273 - Z will always incrememnt toward pole piece (positive direction) 274 - Y will increment with slice number if a non-zero pre-tilt is used 275 - Y increment direction depends on rotation of the stage relative to machining operations 276 277 For X-axis sectioning (not yet implemented, need to determine rotation_side_adjustment) 278 For Y-axis sectioning (not yet implemented, need to determine rotation_side_adjustment) 279 280 Parameters 281 ---------- 282 stage : tbt.StageSettings 283 The stage settings for the experiment. 284 slice_number : int 285 The slice number for the experiment. 286 slice_thickness_um : float 287 The slice thickness in micrometers. 288 289 Returns 290 ------- 291 tbt.StagePositionUser 292 The target position for the stage movement. 293 294 Raises 295 ------ 296 NotImplementedError 297 If the sectioning axis is unsupported. 298 """ 299 300 initial_pos_user = stage.initial_position 301 initial_pos_encoder = user_to_encoder_position(initial_pos_user) 302 slice_thickness_m = slice_thickness_um * Conversions.UM_TO_M 303 pre_tilt_rad = stage.pretilt_angle_deg * Conversions.DEG_TO_RAD 304 sectioning_axis = stage.sectioning_axis 305 rotation_side = stage.rotation_side 306 increment_factor_m = slice_thickness_m * (slice_number - 1) # slices are 1-indexed 307 308 # only modify needed axes for each case, so initialize with original position 309 target_x_m = initial_pos_encoder.x 310 target_y_m = initial_pos_encoder.y 311 target_z_m = initial_pos_encoder.z 312 target_r_rad = initial_pos_encoder.r 313 target_t_rad = initial_pos_encoder.t 314 315 if sectioning_axis == tbt.SectioningAxis.Z: 316 delta_z_m = math.cos(pre_tilt_rad) * increment_factor_m 317 # Z always increments in positive direction 318 target_z_m = initial_pos_encoder.z + delta_z_m 319 320 # Y axis depends on rotation_side 321 delta_y_m = math.sin(pre_tilt_rad) * increment_factor_m 322 323 if rotation_side == tbt.RotationSide.FSL_MILL: 324 target_y_m = ( 325 initial_pos_encoder.y - delta_y_m 326 ) # absolute negative direction (towards laser) 327 elif rotation_side == tbt.RotationSide.FIB_MILL: 328 target_y_m = ( 329 initial_pos_encoder.y + delta_y_m 330 ) # absolute positive direction (towards FIB) 331 elif rotation_side == tbt.RotationSide.EBEAM_NORMAL: 332 target_y_m = initial_pos_encoder.y # no adjustment needed 333 else: 334 raise NotImplementedError( 335 f"Unsupported RotationSide enumeration of '{rotation_side}'" 336 ) 337 338 # TODO 339 elif sectioning_axis == tbt.SectioningAxis.X_POS: 340 raise NotImplementedError("Currently only Z-axis sectioning is supported.") 341 delta_x_m = increment_factor_m 342 pass 343 elif sectioning_axis == tbt.SectioningAxis.X_NEG: 344 raise NotImplementedError("Currently only Z-axis sectioning is supported.") 345 delta_x_m = increment_factor_m 346 pass 347 elif sectioning_axis == tbt.SectioningAxis.Y_POS: 348 raise NotImplementedError("Currently only Z-axis sectioning is supported.") 349 delta_x_m = increment_factor_m 350 pass 351 elif sectioning_axis == tbt.SectioningAxis.Y_NEG: 352 raise NotImplementedError("Currently only Z-axis sectioning is supported.") 353 delta_x_m = increment_factor_m 354 pass 355 356 target_pos_encoder = tbt.StagePositionEncoder( 357 x=target_x_m, 358 y=target_y_m, 359 z=target_z_m, 360 r=target_r_rad, 361 t=target_t_rad, 362 coordinate_system=tbt.StageCoordinateSystem.RAW.value, 363 ) 364 target_pos_user = encoder_to_user_position(target_pos_encoder) 365 return target_pos_user 366 367 368def safe( 369 microscope: tbt.Microscope, 370 position: tbt.StagePositionUser, 371) -> bool: 372 """ 373 Check if the target position is within the stage limits. 374 375 This function checks if the target position is within the stage limits. 376 377 Parameters 378 ---------- 379 microscope : tbt.Microscope 380 The microscope object for which to check the stage limits. 381 position : tbt.StagePositionUser 382 The target position to check. 383 384 Returns 385 ------- 386 bool 387 True if the target position is within the stage limits, False otherwise. 388 """ 389 390 # returns in user units (mm, deg) 391 stage_limits = factory.stage_limits(microscope=microscope) 392 393 if not ut.in_interval( 394 position.x_mm, stage_limits.x_mm, type=tbt.IntervalType.CLOSED 395 ): 396 return False 397 if not ut.in_interval( 398 position.y_mm, stage_limits.y_mm, type=tbt.IntervalType.CLOSED 399 ): 400 return False 401 if not ut.in_interval( 402 position.z_mm, stage_limits.z_mm, type=tbt.IntervalType.CLOSED 403 ): 404 return False 405 if not ut.in_interval( 406 position.t_deg, stage_limits.t_deg, type=tbt.IntervalType.CLOSED 407 ): 408 return False 409 if not ut.in_interval( 410 position.r_deg, stage_limits.r_deg, type=tbt.IntervalType.CLOSED 411 ): 412 return False 413 414 return True 415 416 417def axis_translational_in_range( 418 current_pos_mm: float, 419 target_pos_mm: float, 420 stage_tolerance_um: float, 421) -> bool: 422 """ 423 Determine whether the translation axis needs to be moved. 424 425 This function checks if the translation axis is within the specified tolerance. 426 427 Parameters 428 ---------- 429 current_pos_mm : float 430 The current position of the translation axis in millimeters. 431 target_pos_mm : float 432 The target position of the translation axis in millimeters. 433 stage_tolerance_um : float 434 The tolerance for the translation axis in micrometers. 435 436 Returns 437 ------- 438 bool 439 True if the translation axis is within the specified tolerance, False otherwise. 440 """ 441 return ut.in_interval( 442 current_pos_mm, 443 limit=tbt.Limit( 444 min=target_pos_mm - (stage_tolerance_um * Conversions.UM_TO_MM), 445 max=target_pos_mm + (stage_tolerance_um * Conversions.UM_TO_MM), 446 ), 447 type=tbt.IntervalType.CLOSED, 448 ) 449 450 451def axis_angular_in_range( 452 current_pos_deg: float, 453 target_pos_deg: float, 454 stage_tolerance_deg: float, 455) -> bool: 456 """ 457 Determine whether the angular axis needs to be moved. 458 459 This function checks if the angular axis is within the specified tolerance. 460 461 Parameters 462 ---------- 463 current_pos_deg : float 464 The current position of the angular axis in degrees. 465 target_pos_deg : float 466 The target position of the angular axis in degrees. 467 stage_tolerance_deg : float 468 The tolerance for the angular axis in degrees. 469 470 Returns 471 ------- 472 bool 473 True if the angular axis is within the specified tolerance, False otherwise. 474 """ 475 return ut.in_interval( 476 current_pos_deg, 477 limit=tbt.Limit( 478 min=target_pos_deg - (stage_tolerance_deg * Conversions.DEG_TO_RAD), 479 max=target_pos_deg + (stage_tolerance_deg * Conversions.DEG_TO_RAD), 480 ), 481 type=tbt.IntervalType.CLOSED, 482 ) 483 484 485def axis_in_range( 486 microscope: tbt.Microscope, 487 axis: tbt.StageAxis, 488 target_position: tbt.StagePositionUser, 489 stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance, 490) -> bool: 491 """ 492 Check whether the position of the specified axis is within the stage tolerance. 493 494 This function checks if the position of the specified axis is within the stage tolerance. 495 496 Parameters 497 ---------- 498 microscope : tbt.Microscope 499 The microscope object for which to check the axis position. 500 axis : tbt.StageAxis 501 The axis to check. 502 target_position : tbt.StagePositionUser 503 The target position to check. 504 stage_tolerance : tbt.StageTolerance, optional 505 The stage tolerance for the axis (default is cs.Constants.default_stage_tolerance). 506 507 Returns 508 ------- 509 bool 510 True if the axis position is within the stage tolerance, False otherwise. 511 """ 512 current_position = factory.active_stage_position_settings( 513 microscope=microscope 514 ) # user units [mm_deg] 515 516 # TODO convert to match statements at python >=3.10 517 match_db = { 518 tbt.StageAxis.X: { 519 "current_position": current_position.x_mm, 520 "target_position": target_position.x_mm, 521 "tolerance": stage_tolerance.translational_um * Conversions.UM_TO_MM, 522 }, 523 tbt.StageAxis.Y: { 524 "current_position": current_position.y_mm, 525 "target_position": target_position.y_mm, 526 "tolerance": stage_tolerance.translational_um * Conversions.UM_TO_MM, 527 }, 528 tbt.StageAxis.Z: { 529 "current_position": current_position.z_mm, 530 "target_position": target_position.z_mm, 531 "tolerance": stage_tolerance.translational_um * Conversions.UM_TO_MM, 532 }, 533 tbt.StageAxis.R: { 534 "current_position": current_position.r_deg, 535 "target_position": target_position.r_deg, 536 "tolerance": stage_tolerance.angular_deg, 537 }, 538 tbt.StageAxis.T: { 539 "current_position": current_position.t_deg, 540 "target_position": target_position.t_deg, 541 "tolerance": stage_tolerance.angular_deg, 542 }, 543 } 544 545 return ut.in_interval( 546 val=match_db[axis]["current_position"], 547 limit=tbt.Limit( 548 min=match_db[axis]["target_position"] - match_db[axis]["tolerance"], 549 max=match_db[axis]["target_position"] + match_db[axis]["tolerance"], 550 ), 551 type=tbt.IntervalType.CLOSED, 552 ) 553 554 555def move_axis( 556 microscope: tbt.Microscope, 557 axis: tbt.StageAxis, 558 target_position: tbt.StagePositionUser, 559 num_attempts: int = cs.Constants.stage_move_attempts, 560 stage_delay_s: float = cs.Constants.stage_move_delay_s, 561) -> bool: 562 """ 563 Move the specified stage axis to the requested user target position. 564 565 This function moves the specified stage axis to the requested user target position. 566 567 Parameters 568 ---------- 569 microscope : tbt.Microscope 570 The microscope object for which to move the stage axis. 571 axis : tbt.StageAxis 572 The stage axis to move. 573 target_position : tbt.StagePositionUser 574 The target position to move the axis to. 575 num_attempts : int, optional 576 The number of attempts to move the axis (default is cs.Constants.stage_move_attempts). 577 stage_delay_s : float, optional 578 The delay in seconds between attempts (default is cs.Constants.stage_move_delay_s). 579 580 Returns 581 ------- 582 bool 583 True if the axis is moved to the target position successfully. 584 """ 585 encoder_position = user_to_encoder_position(target_position) 586 # TODO convert to match statements at python >=3.10 587 match_db = { 588 tbt.StageAxis.X: tbt.StagePositionEncoder(x=encoder_position.x), 589 tbt.StageAxis.Y: tbt.StagePositionEncoder(y=encoder_position.y), 590 tbt.StageAxis.Z: tbt.StagePositionEncoder(z=encoder_position.z), 591 tbt.StageAxis.R: tbt.StagePositionEncoder(r=encoder_position.r), 592 tbt.StageAxis.T: tbt.StagePositionEncoder(t=encoder_position.t), 593 } 594 for _ in range(num_attempts): 595 microscope.specimen.stage.absolute_move(match_db[axis]) 596 time.sleep(stage_delay_s) 597 return True 598 599 600def move_stage( 601 microscope: tbt.Microscope, 602 target_position: tbt.StagePositionUser, 603 stage_tolerance: tbt.StageTolerance, 604) -> bool: 605 """ 606 Move the stage axes if they are outside of tolerance. 607 608 This function moves the stage axes to the target position if they are outside of the specified tolerance. The stage axes are moved one at a time in the following sequence: 609 - R-axis: if needed, tilt will be adjusted to 0 degrees first for safety 610 - X-axis 611 - Y-axis 612 - Z-axis 613 - T-axis 614 615 Parameters 616 ---------- 617 microscope : tbt.Microscope 618 The microscope object for which to move the stage. 619 target_position : tbt.StagePositionUser 620 The target position to move the stage to. 621 stage_tolerance : tbt.StageTolerance 622 The stage tolerance for the movement. 623 624 Returns 625 ------- 626 bool 627 True if the stage is moved to the target position successfully. 628 """ 629 630 # ensure RAW specimen coordiantes 631 coordinate_system(microscope=microscope, mode=tbt.StageCoordinateSystem.RAW) 632 633 # r-axis first for safety 634 if not axis_in_range( 635 microscope=microscope, 636 axis=tbt.StageAxis.R, 637 target_position=target_position, 638 stage_tolerance=stage_tolerance, 639 ): 640 # move t-axis to 0 deg (home position) first if needed 641 if not axis_in_range( 642 microscope=microscope, 643 axis=tbt.StageAxis.T, 644 target_position=cs.Constants.home_position, 645 stage_tolerance=stage_tolerance, 646 ): 647 move_axis( 648 microscope=microscope, 649 axis=tbt.StageAxis.T, 650 target_position=cs.Constants.home_position, 651 ) 652 653 move_axis( 654 microscope=microscope, 655 axis=tbt.StageAxis.R, 656 target_position=target_position, 657 ) 658 659 # remaining axes are independent, but sequential 660 remaining_axes = [ 661 tbt.StageAxis.X, 662 tbt.StageAxis.Y, 663 tbt.StageAxis.Z, 664 tbt.StageAxis.T, 665 ] 666 for axis in remaining_axes: 667 if not axis_in_range( 668 microscope=microscope, 669 axis=axis, 670 target_position=target_position, 671 stage_tolerance=stage_tolerance, 672 ): 673 move_axis( 674 microscope=microscope, 675 axis=axis, 676 target_position=target_position, 677 ) 678 679 return True 680 681 682def move_completed( 683 microscope: tbt.Microscope, 684 target_position: tbt.StagePositionUser, 685 stage_tolerance: tbt.StageTolerance, 686) -> bool: 687 """ 688 Check whether the stage is at the target position. 689 690 This function checks if the stage is at the target position within the specified tolerance. 691 692 Parameters 693 ---------- 694 microscope : tbt.Microscope 695 The microscope object for which to check the stage position. 696 target_position : tbt.StagePositionUser 697 The target position to check. 698 stage_tolerance : tbt.StageTolerance 699 The stage tolerance for the position. 700 701 Returns 702 ------- 703 bool 704 True if the stage is at the target position, False otherwise. 705 """ 706 # ensure RAW specimen coordiantes 707 coordinate_system(microscope=microscope, mode=tbt.StageCoordinateSystem.RAW) 708 709 axes = [ 710 tbt.StageAxis.X, 711 tbt.StageAxis.Y, 712 tbt.StageAxis.Z, 713 tbt.StageAxis.R, 714 tbt.StageAxis.T, 715 ] 716 for axis in axes: 717 if not axis_in_range( 718 microscope=microscope, 719 axis=axis, 720 target_position=target_position, 721 stage_tolerance=stage_tolerance, 722 ): 723 # handle -179.9 degrees of rotation being very close equivalently to 180.0 degrees, for example 724 if axis == tbt.StageAxis.R: 725 equivalent_position_pos = tbt.StagePositionUser( 726 x_mm=target_position.x_mm, 727 y_mm=target_position.y_mm, 728 z_mm=target_position.z_mm, 729 r_deg=target_position.r_deg + 360.0, 730 t_deg=target_position.t_deg, 731 ) 732 equivalent_position_neg = tbt.StagePositionUser( 733 x_mm=target_position.x_mm, 734 y_mm=target_position.y_mm, 735 z_mm=target_position.z_mm, 736 r_deg=target_position.r_deg - 360.0, 737 t_deg=target_position.t_deg, 738 ) 739 if axis_in_range( 740 microscope=microscope, 741 axis=axis, 742 target_position=equivalent_position_pos, 743 stage_tolerance=stage_tolerance, 744 ) or axis_in_range( 745 microscope=microscope, 746 axis=axis, 747 target_position=equivalent_position_neg, 748 stage_tolerance=stage_tolerance, 749 ): 750 continue 751 return False 752 return True 753 754 755## main methods 756def home_stage( 757 microscope: tbt.Microscope, 758 stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance, 759) -> bool: 760 """ 761 Move the stage to the home position defined in pytribeam.constants. 762 763 This function moves the stage to the home position defined in pytribeam.constants, which is a special case of the move_to_position function. 764 765 Parameters 766 ---------- 767 microscope : tbt.Microscope 768 The microscope object for which to move the stage. 769 stage_tolerance : tbt.StageTolerance, optional 770 The stage tolerance for the movement (default is cs.Constants.default_stage_tolerance). 771 772 Returns 773 ------- 774 bool 775 True if the stage is moved to the home position successfully. 776 """ 777 target_position = cs.Constants.home_position 778 move_to_position( 779 microscope=microscope, 780 target_position=target_position, 781 stage_tolerance=stage_tolerance, 782 ) 783 return True 784 785 786def move_to_position( 787 microscope: tbt.Microscope, 788 target_position: tbt.StagePositionUser, 789 stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance, 790) -> bool: 791 """ 792 Move the stage to the target position with error checking. 793 794 This function moves the stage to the target position with error checking. 795 796 Parameters 797 ---------- 798 microscope : tbt.Microscope 799 The microscope object for which to move the stage. 800 target_position : tbt.StagePositionUser 801 The target position to move the stage to. 802 stage_tolerance : tbt.StageTolerance, optional 803 The stage tolerance for the movement (default is cs.Constants.default_stage_tolerance). 804 805 Returns 806 ------- 807 bool 808 True if the stage is moved to the target position successfully. 809 810 Raises 811 ValueError 812 If the target position is unsafe. 813 SystemError 814 If the stage move did not execute correctly. 815 """ 816 # check if safe 817 if not safe(microscope=microscope, position=target_position): 818 raise ValueError( 819 f"Destination position of {target_position} is unsafe. Stage limits are:\n\t{factory.stage_limits(microscope=microscope)}. \nExiting now" 820 ) 821 822 # visualize movement on CCD 823 devices.CCD_view(microscope=microscope) 824 825 # move the stage 826 move_stage( 827 microscope=microscope, 828 target_position=target_position, 829 stage_tolerance=stage_tolerance, 830 ) 831 832 # stop visualization on CCD 833 devices.CCD_pause(microscope=microscope) 834 835 # check if completed #TODO clean this with a loop 836 if not move_completed( 837 microscope=microscope, 838 target_position=target_position, 839 stage_tolerance=stage_tolerance, 840 ): 841 # Try again 842 move_stage( 843 microscope=microscope, 844 target_position=target_position, 845 stage_tolerance=stage_tolerance, 846 ) 847 # check if completed 848 if not move_completed( 849 microscope=microscope, 850 target_position=target_position, 851 stage_tolerance=stage_tolerance, 852 ): 853 current_position = factory.active_stage_position_settings( 854 microscope=microscope 855 ) 856 error_message = _bad_axes_message( 857 target_position, current_position, stage_tolerance 858 ) 859 raise SystemError(error_message) 860 861 return True 862 863 864def _bad_axes_message( 865 target_position: tbt.StagePositionUser, 866 current_position: tbt.StagePositionUser, 867 stage_tolerance: tbt.StageTolerance, 868) -> str: 869 """ 870 Generate an error message for axes that are out of tolerance. 871 872 This function generates an error message for axes that are out of tolerance. 873 874 Parameters 875 ---------- 876 target_position : tbt.StagePositionUser 877 The target position of the stage. 878 current_position : tbt.StagePositionUser 879 The current position of the stage. 880 stage_tolerance : tbt.StageTolerance 881 The stage tolerance for the movement. 882 883 Returns 884 ------- 885 str 886 The error message for axes that are out of tolerance. 887 """ 888 error_msg = "Error: Stage move did not execute correctly.\n" 889 x_error_um = np.around( 890 abs(target_position.x_mm - current_position.x_mm) * Conversions.MM_TO_UM, 3 891 ) 892 y_error_um = np.around( 893 abs(target_position.y_mm - current_position.y_mm) * Conversions.MM_TO_UM, 3 894 ) 895 z_error_um = np.around( 896 abs(target_position.z_mm - current_position.z_mm) * Conversions.MM_TO_UM, 3 897 ) 898 899 translation_errors = [x_error_um, y_error_um, z_error_um] 900 translation_axes = ["X", "Y", "Z"] 901 for axis in range(0, len(translation_axes)): 902 if translation_errors[axis] > stage_tolerance.translational_um: 903 error_msg += f"\t {translation_axes[axis]} axis error: {translation_errors[axis]} micron, stage tolerance is {stage_tolerance.translational_um} micron\n" 904 905 t_error_deg = np.around(abs(target_position.t_deg - current_position.t_deg), 3) 906 r_error_deg = np.around(abs(target_position.r_deg - current_position.r_deg), 3) 907 angular_errors = [t_error_deg, r_error_deg] 908 angular_axes = ["T", "R"] 909 for axis in range(0, len(angular_axes)): 910 if angular_errors[axis] > stage_tolerance.translational_um: 911 error_msg += f"\t {angular_axes[axis]} axis error: {angular_errors[axis]} degrees, stage tolerance is {stage_tolerance.angular_deg} degrees\n" 912 913 return error_msg 914 915 916def step_start_position( 917 microscope: tbt.Microscope, 918 slice_number: int, 919 operation: tbt.Step, 920 general_settings: tbt.GeneralSettings, 921) -> bool: 922 """ 923 Move the stage to the starting position for the step. 924 925 This function moves the stage to the starting position for the step based on the slice number, operation, and general settings. 926 927 Parameters 928 ---------- 929 microscope : tbt.Microscope 930 The microscope object for which to move the stage. 931 slice_number : int 932 The slice number for the step. 933 operation : tbt.Step 934 The step object containing the operation settings. 935 general_settings : tbt.GeneralSettings 936 The general settings object. 937 938 Returns 939 ------- 940 bool 941 True if the stage is moved to the starting position successfully. 942 """ 943 position = target_position( 944 operation.stage, 945 slice_number=slice_number, 946 slice_thickness_um=general_settings.slice_thickness_um, 947 ) 948 print("\tMoving to following position:") 949 space = " " 950 print( 951 f"\t\t{'X [mm]' + space:<10}{'Y [mm]' + space:<10}{'Z [mm]' + space:<10}{'R [deg]' + space:<10}{'T [deg]' + space:<10}" 952 ) 953 print( 954 f"\t\t{round(position.x_mm,4):<10}{round(position.y_mm,4):<10}{round(position.z_mm,4):<10}{round(position.r_deg,3):<10}{round(position.t_deg,3):<10}" 955 ) 956 move_to_position( 957 microscope=microscope, 958 target_position=position, 959 stage_tolerance=general_settings.stage_tolerance, 960 )
88def coordinate_system( 89 microscope: tbt.Microscope, 90 mode: tbt.StageCoordinateSystem = tbt.StageCoordinateSystem.RAW, 91) -> bool: 92 """ 93 Set the stage coordinate system mode. 94 95 This function sets the stage coordinate system mode. The default mode is "RAW", which is recommended. 96 97 Parameters 98 ---------- 99 microscope : tbt.Microscope 100 The microscope object for which to set the stage coordinate system mode. 101 mode : tbt.StageCoordinateSystem, optional 102 The stage coordinate system mode to set (default is tbt.StageCoordinateSystem.RAW). 103 104 Returns 105 ------- 106 bool 107 True if the coordinate system mode is set successfully. 108 """ 109 if mode != tbt.StageCoordinateSystem.RAW: 110 warnings.warn( 111 f"""Warning. {mode.value} coordinate system requested 112 instead of "RAW" stage coordinate system. This can lead 113 to inaccurate stage movements and increases collision risk.""" 114 ) 115 # TODO ask for yes no continue 116 microscope.specimen.stage.set_default_coordinate_system(mode.value) 117 return True
Set the stage coordinate system mode.
This function sets the stage coordinate system mode. The default mode is "RAW", which is recommended.
Parameters
microscope : tbt.Microscope The microscope object for which to set the stage coordinate system mode. mode : tbt.StageCoordinateSystem, optional The stage coordinate system mode to set (default is tbt.StageCoordinateSystem.RAW).
Returns
bool True if the coordinate system mode is set successfully.
120def stop(microscope: tbt.Microscope) -> None: 121 """ 122 Immediately stop all stage movement and exit. 123 124 This function stops all stage movement and disconnects the microscope. 125 126 Parameters 127 ---------- 128 microscope : tbt.Microscope 129 The microscope object for which to stop stage movement. 130 131 Raises 132 ------ 133 SystemError 134 If the stage movement is halted. 135 """ 136 microscope.specimen.stage.stop() 137 microscope.disconnect() 138 139 raise SystemError("Microscope stage movement was halted.") 140 # sys.exit("Microscope stage movement was halted.")
Immediately stop all stage movement and exit.
This function stops all stage movement and disconnects the microscope.
Parameters
microscope : tbt.Microscope The microscope object for which to stop stage movement.
Raises
SystemError If the stage movement is halted.
143def encoder_to_user_position(pos: tbt.StagePositionEncoder) -> tbt.StagePositionUser: 144 """ 145 Convert from encoder position (m/radian units) to user position (mm/deg units). 146 147 This function converts a stage position from encoder units to user units. 148 149 Parameters 150 ---------- 151 pos : tbt.StagePositionEncoder 152 The stage position in encoder units. 153 154 Returns 155 ------- 156 tbt.StagePositionUser 157 The stage position in user units. 158 159 Raises 160 ------ 161 TypeError 162 If the provided position is not of type tbt.StagePositionEncoder. 163 """ 164 if not isinstance(pos, tbt.StagePositionEncoder): 165 raise TypeError( 166 f"provided position is not of type '<class pytribeam.types.StagePositionEncoder>', but instead of type '{type(pos)}'. Did you mean to use the function 'user_to_encoder_position'?" 167 ) 168 user_pos = tbt.StagePositionUser( 169 x_mm=round(pos.x * Conversions.M_TO_MM, 6), 170 y_mm=round(pos.y * Conversions.M_TO_MM, 6), 171 z_mm=round(pos.z * Conversions.M_TO_MM, 6), 172 r_deg=round(pos.r * Conversions.RAD_TO_DEG, 6), 173 t_deg=round(pos.t * Conversions.RAD_TO_DEG, 6), 174 coordinate_system=tbt.StageCoordinateSystem(pos.coordinate_system), 175 ) 176 return user_pos
Convert from encoder position (m/radian units) to user position (mm/deg units).
This function converts a stage position from encoder units to user units.
Parameters
pos : tbt.StagePositionEncoder The stage position in encoder units.
Returns
tbt.StagePositionUser The stage position in user units.
Raises
TypeError If the provided position is not of type tbt.StagePositionEncoder.
179def user_to_encoder_position(pos: tbt.StagePositionUser) -> tbt.StagePositionEncoder: 180 """ 181 Convert from user position (mm/deg units) to encoder position (m/radian units). 182 183 This function converts a stage position from user units to encoder units. 184 185 Parameters 186 ---------- 187 pos : tbt.StagePositionUser 188 The stage position in user units. 189 190 Returns 191 ------- 192 tbt.StagePositionEncoder 193 The stage position in encoder units. 194 195 Raises 196 ------ 197 TypeError 198 If the provided position is not of type tbt.StagePositionUser. 199 """ 200 201 if not isinstance(pos, tbt.StagePositionUser): 202 raise TypeError( 203 f"Provided position is not of type '<class pytribeam.types.StagePositionUser>', but instead of type '{type(pos)}'. Did you mean to use the function 'encoder_to_user_position'?" 204 ) 205 encoder_pos = tbt.StagePositionEncoder( 206 x=pos.x_mm * Conversions.MM_TO_M, 207 y=pos.y_mm * Conversions.MM_TO_M, 208 z=pos.z_mm * Conversions.MM_TO_M, 209 r=pos.r_deg * Conversions.DEG_TO_RAD, 210 t=pos.t_deg * Conversions.DEG_TO_RAD, 211 coordinate_system=pos.coordinate_system.value, 212 ) 213 return encoder_pos
Convert from user position (mm/deg units) to encoder position (m/radian units).
This function converts a stage position from user units to encoder units.
Parameters
pos : tbt.StagePositionUser The stage position in user units.
Returns
tbt.StagePositionEncoder The stage position in encoder units.
Raises
TypeError If the provided position is not of type tbt.StagePositionUser.
217def rotation_side_adjustment( 218 rotation_side: tbt.RotationSide, 219 initial_position_m: float, 220 delta_pos_m: float, 221) -> float: 222 """ 223 Adjust the translation stage destination based on the rotation side. 224 225 This function adjusts the translation stage destination based on the specified rotation side. 226 227 Parameters 228 ---------- 229 rotation_side : tbt.RotationSide 230 The rotation side to consider for the adjustment. 231 initial_position_m : float 232 The initial position in meters. 233 delta_pos_m : float 234 The change in position in meters. 235 236 Returns 237 ------- 238 float 239 The adjusted target position in meters. 240 241 Raises 242 ------ 243 NotImplementedError 244 If the rotation side is unsupported. 245 """ 246 if rotation_side == tbt.RotationSide.FSL_MILL: 247 target_m = ( 248 initial_position_m - delta_pos_m 249 ) # absolute negative direction (towards laser) 250 elif rotation_side == tbt.RotationSide.FIB_MILL: 251 target_m = ( 252 initial_position_m + delta_pos_m 253 ) # absolute positive direction (towards FIB) 254 elif rotation_side == tbt.RotationSide.EBEAM_NORMAL: 255 target_m = initial_position_m # no adjustment needed 256 else: 257 raise NotImplementedError( 258 f"Unsupported RotationSide enumeration of '{rotation_side}'" 259 ) 260 return target_m
Adjust the translation stage destination based on the rotation side.
This function adjusts the translation stage destination based on the specified rotation side.
Parameters
rotation_side : tbt.RotationSide The rotation side to consider for the adjustment. initial_position_m : float The initial position in meters. delta_pos_m : float The change in position in meters.
Returns
float The adjusted target position in meters.
Raises
NotImplementedError If the rotation side is unsupported.
263def target_position( 264 stage: tbt.StageSettings, 265 slice_number: int, 266 slice_thickness_um: float, 267) -> tbt.StagePositionUser: 268 """ 269 Calculate the target position for the stage movement. 270 271 This function calculates the target position for the stage movement based on the sectioning axis, slice number, and slice thickness. 272 273 For Z-axis sectioning: 274 - Z will always incrememnt toward pole piece (positive direction) 275 - Y will increment with slice number if a non-zero pre-tilt is used 276 - Y increment direction depends on rotation of the stage relative to machining operations 277 278 For X-axis sectioning (not yet implemented, need to determine rotation_side_adjustment) 279 For Y-axis sectioning (not yet implemented, need to determine rotation_side_adjustment) 280 281 Parameters 282 ---------- 283 stage : tbt.StageSettings 284 The stage settings for the experiment. 285 slice_number : int 286 The slice number for the experiment. 287 slice_thickness_um : float 288 The slice thickness in micrometers. 289 290 Returns 291 ------- 292 tbt.StagePositionUser 293 The target position for the stage movement. 294 295 Raises 296 ------ 297 NotImplementedError 298 If the sectioning axis is unsupported. 299 """ 300 301 initial_pos_user = stage.initial_position 302 initial_pos_encoder = user_to_encoder_position(initial_pos_user) 303 slice_thickness_m = slice_thickness_um * Conversions.UM_TO_M 304 pre_tilt_rad = stage.pretilt_angle_deg * Conversions.DEG_TO_RAD 305 sectioning_axis = stage.sectioning_axis 306 rotation_side = stage.rotation_side 307 increment_factor_m = slice_thickness_m * (slice_number - 1) # slices are 1-indexed 308 309 # only modify needed axes for each case, so initialize with original position 310 target_x_m = initial_pos_encoder.x 311 target_y_m = initial_pos_encoder.y 312 target_z_m = initial_pos_encoder.z 313 target_r_rad = initial_pos_encoder.r 314 target_t_rad = initial_pos_encoder.t 315 316 if sectioning_axis == tbt.SectioningAxis.Z: 317 delta_z_m = math.cos(pre_tilt_rad) * increment_factor_m 318 # Z always increments in positive direction 319 target_z_m = initial_pos_encoder.z + delta_z_m 320 321 # Y axis depends on rotation_side 322 delta_y_m = math.sin(pre_tilt_rad) * increment_factor_m 323 324 if rotation_side == tbt.RotationSide.FSL_MILL: 325 target_y_m = ( 326 initial_pos_encoder.y - delta_y_m 327 ) # absolute negative direction (towards laser) 328 elif rotation_side == tbt.RotationSide.FIB_MILL: 329 target_y_m = ( 330 initial_pos_encoder.y + delta_y_m 331 ) # absolute positive direction (towards FIB) 332 elif rotation_side == tbt.RotationSide.EBEAM_NORMAL: 333 target_y_m = initial_pos_encoder.y # no adjustment needed 334 else: 335 raise NotImplementedError( 336 f"Unsupported RotationSide enumeration of '{rotation_side}'" 337 ) 338 339 # TODO 340 elif sectioning_axis == tbt.SectioningAxis.X_POS: 341 raise NotImplementedError("Currently only Z-axis sectioning is supported.") 342 delta_x_m = increment_factor_m 343 pass 344 elif sectioning_axis == tbt.SectioningAxis.X_NEG: 345 raise NotImplementedError("Currently only Z-axis sectioning is supported.") 346 delta_x_m = increment_factor_m 347 pass 348 elif sectioning_axis == tbt.SectioningAxis.Y_POS: 349 raise NotImplementedError("Currently only Z-axis sectioning is supported.") 350 delta_x_m = increment_factor_m 351 pass 352 elif sectioning_axis == tbt.SectioningAxis.Y_NEG: 353 raise NotImplementedError("Currently only Z-axis sectioning is supported.") 354 delta_x_m = increment_factor_m 355 pass 356 357 target_pos_encoder = tbt.StagePositionEncoder( 358 x=target_x_m, 359 y=target_y_m, 360 z=target_z_m, 361 r=target_r_rad, 362 t=target_t_rad, 363 coordinate_system=tbt.StageCoordinateSystem.RAW.value, 364 ) 365 target_pos_user = encoder_to_user_position(target_pos_encoder) 366 return target_pos_user
Calculate the target position for the stage movement.
This function calculates the target position for the stage movement based on the sectioning axis, slice number, and slice thickness.
For Z-axis sectioning: - Z will always incrememnt toward pole piece (positive direction) - Y will increment with slice number if a non-zero pre-tilt is used - Y increment direction depends on rotation of the stage relative to machining operations
For X-axis sectioning (not yet implemented, need to determine rotation_side_adjustment) For Y-axis sectioning (not yet implemented, need to determine rotation_side_adjustment)
Parameters
stage : tbt.StageSettings The stage settings for the experiment. slice_number : int The slice number for the experiment. slice_thickness_um : float The slice thickness in micrometers.
Returns
tbt.StagePositionUser The target position for the stage movement.
Raises
NotImplementedError If the sectioning axis is unsupported.
369def safe( 370 microscope: tbt.Microscope, 371 position: tbt.StagePositionUser, 372) -> bool: 373 """ 374 Check if the target position is within the stage limits. 375 376 This function checks if the target position is within the stage limits. 377 378 Parameters 379 ---------- 380 microscope : tbt.Microscope 381 The microscope object for which to check the stage limits. 382 position : tbt.StagePositionUser 383 The target position to check. 384 385 Returns 386 ------- 387 bool 388 True if the target position is within the stage limits, False otherwise. 389 """ 390 391 # returns in user units (mm, deg) 392 stage_limits = factory.stage_limits(microscope=microscope) 393 394 if not ut.in_interval( 395 position.x_mm, stage_limits.x_mm, type=tbt.IntervalType.CLOSED 396 ): 397 return False 398 if not ut.in_interval( 399 position.y_mm, stage_limits.y_mm, type=tbt.IntervalType.CLOSED 400 ): 401 return False 402 if not ut.in_interval( 403 position.z_mm, stage_limits.z_mm, type=tbt.IntervalType.CLOSED 404 ): 405 return False 406 if not ut.in_interval( 407 position.t_deg, stage_limits.t_deg, type=tbt.IntervalType.CLOSED 408 ): 409 return False 410 if not ut.in_interval( 411 position.r_deg, stage_limits.r_deg, type=tbt.IntervalType.CLOSED 412 ): 413 return False 414 415 return True
Check if the target position is within the stage limits.
This function checks if the target position is within the stage limits.
Parameters
microscope : tbt.Microscope The microscope object for which to check the stage limits. position : tbt.StagePositionUser The target position to check.
Returns
bool True if the target position is within the stage limits, False otherwise.
418def axis_translational_in_range( 419 current_pos_mm: float, 420 target_pos_mm: float, 421 stage_tolerance_um: float, 422) -> bool: 423 """ 424 Determine whether the translation axis needs to be moved. 425 426 This function checks if the translation axis is within the specified tolerance. 427 428 Parameters 429 ---------- 430 current_pos_mm : float 431 The current position of the translation axis in millimeters. 432 target_pos_mm : float 433 The target position of the translation axis in millimeters. 434 stage_tolerance_um : float 435 The tolerance for the translation axis in micrometers. 436 437 Returns 438 ------- 439 bool 440 True if the translation axis is within the specified tolerance, False otherwise. 441 """ 442 return ut.in_interval( 443 current_pos_mm, 444 limit=tbt.Limit( 445 min=target_pos_mm - (stage_tolerance_um * Conversions.UM_TO_MM), 446 max=target_pos_mm + (stage_tolerance_um * Conversions.UM_TO_MM), 447 ), 448 type=tbt.IntervalType.CLOSED, 449 )
Determine whether the translation axis needs to be moved.
This function checks if the translation axis is within the specified tolerance.
Parameters
current_pos_mm : float The current position of the translation axis in millimeters. target_pos_mm : float The target position of the translation axis in millimeters. stage_tolerance_um : float The tolerance for the translation axis in micrometers.
Returns
bool True if the translation axis is within the specified tolerance, False otherwise.
452def axis_angular_in_range( 453 current_pos_deg: float, 454 target_pos_deg: float, 455 stage_tolerance_deg: float, 456) -> bool: 457 """ 458 Determine whether the angular axis needs to be moved. 459 460 This function checks if the angular axis is within the specified tolerance. 461 462 Parameters 463 ---------- 464 current_pos_deg : float 465 The current position of the angular axis in degrees. 466 target_pos_deg : float 467 The target position of the angular axis in degrees. 468 stage_tolerance_deg : float 469 The tolerance for the angular axis in degrees. 470 471 Returns 472 ------- 473 bool 474 True if the angular axis is within the specified tolerance, False otherwise. 475 """ 476 return ut.in_interval( 477 current_pos_deg, 478 limit=tbt.Limit( 479 min=target_pos_deg - (stage_tolerance_deg * Conversions.DEG_TO_RAD), 480 max=target_pos_deg + (stage_tolerance_deg * Conversions.DEG_TO_RAD), 481 ), 482 type=tbt.IntervalType.CLOSED, 483 )
Determine whether the angular axis needs to be moved.
This function checks if the angular axis is within the specified tolerance.
Parameters
current_pos_deg : float The current position of the angular axis in degrees. target_pos_deg : float The target position of the angular axis in degrees. stage_tolerance_deg : float The tolerance for the angular axis in degrees.
Returns
bool True if the angular axis is within the specified tolerance, False otherwise.
486def axis_in_range( 487 microscope: tbt.Microscope, 488 axis: tbt.StageAxis, 489 target_position: tbt.StagePositionUser, 490 stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance, 491) -> bool: 492 """ 493 Check whether the position of the specified axis is within the stage tolerance. 494 495 This function checks if the position of the specified axis is within the stage tolerance. 496 497 Parameters 498 ---------- 499 microscope : tbt.Microscope 500 The microscope object for which to check the axis position. 501 axis : tbt.StageAxis 502 The axis to check. 503 target_position : tbt.StagePositionUser 504 The target position to check. 505 stage_tolerance : tbt.StageTolerance, optional 506 The stage tolerance for the axis (default is cs.Constants.default_stage_tolerance). 507 508 Returns 509 ------- 510 bool 511 True if the axis position is within the stage tolerance, False otherwise. 512 """ 513 current_position = factory.active_stage_position_settings( 514 microscope=microscope 515 ) # user units [mm_deg] 516 517 # TODO convert to match statements at python >=3.10 518 match_db = { 519 tbt.StageAxis.X: { 520 "current_position": current_position.x_mm, 521 "target_position": target_position.x_mm, 522 "tolerance": stage_tolerance.translational_um * Conversions.UM_TO_MM, 523 }, 524 tbt.StageAxis.Y: { 525 "current_position": current_position.y_mm, 526 "target_position": target_position.y_mm, 527 "tolerance": stage_tolerance.translational_um * Conversions.UM_TO_MM, 528 }, 529 tbt.StageAxis.Z: { 530 "current_position": current_position.z_mm, 531 "target_position": target_position.z_mm, 532 "tolerance": stage_tolerance.translational_um * Conversions.UM_TO_MM, 533 }, 534 tbt.StageAxis.R: { 535 "current_position": current_position.r_deg, 536 "target_position": target_position.r_deg, 537 "tolerance": stage_tolerance.angular_deg, 538 }, 539 tbt.StageAxis.T: { 540 "current_position": current_position.t_deg, 541 "target_position": target_position.t_deg, 542 "tolerance": stage_tolerance.angular_deg, 543 }, 544 } 545 546 return ut.in_interval( 547 val=match_db[axis]["current_position"], 548 limit=tbt.Limit( 549 min=match_db[axis]["target_position"] - match_db[axis]["tolerance"], 550 max=match_db[axis]["target_position"] + match_db[axis]["tolerance"], 551 ), 552 type=tbt.IntervalType.CLOSED, 553 )
Check whether the position of the specified axis is within the stage tolerance.
This function checks if the position of the specified axis is within the stage tolerance.
Parameters
microscope : tbt.Microscope The microscope object for which to check the axis position. axis : tbt.StageAxis The axis to check. target_position : tbt.StagePositionUser The target position to check. stage_tolerance : tbt.StageTolerance, optional The stage tolerance for the axis (default is cs.Constants.default_stage_tolerance).
Returns
bool True if the axis position is within the stage tolerance, False otherwise.
556def move_axis( 557 microscope: tbt.Microscope, 558 axis: tbt.StageAxis, 559 target_position: tbt.StagePositionUser, 560 num_attempts: int = cs.Constants.stage_move_attempts, 561 stage_delay_s: float = cs.Constants.stage_move_delay_s, 562) -> bool: 563 """ 564 Move the specified stage axis to the requested user target position. 565 566 This function moves the specified stage axis to the requested user target position. 567 568 Parameters 569 ---------- 570 microscope : tbt.Microscope 571 The microscope object for which to move the stage axis. 572 axis : tbt.StageAxis 573 The stage axis to move. 574 target_position : tbt.StagePositionUser 575 The target position to move the axis to. 576 num_attempts : int, optional 577 The number of attempts to move the axis (default is cs.Constants.stage_move_attempts). 578 stage_delay_s : float, optional 579 The delay in seconds between attempts (default is cs.Constants.stage_move_delay_s). 580 581 Returns 582 ------- 583 bool 584 True if the axis is moved to the target position successfully. 585 """ 586 encoder_position = user_to_encoder_position(target_position) 587 # TODO convert to match statements at python >=3.10 588 match_db = { 589 tbt.StageAxis.X: tbt.StagePositionEncoder(x=encoder_position.x), 590 tbt.StageAxis.Y: tbt.StagePositionEncoder(y=encoder_position.y), 591 tbt.StageAxis.Z: tbt.StagePositionEncoder(z=encoder_position.z), 592 tbt.StageAxis.R: tbt.StagePositionEncoder(r=encoder_position.r), 593 tbt.StageAxis.T: tbt.StagePositionEncoder(t=encoder_position.t), 594 } 595 for _ in range(num_attempts): 596 microscope.specimen.stage.absolute_move(match_db[axis]) 597 time.sleep(stage_delay_s) 598 return True
Move the specified stage axis to the requested user target position.
This function moves the specified stage axis to the requested user target position.
Parameters
microscope : tbt.Microscope The microscope object for which to move the stage axis. axis : tbt.StageAxis The stage axis to move. target_position : tbt.StagePositionUser The target position to move the axis to. num_attempts : int, optional The number of attempts to move the axis (default is cs.Constants.stage_move_attempts). stage_delay_s : float, optional The delay in seconds between attempts (default is cs.Constants.stage_move_delay_s).
Returns
bool True if the axis is moved to the target position successfully.
601def move_stage( 602 microscope: tbt.Microscope, 603 target_position: tbt.StagePositionUser, 604 stage_tolerance: tbt.StageTolerance, 605) -> bool: 606 """ 607 Move the stage axes if they are outside of tolerance. 608 609 This function moves the stage axes to the target position if they are outside of the specified tolerance. The stage axes are moved one at a time in the following sequence: 610 - R-axis: if needed, tilt will be adjusted to 0 degrees first for safety 611 - X-axis 612 - Y-axis 613 - Z-axis 614 - T-axis 615 616 Parameters 617 ---------- 618 microscope : tbt.Microscope 619 The microscope object for which to move the stage. 620 target_position : tbt.StagePositionUser 621 The target position to move the stage to. 622 stage_tolerance : tbt.StageTolerance 623 The stage tolerance for the movement. 624 625 Returns 626 ------- 627 bool 628 True if the stage is moved to the target position successfully. 629 """ 630 631 # ensure RAW specimen coordiantes 632 coordinate_system(microscope=microscope, mode=tbt.StageCoordinateSystem.RAW) 633 634 # r-axis first for safety 635 if not axis_in_range( 636 microscope=microscope, 637 axis=tbt.StageAxis.R, 638 target_position=target_position, 639 stage_tolerance=stage_tolerance, 640 ): 641 # move t-axis to 0 deg (home position) first if needed 642 if not axis_in_range( 643 microscope=microscope, 644 axis=tbt.StageAxis.T, 645 target_position=cs.Constants.home_position, 646 stage_tolerance=stage_tolerance, 647 ): 648 move_axis( 649 microscope=microscope, 650 axis=tbt.StageAxis.T, 651 target_position=cs.Constants.home_position, 652 ) 653 654 move_axis( 655 microscope=microscope, 656 axis=tbt.StageAxis.R, 657 target_position=target_position, 658 ) 659 660 # remaining axes are independent, but sequential 661 remaining_axes = [ 662 tbt.StageAxis.X, 663 tbt.StageAxis.Y, 664 tbt.StageAxis.Z, 665 tbt.StageAxis.T, 666 ] 667 for axis in remaining_axes: 668 if not axis_in_range( 669 microscope=microscope, 670 axis=axis, 671 target_position=target_position, 672 stage_tolerance=stage_tolerance, 673 ): 674 move_axis( 675 microscope=microscope, 676 axis=axis, 677 target_position=target_position, 678 ) 679 680 return True
Move the stage axes if they are outside of tolerance.
This function moves the stage axes to the target position if they are outside of the specified tolerance. The stage axes are moved one at a time in the following sequence:
- R-axis: if needed, tilt will be adjusted to 0 degrees first for safety
- X-axis
- Y-axis
- Z-axis
- T-axis
Parameters
microscope : tbt.Microscope The microscope object for which to move the stage. target_position : tbt.StagePositionUser The target position to move the stage to. stage_tolerance : tbt.StageTolerance The stage tolerance for the movement.
Returns
bool True if the stage is moved to the target position successfully.
683def move_completed( 684 microscope: tbt.Microscope, 685 target_position: tbt.StagePositionUser, 686 stage_tolerance: tbt.StageTolerance, 687) -> bool: 688 """ 689 Check whether the stage is at the target position. 690 691 This function checks if the stage is at the target position within the specified tolerance. 692 693 Parameters 694 ---------- 695 microscope : tbt.Microscope 696 The microscope object for which to check the stage position. 697 target_position : tbt.StagePositionUser 698 The target position to check. 699 stage_tolerance : tbt.StageTolerance 700 The stage tolerance for the position. 701 702 Returns 703 ------- 704 bool 705 True if the stage is at the target position, False otherwise. 706 """ 707 # ensure RAW specimen coordiantes 708 coordinate_system(microscope=microscope, mode=tbt.StageCoordinateSystem.RAW) 709 710 axes = [ 711 tbt.StageAxis.X, 712 tbt.StageAxis.Y, 713 tbt.StageAxis.Z, 714 tbt.StageAxis.R, 715 tbt.StageAxis.T, 716 ] 717 for axis in axes: 718 if not axis_in_range( 719 microscope=microscope, 720 axis=axis, 721 target_position=target_position, 722 stage_tolerance=stage_tolerance, 723 ): 724 # handle -179.9 degrees of rotation being very close equivalently to 180.0 degrees, for example 725 if axis == tbt.StageAxis.R: 726 equivalent_position_pos = tbt.StagePositionUser( 727 x_mm=target_position.x_mm, 728 y_mm=target_position.y_mm, 729 z_mm=target_position.z_mm, 730 r_deg=target_position.r_deg + 360.0, 731 t_deg=target_position.t_deg, 732 ) 733 equivalent_position_neg = tbt.StagePositionUser( 734 x_mm=target_position.x_mm, 735 y_mm=target_position.y_mm, 736 z_mm=target_position.z_mm, 737 r_deg=target_position.r_deg - 360.0, 738 t_deg=target_position.t_deg, 739 ) 740 if axis_in_range( 741 microscope=microscope, 742 axis=axis, 743 target_position=equivalent_position_pos, 744 stage_tolerance=stage_tolerance, 745 ) or axis_in_range( 746 microscope=microscope, 747 axis=axis, 748 target_position=equivalent_position_neg, 749 stage_tolerance=stage_tolerance, 750 ): 751 continue 752 return False 753 return True
Check whether the stage is at the target position.
This function checks if the stage is at the target position within the specified tolerance.
Parameters
microscope : tbt.Microscope The microscope object for which to check the stage position. target_position : tbt.StagePositionUser The target position to check. stage_tolerance : tbt.StageTolerance The stage tolerance for the position.
Returns
bool True if the stage is at the target position, False otherwise.
757def home_stage( 758 microscope: tbt.Microscope, 759 stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance, 760) -> bool: 761 """ 762 Move the stage to the home position defined in pytribeam.constants. 763 764 This function moves the stage to the home position defined in pytribeam.constants, which is a special case of the move_to_position function. 765 766 Parameters 767 ---------- 768 microscope : tbt.Microscope 769 The microscope object for which to move the stage. 770 stage_tolerance : tbt.StageTolerance, optional 771 The stage tolerance for the movement (default is cs.Constants.default_stage_tolerance). 772 773 Returns 774 ------- 775 bool 776 True if the stage is moved to the home position successfully. 777 """ 778 target_position = cs.Constants.home_position 779 move_to_position( 780 microscope=microscope, 781 target_position=target_position, 782 stage_tolerance=stage_tolerance, 783 ) 784 return True
Move the stage to the home position defined in pytribeam.constants.
This function moves the stage to the home position defined in pytribeam.constants, which is a special case of the move_to_position function.
Parameters
microscope : tbt.Microscope The microscope object for which to move the stage. stage_tolerance : tbt.StageTolerance, optional The stage tolerance for the movement (default is cs.Constants.default_stage_tolerance).
Returns
bool True if the stage is moved to the home position successfully.
787def move_to_position( 788 microscope: tbt.Microscope, 789 target_position: tbt.StagePositionUser, 790 stage_tolerance: tbt.StageTolerance = cs.Constants.default_stage_tolerance, 791) -> bool: 792 """ 793 Move the stage to the target position with error checking. 794 795 This function moves the stage to the target position with error checking. 796 797 Parameters 798 ---------- 799 microscope : tbt.Microscope 800 The microscope object for which to move the stage. 801 target_position : tbt.StagePositionUser 802 The target position to move the stage to. 803 stage_tolerance : tbt.StageTolerance, optional 804 The stage tolerance for the movement (default is cs.Constants.default_stage_tolerance). 805 806 Returns 807 ------- 808 bool 809 True if the stage is moved to the target position successfully. 810 811 Raises 812 ValueError 813 If the target position is unsafe. 814 SystemError 815 If the stage move did not execute correctly. 816 """ 817 # check if safe 818 if not safe(microscope=microscope, position=target_position): 819 raise ValueError( 820 f"Destination position of {target_position} is unsafe. Stage limits are:\n\t{factory.stage_limits(microscope=microscope)}. \nExiting now" 821 ) 822 823 # visualize movement on CCD 824 devices.CCD_view(microscope=microscope) 825 826 # move the stage 827 move_stage( 828 microscope=microscope, 829 target_position=target_position, 830 stage_tolerance=stage_tolerance, 831 ) 832 833 # stop visualization on CCD 834 devices.CCD_pause(microscope=microscope) 835 836 # check if completed #TODO clean this with a loop 837 if not move_completed( 838 microscope=microscope, 839 target_position=target_position, 840 stage_tolerance=stage_tolerance, 841 ): 842 # Try again 843 move_stage( 844 microscope=microscope, 845 target_position=target_position, 846 stage_tolerance=stage_tolerance, 847 ) 848 # check if completed 849 if not move_completed( 850 microscope=microscope, 851 target_position=target_position, 852 stage_tolerance=stage_tolerance, 853 ): 854 current_position = factory.active_stage_position_settings( 855 microscope=microscope 856 ) 857 error_message = _bad_axes_message( 858 target_position, current_position, stage_tolerance 859 ) 860 raise SystemError(error_message) 861 862 return True
Move the stage to the target position with error checking.
This function moves the stage to the target position with error checking.
Parameters
microscope : tbt.Microscope The microscope object for which to move the stage. target_position : tbt.StagePositionUser The target position to move the stage to. stage_tolerance : tbt.StageTolerance, optional The stage tolerance for the movement (default is cs.Constants.default_stage_tolerance).
Returns
bool True if the stage is moved to the target position successfully.
Raises ValueError If the target position is unsafe. SystemError If the stage move did not execute correctly.
917def step_start_position( 918 microscope: tbt.Microscope, 919 slice_number: int, 920 operation: tbt.Step, 921 general_settings: tbt.GeneralSettings, 922) -> bool: 923 """ 924 Move the stage to the starting position for the step. 925 926 This function moves the stage to the starting position for the step based on the slice number, operation, and general settings. 927 928 Parameters 929 ---------- 930 microscope : tbt.Microscope 931 The microscope object for which to move the stage. 932 slice_number : int 933 The slice number for the step. 934 operation : tbt.Step 935 The step object containing the operation settings. 936 general_settings : tbt.GeneralSettings 937 The general settings object. 938 939 Returns 940 ------- 941 bool 942 True if the stage is moved to the starting position successfully. 943 """ 944 position = target_position( 945 operation.stage, 946 slice_number=slice_number, 947 slice_thickness_um=general_settings.slice_thickness_um, 948 ) 949 print("\tMoving to following position:") 950 space = " " 951 print( 952 f"\t\t{'X [mm]' + space:<10}{'Y [mm]' + space:<10}{'Z [mm]' + space:<10}{'R [deg]' + space:<10}{'T [deg]' + space:<10}" 953 ) 954 print( 955 f"\t\t{round(position.x_mm,4):<10}{round(position.y_mm,4):<10}{round(position.z_mm,4):<10}{round(position.r_deg,3):<10}{round(position.t_deg,3):<10}" 956 ) 957 move_to_position( 958 microscope=microscope, 959 target_position=position, 960 stage_tolerance=general_settings.stage_tolerance, 961 )
Move the stage to the starting position for the step.
This function moves the stage to the starting position for the step based on the slice number, operation, and general settings.
Parameters
microscope : tbt.Microscope The microscope object for which to move the stage. slice_number : int The slice number for the step. operation : tbt.Step The step object containing the operation settings. general_settings : tbt.GeneralSettings The general settings object.
Returns
bool True if the stage is moved to the starting position successfully.