Coverage for src/pytribeam/laser.py: 0%
246 statements
« prev ^ index » next coverage.py v7.6.1, created at 2026-06-16 18:30 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2026-06-16 18:30 +0000
1#!/usr/bin/python3
2"""
3Laser Module
4============
6This module contains functions for managing and controlling the laser in the microscope, including setting laser parameters, checking laser connections, and performing laser operations.
8Functions
9---------
10laser_state_to_db(state: tbt.LaserState) -> dict
11 Convert a laser state object into a flattened dictionary.
13laser_connected() -> bool
14 Check if the laser is connected.
16_device_connections() -> tbt.DeviceStatus
17 Check the connection status of the laser and associated external devices.
19pattern_mode(mode: tbt.LaserPatternMode) -> bool
20 Set the laser pattern mode.
22pulse_energy_uj(energy_uj: float, energy_tol_uj: float = Constants.laser_energy_tol_uj, delay_s: float = 3.0) -> bool
23 Set the pulse energy on the laser.
25pulse_divider(divider: int, delay_s: float = Constants.laser_delay_s) -> bool
26 Set the pulse divider on the laser.
28set_wavelength(wavelength: tbt.LaserWavelength, frequency_khz: float = 60, timeout_s: int = 20, num_attempts: int = 2, delay_s: int = 5) -> bool
29 Set the wavelength and frequency of the laser.
31read_power(delay_s: float = Constants.laser_delay_s) -> float
32 Measure the laser power in watts.
34insert_shutter(microscope: tbt.Microscope) -> bool
35 Insert the laser shutter.
37retract_shutter(microscope: tbt.Microscope) -> bool
38 Retract the laser shutter.
40pulse_polarization(polarization: tbt.LaserPolarization, wavelength: tbt.LaserWavelength) -> bool
41 Configure the polarization of the laser light.
43pulse_settings(pulse: tbt.LaserPulse) -> bool
44 Apply the pulse settings to the laser.
46retract_laser_objective() -> bool
47 Retract the laser objective to a safe position.
49objective_position(position_mm: float, tolerance_mm=Constants.laser_objective_tolerance_mm) -> bool
50 Move the laser objective to the requested position.
52beam_shift(shift_um: tbt.Point, shift_tolerance_um: float = Constants.laser_beam_shift_tolerance_um) -> bool
53 Adjust the laser beam shift to the specified values.
55create_pattern(pattern: tbt.LaserPattern) -> bool
56 Create a laser pattern and check that it is set correctly.
58apply_laser_settings(image_beam: tbt.Beam, settings: tbt.LaserSettings) -> bool
59 Apply the laser settings to the current patterning.
61execute_patterning() -> bool
62 Execute the laser patterning.
64mill_region(settings: tbt.LaserSettings) -> bool
65 Perform laser milling on a specified region.
67laser_operation(step: tbt.Step, general_settings: tbt.GeneralSettings, slice_number: int) -> bool
68 Perform a laser operation based on the specified step and settings.
70map_ebsd() -> bool
71 Start an EBSD map and ensure it takes the minimum expected time.
73map_eds() -> bool
74 Start an EDS map and ensure it takes the minimum expected time.
75"""
77# Default python modules
78import time
79import contextlib, io
80import math
82try:
83 import Laser.PythonControl as tfs_laser
85 print("Laser PythonControl API imported.")
86except:
87 print("WARNING: Laser API not imported!")
88 print("\tLaser control, as well as EBSD and EDS control are unavailable.")
90# 3rd party .whl modules
92# Local scripts
93from pytribeam.constants import Constants
94import pytribeam.factory as factory
95import pytribeam.types as tbt
96import pytribeam.utilities as ut
97import pytribeam.insertable_devices as devices
98import pytribeam.image as img
99import pytribeam.log as log
102def laser_state_to_db(state: tbt.LaserState) -> dict:
103 """
104 This function converts a `LaserState` object into a flattened dictionary representation.
106 Parameters
107 ----------
108 state : tbt.LaserState
109 The laser state object to convert.
111 Returns
112 -------
113 dict
114 A flattened dictionary representation of the laser state.
115 """
116 db = {}
118 db["wavelength_nm"] = state.wavelength_nm
119 db["frequency_khz"] = state.frequency_khz
120 db["pulse_divider"] = state.pulse_divider
121 db["pulse_energy_uj"] = state.pulse_energy_uj
122 db["objective_position_mm"] = state.objective_position_mm
123 db["expected_pattern_duration_s"] = state.expected_pattern_duration_s
125 beam_shift = state.beam_shift_um
126 db["beam_shift_um_x"] = beam_shift.x
127 db["beam_shift_um_y"] = beam_shift.y
129 # we can name these differently depending on the needs of the GUI
130 pattern = state.pattern
131 db["laser_pattern_mode"] = pattern.mode.value
132 db["laser_pattern_rotation_deg"] = pattern.rotation_deg
133 db["laser_pattern_pulses_per_pixel"] = pattern.pulses_per_pixel
134 db["laser_pattern_pixel_dwell_ms"] = pattern.pixel_dwell_ms
136 geometry = pattern.geometry
137 db["passes"] = geometry.passes
138 db["laser_scan_type"] = geometry.scan_type.value
139 db["geometry_type"] = geometry.type.value
141 if geometry.type == tbt.LaserPatternType.BOX:
142 db["size_x_um"] = geometry.size_x_um
143 db["size_y_um"] = geometry.size_y_um
144 db["pitch_x_um"] = geometry.pitch_x_um
145 db["pitch_y_um"] = geometry.pitch_y_um
146 db["coordinate_ref"] = geometry.coordinate_ref
148 if geometry.type == tbt.LaserPatternType.LINE:
149 db["size_um"] = geometry.size_um
150 db["pitch_um"] = geometry.pitch_um
152 return db
155def laser_connected() -> bool:
156 """
157 Check if the laser is connected.
159 This function tests the connection to the laser and returns True if the connection is successful.
161 Returns
162 -------
163 bool
164 True if the laser is connected, False otherwise.
165 """
166 connect_msg = "Connection test successful.\n"
167 laser_status = io.StringIO()
168 try:
169 with contextlib.redirect_stdout(laser_status):
170 tfs_laser.TestConnection()
171 except:
172 return False
173 else:
174 if laser_status.getvalue() == connect_msg:
175 return True
176 return False
179def _device_connections() -> tbt.DeviceStatus:
180 """
181 Check the connection status of the laser and associated external devices.
183 This function checks the connection status of the laser, EBSD, and EDS devices. It is meant to be a quick tool for the GUI and does not provide additional information for troubleshooting.
185 Returns
186 -------
187 tbt.DeviceStatus
188 The connection status of the laser, EBSD, and EDS devices.
189 """
190 # laser must be connected to connect with other devices:
191 if not laser_connected():
192 laser = tbt.RetractableDeviceState.ERROR
193 ebsd = tbt.RetractableDeviceState.ERROR
194 eds = tbt.RetractableDeviceState.ERROR
195 else:
196 laser = tbt.RetractableDeviceState.CONNECTED
197 ebsd = devices.connect_EBSD() # retractable device state
198 eds = devices.connect_EDS() # retractable device state
200 return tbt.DeviceStatus(
201 laser=laser,
202 ebsd=ebsd,
203 eds=eds,
204 )
207def pattern_mode(mode: tbt.LaserPatternMode) -> bool:
208 """
209 Set the laser pattern mode.
211 This function sets the laser pattern mode and verifies that it has been set correctly.
213 Parameters
214 ----------
215 mode : tbt.LaserPatternMode
216 The laser pattern mode to set.
218 Returns
219 -------
220 bool
221 True if the pattern mode is set correctly.
223 Raises
224 ------
225 SystemError
226 If the pattern mode cannot be set correctly.
227 """
228 tfs_laser.Patterning_Mode(mode.value)
229 laser_state = factory.active_laser_state()
230 if laser_state.pattern.mode != mode:
231 raise SystemError("Unable to correctly set pattern mode.")
232 return True
235def pulse_energy_uj(
236 energy_uj: float,
237 energy_tol_uj: float = Constants.laser_energy_tol_uj,
238 delay_s: float = 3.0,
239) -> bool:
240 """
241 Set the pulse energy on the laser.
243 This function sets the pulse energy on the laser and verifies that it has been set correctly. It should be done after setting the pulse divider.
245 Parameters
246 ----------
247 energy_uj : float
248 The pulse energy to set in microjoules.
249 energy_tol_uj : float, optional
250 The tolerance for the pulse energy in microjoules (default is Constants.laser_energy_tol_uj).
251 delay_s : float, optional
252 The delay in seconds after setting the pulse energy (default is 3.0 seconds).
254 Returns
255 -------
256 bool
257 True if the pulse energy is set correctly.
259 Raises
260 ------
261 ValueError
262 If the pulse energy cannot be set correctly.
263 """
264 tfs_laser.Laser_SetPulseEnergy_MicroJoules(energy_uj)
265 time.sleep(delay_s)
266 laser_state = factory.active_laser_state()
267 if not ut.in_interval(
268 val=laser_state.pulse_energy_uj,
269 limit=tbt.Limit(
270 min=energy_uj - energy_tol_uj,
271 max=energy_uj + energy_tol_uj,
272 ),
273 type=tbt.IntervalType.CLOSED,
274 ):
275 raise ValueError(
276 f"Could not properly set pulse energy, requested '{energy_uj}' uJ",
277 f"Current settings is {round(laser_state.pulse_energy_uj, 3)} uJ",
278 )
279 return True
282def pulse_divider(
283 divider: int,
284 delay_s: float = Constants.laser_delay_s,
285) -> bool:
286 """
287 Set the pulse divider on the laser.
289 This function sets the pulse divider on the laser and verifies that it has been set correctly.
291 Parameters
292 ----------
293 divider : int
294 The pulse divider to set.
295 delay_s : float, optional
296 The delay in seconds after setting the pulse divider (default is Constants.laser_delay_s).
298 Returns
299 -------
300 bool
301 True if the pulse divider is set correctly.
303 Raises
304 ------
305 ValueError
306 If the pulse divider cannot be set correctly.
307 """
308 tfs_laser.Laser_PulseDivider(divider)
309 time.sleep(delay_s)
310 laser_state = factory.active_laser_state()
311 if laser_state.pulse_divider != divider:
312 raise ValueError(
313 f"Could not properly set pulse divider, requested '{divider}'",
314 f"Current settings have a divider of {laser_state.pulse_divider}.",
315 )
316 return True
319def set_wavelength(
320 wavelength: tbt.LaserWavelength,
321 frequency_khz: float = 60, # make constnat
322 timeout_s: int = 20, # 120, # make constant
323 num_attempts: int = 2, # TODO make a constant
324 delay_s: int = 5, # make a constant
325) -> bool:
326 """
327 Set the wavelength and frequency of the laser.
329 This function sets the wavelength and frequency of the laser and verifies that they have been set correctly.
331 Parameters
332 ----------
333 wavelength : tbt.LaserWavelength
334 The wavelength to set.
335 frequency_khz : float, optional
336 The frequency to set in kHz (default is 60 kHz).
337 timeout_s : int, optional
338 The timeout in seconds for each attempt (default is 20 seconds).
339 num_attempts : int, optional
340 The number of attempts to set the wavelength and frequency (default is 2).
341 delay_s : int, optional
342 The delay in seconds between checks (default is 5 seconds).
344 Returns
345 -------
346 bool
347 True if the wavelength and frequency are set correctly, False otherwise.
348 """
350 def correct_preset(laser_state: tbt.LaserState):
351 if laser_state.wavelength_nm == wavelength:
352 return math.isclose(laser_state.frequency_khz, frequency_khz, rel_tol=0.05)
353 # TODO use constant for tolerance):
354 return False
356 for _ in range(num_attempts):
357 if correct_preset(factory.active_laser_state()):
358 return True
359 print("Adjusting preset...")
360 tfs_laser.Laser_SetPreset(
361 wavelength_nm=wavelength.value, frequency_kHz=frequency_khz
362 )
363 time_remaining = timeout_s
364 while time_remaining > 0:
365 laser_state = factory.active_laser_state()
366 # print(time_remaining, laser_state.frequency_khz)
367 if correct_preset(laser_state=laser_state):
368 return True
369 time.sleep(delay_s)
370 time_remaining -= delay_s
372 return False
375def read_power(delay_s: float = Constants.laser_delay_s) -> float:
376 """
377 Measure the laser power in watts.
379 This function measures the laser power using an external power meter.
381 Parameters
382 ----------
383 delay_s : float, optional
384 The delay in seconds before reading the power (default is Constants.laser_delay_s).
386 Returns
387 -------
388 float
389 The measured laser power in watts.
390 """
391 tfs_laser.Laser_ExternalPowerMeter_PowerMonitoringON()
392 tfs_laser.Laser_ExternalPowerMeter_SetZeroOffset()
393 tfs_laser.Laser_FireContinuously_Start()
394 time.sleep(delay_s)
395 power = tfs_laser.Laser_ExternalPowerMeter_ReadPower()
396 tfs_laser.Laser_FireContinuously_Stop()
397 tfs_laser.Laser_ExternalPowerMeter_PowerMonitoringOFF()
398 return power
401def insert_shutter(microscope: tbt.Microscope) -> bool:
402 """
403 Insert the laser shutter.
405 This function inserts the laser shutter and verifies that it has been inserted correctly.
407 Parameters
408 ----------
409 microscope : tbt.Microscope
410 The microscope object for which to insert the laser shutter.
412 Returns
413 -------
414 bool
415 True if the laser shutter is successfully inserted.
417 Raises
418 ------
419 SystemError
420 If the laser shutter cannot be inserted.
421 """
422 devices.CCD_view(microscope=microscope)
423 if tfs_laser.Shutter_GetState() != "Inserted":
424 tfs_laser.Shutter_Insert()
425 state = tfs_laser.Shutter_GetState()
426 if state != "Inserted":
427 raise SystemError(
428 f"Could not insert laser shutter, current laser shutter state is '{state}'."
429 )
430 devices.CCD_pause(microscope=microscope)
431 return True
434def retract_shutter(microscope: tbt.Microscope) -> bool:
435 """
436 Retract the laser shutter.
438 This function retracts the laser shutter and verifies that it has been retracted correctly.
440 Parameters
441 ----------
442 microscope : tbt.Microscope
443 The microscope object for which to retract the laser shutter.
445 Returns
446 -------
447 bool
448 True if the laser shutter is successfully retracted.
450 Raises
451 ------
452 SystemError
453 If the laser shutter cannot be retracted.
454 """
455 devices.CCD_view(microscope=microscope)
456 if tfs_laser.Shutter_GetState() != "Retracted":
457 tfs_laser.Shutter_Retract()
458 state = tfs_laser.Shutter_GetState()
459 if state != "Retracted":
460 raise SystemError(
461 f"Could not retract laser shutter, current laser shutter state is '{state}'."
462 )
463 devices.CCD_pause(microscope=microscope)
464 return True
467def pulse_polarization(
468 polarization: tbt.LaserPolarization, wavelength: tbt.LaserWavelength
469) -> bool:
470 """
471 Configure the polarization of the laser light.
473 This function sets the polarization of the laser light based on the specified polarization and wavelength. The polarization is controlled via "FlipperConfiguration", which takes the following values:
474 - Waveplate_None switches to Vert. (P)
475 - Waveplate_1030 switches to Horiz. (S)
476 - Waveplate_515 switches to Horiz. (S)
478 Parameters
479 ----------
480 polarization : tbt.LaserPolarization
481 The desired polarization of the laser light.
482 wavelength : tbt.LaserWavelength
483 The wavelength of the laser light.
485 Returns
486 -------
487 bool
488 True if the polarization is set correctly.
490 Raises
491 ------
492 KeyError
493 If the laser wavelength or pulse polarization is invalid.
494 """
495 if polarization == tbt.LaserPolarization.VERTICAL:
496 tfs_laser.FlipperConfiguration("Waveplate_None")
497 return True
498 elif polarization == tbt.LaserPolarization.HORIZONTAL:
499 match_db = {
500 tbt.LaserWavelength.NM_1030: "Waveplate_1030",
501 tbt.LaserWavelength.NM_515: "Waveplate_515",
502 }
503 try:
504 tfs_laser.FlipperConfiguration(match_db[wavelength])
505 except KeyError:
506 raise KeyError(
507 f"Invalid laser wavelength, valid options are {[i.value for i in tbt.LaserWavelength]}"
508 )
509 return True
510 else:
511 raise KeyError(
512 f"Invalid pulse polarization, valid options are {[i.value for i in tbt.LaserPolarization]}"
513 )
516def pulse_settings(pulse: tbt.LaserPulse) -> True:
517 """
518 Apply the pulse settings to the laser.
520 This function applies the specified pulse settings to the laser, including wavelength, pulse divider, pulse energy, and polarization.
522 Parameters
523 ----------
524 pulse : tbt.LaserPulse
525 The pulse settings to apply.
527 Returns
528 -------
529 bool
530 True if the pulse settings are applied correctly.
531 """
532 active_state = factory.active_laser_state()
533 if pulse.wavelength_nm != active_state.wavelength_nm:
534 # wavelength settings
535 set_wavelength(wavelength=pulse.wavelength_nm)
536 pulse_divider(divider=pulse.divider)
537 pulse_energy_uj(energy_uj=pulse.energy_uj)
538 pulse_polarization(polarization=pulse.polarization, wavelength=pulse.wavelength_nm)
539 return True
542def retract_laser_objective() -> bool:
543 """
544 Retract the laser objective to a safe position.
546 This function retracts the laser objective to a predefined safe position.
548 Returns
549 -------
550 bool
551 True if the laser objective is successfully retracted.
552 """
553 objective_position(position_mm=Constants.laser_objective_retracted_mm)
554 return True
557def objective_position(
558 position_mm: float,
559 tolerance_mm=Constants.laser_objective_tolerance_mm,
560) -> bool:
561 """
562 Move the laser objective to the requested position.
564 This function moves the laser objective to the specified position and verifies that it has been moved correctly.
566 Parameters
567 ----------
568 position_mm : float
569 The desired position of the laser objective in millimeters.
570 tolerance_mm : float, optional
571 The tolerance for the laser objective position in millimeters (default is Constants.laser_objective_tolerance_mm).
573 Returns
574 -------
575 bool
576 True if the laser objective is moved to the requested position correctly.
578 Raises
579 ------
580 ValueError
581 If the requested position is out of range.
582 SystemError
583 If the laser objective cannot be moved to the requested position.
584 """
585 tfs_laser.LIP_UnlockZ()
587 if not ut.in_interval(
588 val=position_mm,
589 limit=Constants.laser_objective_limit_mm,
590 type=tbt.IntervalType.CLOSED,
591 ):
592 raise ValueError(
593 f"Requested laser objective position of {position_mm} mm is out of range. Laser objective can travel from {Constants.laser_objective_limit_mm.min} to {Constants.laser_objective_limit_mm.max} mm."
594 )
596 for _ in range(2):
597 if ut.in_interval(
598 val=tfs_laser.LIP_GetZPosition(),
599 limit=tbt.Limit(
600 min=position_mm - tolerance_mm, max=position_mm + tolerance_mm
601 ),
602 type=tbt.IntervalType.CLOSED,
603 ):
604 return True
605 tfs_laser.LIP_SetZPosition(position_mm, asynchronously=False)
607 raise SystemError(
608 f"Unable to move laser injection port objective to requested position of {position_mm} +/- {tolerance_mm} mm.",
609 f"Currently at {tfs_laser.LIP_GetZPosition()} mm.",
610 )
613def _shift_axis(
614 target: float,
615 current: float,
616 tolerance: float,
617 axis: str,
618) -> bool:
619 """
620 Helper function for beam shift.
622 This function adjusts the beam shift for the specified axis to the target value within the given tolerance.
624 Parameters
625 ----------
626 target : float
627 The target value for the beam shift.
628 current : float
629 The current value of the beam shift.
630 tolerance : float
631 The tolerance for the beam shift.
632 axis : str
633 The axis to adjust ("X" or "Y").
635 Returns
636 -------
637 bool
638 True if the beam shift is adjusted to the target value correctly, False otherwise.
639 """
640 for _ in range(2):
641 if ut.in_interval(
642 val=current,
643 limit=tbt.Limit(
644 min=target - tolerance,
645 max=target + tolerance,
646 ),
647 type=tbt.IntervalType.CLOSED,
648 ):
649 return True
650 if axis == "X":
651 tfs_laser.BeamShift_Set_X(value=target)
652 current = tfs_laser.BeamShift_Get_X()
653 if axis == "Y":
654 tfs_laser.BeamShift_Set_Y(value=target)
655 current = tfs_laser.BeamShift_Get_Y()
657 return False
660def beam_shift(
661 shift_um: tbt.Point,
662 shift_tolerance_um: float = Constants.laser_beam_shift_tolerance_um,
663) -> bool:
664 """
665 Adjust the laser beam shift to the specified values.
667 This function adjusts the laser beam shift to the specified x and y values within the given tolerance.
669 Parameters
670 ----------
671 shift_um : tbt.Point
672 The target beam shift values in micrometers.
673 shift_tolerance_um : float, optional
674 The tolerance for the beam shift in micrometers (default is Constants.laser_beam_shift_tolerance_um).
676 Returns
677 -------
678 bool
679 True if the beam shift is adjusted to the target values correctly.
681 Raises
682 ------
683 ValueError
684 If the beam shift cannot be adjusted to the target values.
685 """
686 current_shift_x = tfs_laser.BeamShift_Get_X()
687 current_shift_y = tfs_laser.BeamShift_Get_Y()
689 if not (
690 _shift_axis(
691 target=shift_um.x,
692 current=current_shift_x,
693 tolerance=shift_tolerance_um,
694 axis="X",
695 )
696 and _shift_axis(
697 target=shift_um.y,
698 current=current_shift_y,
699 tolerance=shift_tolerance_um,
700 axis="Y",
701 )
702 ):
703 raise ValueError(
704 f"Unable to set laser beam shift. Requested beam shift of (x,y) = ({shift_um.x} um,{shift_um.y} um,), but current beam shift is ({tfs_laser.BeamShift_Get_X()} um, {tfs_laser.BeamShift_Get_Y()} um)."
705 )
706 return True
709def create_pattern(pattern: tbt.LaserPattern):
710 """
711 Create a laser pattern and check that it is set correctly.
713 This function creates a laser pattern based on the specified pattern settings and verifies that it has been set correctly.
715 Parameters
716 ----------
717 pattern : tbt.LaserPattern
718 The laser pattern settings to create.
720 Returns
721 -------
722 bool
723 True if the pattern is created and set correctly.
725 Raises
726 ------
727 ValueError
728 If the pattern geometry type is unsupported.
729 SystemError
730 If the pattern cannot be set correctly.
731 """
732 pattern_mode(mode=pattern.mode)
734 # check if pattern is empty or not
735 if isinstance(pattern.geometry, tbt.LaserBoxPattern):
736 box = pattern.geometry
737 tfs_laser.Patterning_CreatePattern_Box(
738 sizeX_um=box.size_x_um,
739 sizeY_um=box.size_y_um,
740 pitchX_um=box.pitch_x_um,
741 pitchY_um=box.pitch_y_um,
742 dwellTime_ms=pattern.pixel_dwell_ms,
743 passes_int=box.passes,
744 pulsesPerPixel_int=pattern.pulses_per_pixel,
745 scanrotation_degrees=pattern.rotation_deg,
746 scantype_string=box.scan_type.value, # cast enum to string
747 coordinateReference_string=box.coordinate_ref.value, # cast enum to string
748 )
749 elif isinstance(pattern.geometry, tbt.LaserLinePattern):
750 line = pattern.geometry
751 tfs_laser.Patterning_CreatePattern_Line(
752 sizeX_um=line.size_um,
753 pitchX_um=line.pitch_um,
754 dwellTime_ms=pattern.pixel_dwell_ms,
755 passes_int=line.passes,
756 pulsesPerPixel_int=pattern.pulses_per_pixel,
757 scanrotation_degrees=pattern.rotation_deg,
758 scantype_string=line.scan_type.value, # cast enum to string
759 )
760 else:
761 raise ValueError(
762 f"Unsupported pattern geometry of type '{type(pattern.geometry)}'. Supported types are {tbt.LaserLinePattern, tbt.LaserBoxPattern}"
763 )
764 laser_state = factory.active_laser_state()
765 if laser_state.pattern != pattern:
766 raise SystemError("Unable to correctly set Pattern.")
767 return True
770def apply_laser_settings(image_beam: tbt.Beam, settings: tbt.LaserSettings) -> bool:
771 """
772 Apply the laser settings to the current patterning.
774 This function applies the specified laser settings to the current patterning, including beam scan rotation, pulse settings, objective position, beam shift, and patterning settings.
776 Parameters
777 ----------
778 image_beam : tbt.Beam
779 The beam settings for the image.
780 settings : tbt.LaserSettings
781 The laser settings to apply.
783 Returns
784 -------
785 bool
786 True if the laser settings are applied correctly.
787 """
788 microscope = settings.microscope
790 # forces rotation of electron beam for laser (TFS required)
791 img.beam_scan_rotation(
792 beam=image_beam,
793 microscope=microscope,
794 rotation_deg=Constants.image_scan_rotation_for_laser_deg,
795 )
796 # pulse settings
797 pulse_settings(pulse=settings.pulse)
799 # objective position
800 objective_position(settings.objective_position_mm)
802 # beam shift
803 beam_shift(settings.beam_shift_um)
805 # apply patterning settings
806 create_pattern(pattern=settings.pattern)
808 return True
811def execute_patterning() -> bool:
812 """
813 Execute the laser patterning.
815 This function starts the laser patterning process.
817 Returns
818 -------
819 bool
820 True if the patterning process is started successfully.
821 """
822 tfs_laser.Patterning_Start()
824 return True
827### main methods
830def mill_region(
831 settings: tbt.LaserSettings,
832) -> bool:
833 """
834 Perform laser milling on a specified region.
836 This function performs laser milling on a specified region using the provided laser settings. It checks the laser connection, applies the laser settings, inserts the shutter, executes the patterning, retracts the shutter, and resets the scan rotation.
838 Parameters
839 ----------
840 settings : tbt.LaserSettings
841 The laser settings to use for milling.
843 Returns
844 -------
845 bool
846 True if the milling process is completed successfully.
848 Raises
849 ------
850 SystemError
851 If the laser is not connected.
852 """
853 # check connection
854 if not laser_connected():
855 raise SystemError("Laser is not connected")
857 microscope = settings.microscope
858 # initial_scan_rotation of ebeam
859 devices.device_access(microscope=microscope)
860 active_beam = factory.active_beam_with_settings(microscope=microscope)
861 scan_settings = factory.active_scan_settings(microscope=microscope)
862 initial_scan_rotation_deg = scan_settings.rotation_deg
864 # apply laser settings
865 apply_laser_settings(
866 image_beam=active_beam,
867 settings=settings,
868 )
870 # insert shutter
871 insert_shutter(microscope=microscope)
873 # execute patterning
874 execute_patterning()
876 # retract shutter
877 retract_shutter(microscope=microscope)
878 time.sleep(1)
880 # reset scan rotation
881 img.beam_scan_rotation(
882 beam=active_beam,
883 microscope=microscope,
884 rotation_deg=initial_scan_rotation_deg,
885 )
887 return True
890def laser_operation(
891 step: tbt.Step, general_settings: tbt.GeneralSettings, slice_number: int
892) -> bool:
893 """
894 Perform a laser operation based on the specified step and settings.
896 This function performs a laser operation using the provided step and general settings. It logs the laser power before and after the operation, and performs the milling process.
898 Parameters
899 ----------
900 step : tbt.Step
901 The step object containing the operation settings.
902 general_settings : tbt.GeneralSettings
903 The general settings object.
904 slice_number : int
905 The slice number for the operation.
907 Returns
908 -------
909 bool
910 True if the laser operation is completed successfully.
911 """
912 # log laser power before
913 laser_power_w = read_power()
914 log.laser_power(
915 step_number=step.number,
916 step_name=step.name,
917 slice_number=slice_number,
918 log_filepath=general_settings.log_filepath,
919 dataset_name=Constants.pre_lasing_dataset_name,
920 power_w=laser_power_w,
921 )
923 mill_region(settings=step.operation_settings)
925 # log laser power after
926 laser_power_w = read_power()
927 log.laser_power(
928 step_number=step.number,
929 step_name=step.name,
930 slice_number=slice_number,
931 log_filepath=general_settings.log_filepath,
932 dataset_name=Constants.post_lasing_dataset_name,
933 power_w=laser_power_w,
934 )
936 return True
939def map_ebsd() -> bool:
940 """
941 Start an EBSD map and ensure it takes the minimum expected time.
943 This function starts an EBSD map and checks that the mapping process takes at least the minimum expected time. If the mapping process is too short, it raises an error.
945 Returns
946 -------
947 bool
948 True if the EBSD mapping is completed successfully.
950 Raises
951 ------
952 ValueError
953 If the mapping process does not take the minimum expected time.
954 """
955 start_time = time.time()
956 tfs_laser.EBSD_StartMap()
957 time.sleep(1)
958 end_time = time.time()
959 map_time = end_time - start_time
960 if map_time < Constants.min_map_time_s:
961 raise ValueError(
962 f"Mapping did not take minimum expected time of {Constants.min_map_time_s} seconds, please reset EBSD mapping software"
963 )
964 print(f"\t\tMapping Complete in {int(map_time)} seconds.")
965 return True
968def map_eds() -> bool:
969 """
970 Start an EDS map and ensure it takes the minimum expected time.
972 This function starts an EDS map and checks that the mapping process takes at least the minimum expected time. If the mapping process is too short, it raises an error.
974 Returns
975 -------
976 bool
977 True if the EDS mapping is completed successfully.
979 Raises
980 ------
981 ValueError
982 If the mapping process does not take the minimum expected time.
983 """
984 start_time = time.time()
985 tfs_laser.EDS_StartMap()
986 time.sleep(1)
987 end_time = time.time()
988 map_time = end_time - start_time
989 if map_time < Constants.min_map_time_s:
990 raise ValueError(
991 f"Mapping did not take minimum expected time of {Constants.min_map_time_s} seconds, please reset EDS mapping software"
992 )
993 print(f"\t\tMapping Complete in {int(map_time)} seconds.")
994 return True