Coverage for src\pytribeam\image.py: 85%

277 statements  

« prev     ^ index     » next       coverage.py v7.5.1, created at 2025-03-04 17:41 -0800

1""" 

2Microscope Imaging Module 

3========================= 

4 

5This module provides a set of functions for configuring and operating the microscope for imaging purposes. 

6It includes functions for setting beam parameters, detector settings, and capturing images with custom 

7or preset resolutions. 

8 

9Functions 

10--------- 

11beam_angular_correction(microscope, dynamic_focus, tilt_correction, delay_s=0.5) 

12 Uses auto mode to set tilt correction and dynamic focus. 

13 

14beam_current(beam, microscope, current_na, current_tol_na, delay_s=5.0) 

15 Sets the current for the selected beam type, with inputs in units of nanoamps. 

16 

17beam_dwell_time(beam, microscope, dwell_us, delay_s=0.1) 

18 Sets the dwell time for the selected beam, with inputs in units of microseconds. 

19 

20beam_hfw(beam, microscope, hfw_mm, delay_s=0.1) 

21 Sets the horizontal field width for the selected beam, with inputs in units of millimeters. 

22 

23beam_ready(beam, microscope, delay_s=5.0, attempts=2) 

24 Checks if the beam is on or blanked, and tries to turn it on and unblank it if possible. 

25 

26beam_scan_full_frame(beam, microscope) 

27 Sets the beam scan mode to full frame. 

28 

29beam_scan_resolution(beam, microscope, resolution, delay_s=0.1) 

30 Sets the scan resolution for the selected beam, with inputs in units of preset resolutions. 

31 

32beam_scan_rotation(beam, microscope, rotation_deg, delay_s=0.1) 

33 Sets the scan rotation for the selected beam, with inputs in units of degrees. 

34 

35beam_voltage(beam, microscope, voltage_kv, voltage_tol_kv, delay_s=5.0) 

36 Sets the voltage for a given beam type, with inputs in units of kilovolts. 

37 

38beam_working_dist(beam, microscope, wd_mm, delay_s=0.1) 

39 Sets the working distance for the selected beam, with inputs in units of millimeters. 

40 

41collect_multiple_images(multiple_img_settings, num_frames) 

42 Sets up scanning for multiple frames. 

43 

44collect_single_image(save_path, img_settings) 

45 Collects a single frame image with defined image settings. 

46 

47detector_auto_cb(microscope, beam, settings, delay_s=0.1) 

48 Runs the detector auto contrast-brightness function. 

49 

50detector_brightness(microscope, brightness, delay_s=0.1) 

51 Sets the detector brightness with input from 0 to 1. 

52 

53detector_cb(microscope, detector_settings, beam) 

54 Sets detector contrast and brightness. 

55 

56detector_contrast(microscope, contrast, delay_s=0.1) 

57 Sets the detector contrast with input from 0 to 1. 

58 

59detector_mode(microscope, detector_mode, delay_s=0.1) 

60 Sets the detector mode. 

61 

62detector_type(microscope, detector, delay_s=0.1) 

63 Sets the detector type. 

64 

65grab_custom_resolution_frame(img_settings, save_path) 

66 Captures a single frame image using custom resolutions and saves it to the specified path. 

67 

68grab_preset_resolution_frame(img_settings) 

69 Captures a single frame image using preset resolutions. 

70 

71image_operation(step, image_settings, general_settings, slice_number) 

72 Performs an image operation based on the specified settings. 

73 

74imaging_detector(img_settings) 

75 Prepares the detector and inserts it if applicable. 

76 

77imaging_device(microscope, beam) 

78 Prepares the imaging beam, viewing quad, and the beam voltage and current. 

79 

80imaging_scan(img_settings) 

81 Sets all scan settings except for the resolution. 

82 

83prepare_imaging(img_settings) 

84 Prepares various imaging settings. 

85 

86set_beam_device(microscope, device, delay_s=0.1) 

87 Sets the active imaging device. 

88 

89set_view(microscope, quad) 

90 Sets the active view to the specified quad. 

91 

92""" 

93 

94# Default python modules 

95# from functools import singledispatch 

96from pathlib import Path 

97import time 

98import warnings 

99from typing import List 

100import math 

101 

102# Local scripts 

103import pytribeam.constants as cs 

104import pytribeam.insertable_devices as devices 

105import pytribeam.types as tbt 

106import pytribeam.utilities as ut 

107 

108 

109def beam_angular_correction( 

110 microscope: tbt.Microscope, 

111 dynamic_focus: bool, 

112 tilt_correction: bool, 

113 # correction_angle_deg: float = None, 

114 delay_s: float = 0.5, 

115) -> bool: 

116 """ 

117 Uses auto mode to set tilt correction and dynamic focus. 

118 

119 This function configures the electron beam's angular correction mode to automatic, 

120 sets the scan rotation to zero, and enables or disables dynamic focus and tilt correction 

121 based on the provided parameters. 

122 

123 Parameters 

124 ---------- 

125 microscope : tbt.Microscope 

126 The microscope object to configure. 

127 dynamic_focus : bool 

128 If True, dynamic focus will be turned on. If False, dynamic focus will be turned off. 

129 tilt_correction : bool 

130 If True, tilt correction will be turned on. If False, tilt correction will be turned off. 

131 delay_s : float, optional 

132 The delay in seconds to wait after turning dynamic focus or tilt correction on or off (default is 0.5). 

133 

134 Returns 

135 ------- 

136 bool 

137 True if the configuration is successful, False otherwise. 

138 

139 Raises 

140 ------ 

141 SystemError 

142 If unable to turn dynamic focus or tilt correction on or off. 

143 

144 Examples 

145 -------- 

146 >>> import pytribeam.types as tbt 

147 >>> microscope = tbt.Microscope() 

148 >>> microscope.connect("localhost") 

149 Client connecting to [localhost:7520]... 

150 Client connected to [localhost:7520] 

151 >>> success = beam_angular_correction(microscope, dynamic_focus=True, tilt_correction=False) 

152 >>> print(success) 

153 True 

154 """ 

155 angular_correction = microscope.beams.electron_beam.angular_correction 

156 angular_correction.mode = tbt.AngularCorrectionMode.AUTOMATIC 

157 # scan rotation must be zero for auto mode 

158 microscope.beams.electron_beam.scanning.rotation.value = 0.0 

159 

160 # manual adjustment not implemented yet, only works at zero scan rotation 

161 # if correction_angle_deg is not None: 

162 # angular_correction.mode = tbt.AngularCorrectionMode.MANUAL 

163 if dynamic_focus: 

164 angular_correction.dynamic_focus.turn_on() 

165 time.sleep(delay_s) 

166 if not angular_correction.dynamic_focus.is_on: 

167 raise SystemError("Unable to turn dynamic focus on.") 

168 if (not dynamic_focus) or (dynamic_focus is None): 

169 angular_correction.dynamic_focus.turn_off() 

170 time.sleep(delay_s) 

171 if angular_correction.dynamic_focus.is_on: 

172 raise SystemError("Unable to turn dynamic focus off.") 

173 

174 if tilt_correction: 

175 angular_correction.tilt_correction.turn_on() 

176 time.sleep(delay_s) 

177 if not angular_correction.tilt_correction.is_on: 

178 raise SystemError("Unable to turn tilt correction on.") 

179 if (not tilt_correction) or (tilt_correction is None): 

180 angular_correction.tilt_correction.turn_off() 

181 time.sleep(delay_s) 

182 if angular_correction.tilt_correction.is_on: 

183 raise SystemError("Unable to turn tilt correction off.") 

184 

185 

186def beam_current( 

187 beam: tbt.Beam, 

188 microscope: tbt.Microscope, 

189 current_na: float, 

190 current_tol_na: float, 

191 delay_s: float = 5.0, 

192) -> bool: 

193 """ 

194 Sets the current for the selected beam type, with inputs in units of nanoamps. 

195 

196 This function sets the beam current for the specified beam type on the microscope. 

197 If the current difference exceeds the tolerance, it adjusts the beam current and 

198 waits for the specified delay. 

199 

200 Parameters 

201 ---------- 

202 beam : tbt.Beam 

203 The beam type to configure. 

204 microscope : tbt.Microscope 

205 The microscope object to configure. 

206 current_na : float 

207 The desired beam current in nanoamps. 

208 current_tol_na : float 

209 The tolerance for the beam current in nanoamps. 

210 delay_s : float, optional 

211 The delay in seconds to wait after adjusting the beam current (default is 5.0). 

212 

213 Returns 

214 ------- 

215 bool 

216 True if the beam current is set successfully, False otherwise. 

217 

218 Raises 

219 ------ 

220 ValueError 

221 If the beam current cannot be adjusted within the specified tolerance. 

222 

223 Examples 

224 -------- 

225 >>> import pytribeam.types as tbt 

226 >>> microscope = tbt.Microscope() 

227 >>> microscope.connect("localhost") 

228 Client connecting to [localhost:7520]... 

229 Client connected to [localhost:7520] 

230 >>> beam = tbt.ElectronBeam(settings=None) 

231 >>> success = beam_current(beam, microscope, current_na=1.0, current_tol_na=0.1) 

232 >>> print(success) 

233 True 

234 """ 

235 selected_beam = ut.beam_type(beam, microscope) 

236 

237 exisiting_current_a = selected_beam.beam_current.value # amps 

238 delta_current_na = abs( 

239 exisiting_current_a * cs.Conversions.A_TO_NA - current_na 

240 ) # nanoamps 

241 if delta_current_na > current_tol_na: 

242 warnings.warn( 

243 "Requested beam current is not the current setting, imaging conditions may be non-ideal." 

244 ) 

245 print("Adjusting beam current...") 

246 selected_beam.beam_current.value = current_na * cs.Conversions.NA_TO_A 

247 time.sleep(delay_s) 

248 

249 new_current_na = selected_beam.beam_current.value * cs.Conversions.A_TO_NA 

250 current_diff_na = abs(new_current_na - current_na) 

251 if current_diff_na > current_tol_na: 

252 raise ValueError( 

253 f"""Could not correctly adjust beam voltage, 

254 requested {current_na} nA, current beam current is 

255 {new_current_na} nA""" 

256 ) 

257 

258 return True 

259 

260 

261def beam_dwell_time( 

262 beam: tbt.Beam, 

263 microscope: tbt.Microscope, 

264 dwell_us: float, 

265 delay_s: float = 0.1, 

266) -> bool: 

267 """ 

268 Sets the dwell time for the selected beam, with inputs in units of microseconds. 

269 

270 This function sets the dwell time for the specified beam type on the microscope. 

271 It converts the dwell time from microseconds to seconds, sets the dwell time, and 

272 waits for the specified delay. 

273 

274 Parameters 

275 ---------- 

276 beam : tbt.Beam 

277 The beam type to configure. 

278 microscope : tbt.Microscope 

279 The microscope object to configure. 

280 dwell_us : float 

281 The desired dwell time in microseconds. 

282 delay_s : float, optional 

283 The delay in seconds to wait after adjusting the dwell time (default is 0.1). 

284 

285 Returns 

286 ------- 

287 bool 

288 True if the dwell time is set successfully, False otherwise. 

289 

290 Raises 

291 ------ 

292 ValueError 

293 If the dwell time cannot be adjusted correctly. 

294 

295 Examples 

296 -------- 

297 >>> import pytribeam.types as tbt 

298 >>> microscope = tbt.Microscope() 

299 >>> microscope.connect("localhost") 

300 Client connecting to [localhost:7520]... 

301 Client connected to [localhost:7520] 

302 >>> beam = tbt.ElectronBeam(settings=None) 

303 >>> success = beam_dwell_time(beam, microscope, dwell_us=10.0) 

304 >>> print(success) 

305 True 

306 """ 

307 selected_beam = ut.beam_type(beam, microscope) 

308 dwell_s = dwell_us * cs.Conversions.US_TO_S 

309 selected_beam.scanning.dwell_time.value = dwell_s 

310 time.sleep(delay_s) 

311 if not math.isclose( 

312 selected_beam.scanning.dwell_time.value, 

313 dwell_s, 

314 rel_tol=cs.Constants.beam_dwell_tol_ratio, 

315 ): 

316 raise ValueError( 

317 f"""Could not correctly adjust dwell time, 

318 requested {dwell_s} seconds, current dwell time is 

319 {selected_beam.scanning.dwell_time.value} seconds""" 

320 ) 

321 

322 return True 

323 

324 

325def beam_hfw( 

326 beam: tbt.Beam, 

327 microscope: tbt.Microscope, 

328 hfw_mm: float, 

329 delay_s: float = 0.1, 

330 hfw_tol_mm: float = 1e-6, # 1 nm of tolerance 

331) -> bool: 

332 """ 

333 Sets the horizontal field width for the selected beam, with inputs in units of millimeters. 

334 

335 This function sets the horizontal field width (HFW) for the specified beam type on the microscope. 

336 It converts the HFW from millimeters to meters, sets the HFW, and waits for the specified delay. 

337 This should be done after adjusting the working distance. 

338 

339 Parameters 

340 ---------- 

341 beam : tbt.Beam 

342 The beam type to configure. 

343 microscope : tbt.Microscope 

344 The microscope object to configure. 

345 hfw_mm : float 

346 The desired horizontal field width in millimeters. 

347 delay_s : float, optional 

348 The delay in seconds to wait after adjusting the HFW (default is 0.1). 

349 

350 Returns 

351 ------- 

352 bool 

353 True if the HFW is set successfully, False otherwise. 

354 

355 Raises 

356 ------ 

357 ValueError 

358 If the HFW cannot be adjusted correctly. 

359 

360 Examples 

361 -------- 

362 >>> import pytribeam.types as tbt 

363 >>> microscope = tbt.Microscope() 

364 >>> microscope.connect("localhost") 

365 Client connecting to [localhost:7520]... 

366 Client connected to [localhost:7520] 

367 >>> beam = tbt.ElectronBeam(settings=None) 

368 >>> success = beam_hfw(beam, microscope, hfw_mm=1.0) 

369 >>> print(success) 

370 True 

371 """ 

372 selected_beam = ut.beam_type(beam, microscope) 

373 hfw_m = hfw_mm * cs.Conversions.MM_TO_M 

374 selected_beam.horizontal_field_width.value = hfw_m 

375 time.sleep(delay_s) 

376 if not math.isclose( 

377 selected_beam.horizontal_field_width.value, 

378 hfw_m, 

379 abs_tol=hfw_tol_mm 

380 * cs.Conversions.MM_TO_M, # convert to meters for autoscript calls 

381 ): 

382 raise ValueError( 

383 f"""Could not correctly adjust horizontal field width, 

384 requested {hfw_mm} millimeters, current field with is 

385 {selected_beam.horizontal_field_width.value*cs.Conversions.M_TO_MM} millimeters""" 

386 ) 

387 

388 return True 

389 

390 

391def beam_ready( 

392 beam: tbt.Beam, 

393 microscope: tbt.Microscope, 

394 delay_s: float = 5.0, 

395 attempts: int = 2, 

396) -> bool: 

397 """ 

398 Checks if the beam is on or blanked, and tries to turn it on and unblank it if possible. 

399 

400 This function checks the vacuum state, ensures the beam is on, and unblanks the beam if it is blanked. 

401 It makes multiple attempts to turn on and unblank the beam, waiting for the specified delay between attempts. 

402 

403 Parameters 

404 ---------- 

405 beam : tbt.Beam 

406 The beam type to check. 

407 microscope : tbt.Microscope 

408 The microscope object to check. 

409 delay_s : float, optional 

410 The delay in seconds to wait between attempts (default is 5.0). 

411 attempts : int, optional 

412 The number of attempts to turn on and unblank the beam (default is 2). 

413 

414 Returns 

415 ------- 

416 bool 

417 True if the beam is ready (on and unblanked), False otherwise. 

418 

419 Raises 

420 ------ 

421 ValueError 

422 If the vacuum is not pumped. 

423 If the beam cannot be turned on after the specified number of attempts. 

424 If the beam cannot be unblanked after the specified number of attempts. 

425 

426 Examples 

427 -------- 

428 >>> import pytribeam.types as tbt 

429 >>> microscope = tbt.Microscope() 

430 >>> microscope.connect("localhost") 

431 Client connecting to [localhost:7520]... 

432 Client connected to [localhost:7520] 

433 >>> beam = tbt.ElectronBeam(settings=None) 

434 >>> success = beam_ready(beam, microscope) 

435 >>> print(success) 

436 True 

437 """ 

438 selected_beam = ut.beam_type(beam, microscope) 

439 

440 # check vaccum 

441 vacuum = microscope.vacuum.chamber_state 

442 if vacuum != tbt.VacuumState.PUMPED.value: 

443 raise ValueError(f'Vacuum is not pumped, current state is "{vacuum}"') 

444 

445 # check beam on 

446 count: int = 0 

447 while count < attempts: 

448 beam_on = selected_beam.is_on 

449 if not beam_on: 

450 selected_beam.turn_on() 

451 time.sleep(delay_s) 

452 count += 1 

453 if not beam_on: 

454 raise ValueError( 

455 f"Unable to turn on {beam.type} beam after {attempts} attempts." 

456 ) 

457 

458 # check beam blank 

459 count: int = 0 

460 while count < attempts: 

461 beam_blank = selected_beam.is_blanked 

462 if beam_blank: 

463 selected_beam.unblank() 

464 time.sleep(delay_s) 

465 count += 1 

466 if beam_blank: 

467 raise ValueError( 

468 f"Unable to unblank {beam.type} beam after {attempts} attempts." 

469 ) 

470 

471 return True 

472 

473 

474def beam_scan_full_frame( 

475 beam: tbt.Beam, 

476 microscope: tbt.Microscope, 

477) -> bool: 

478 """ 

479 Set beam scan mode to full frame. 

480 

481 This function sets the scanning mode of the specified beam to full frame on the microscope. 

482 

483 Parameters 

484 ---------- 

485 beam : tbt.Beam 

486 The beam type to configure. 

487 microscope : tbt.Microscope 

488 The microscope object to configure. 

489 

490 Returns 

491 ------- 

492 bool 

493 True if the scan mode is set to full frame successfully, False otherwise. 

494 

495 Raises 

496 ------ 

497 SystemError 

498 If unable to set the scan mode to full frame. 

499 

500 Examples 

501 -------- 

502 >>> import pytribeam.types as tbt 

503 >>> microscope = tbt.Microscope() 

504 >>> microscope.connect("localhost") 

505 Client connecting to [localhost:7520]... 

506 Client connected to [localhost:7520] 

507 >>> beam = tbt.ElectronBeam(settings=None) 

508 >>> success = beam_scan_full_frame(beam, microscope) 

509 >>> print(success) 

510 True 

511 """ 

512 selected_beam = ut.beam_type(beam, microscope) 

513 selected_beam.scanning.mode.set_full_frame() 

514 scan_mode = selected_beam.scanning.mode.value 

515 if not scan_mode == tbt.ScanMode.FULL_FRAME.value: 

516 raise SystemError( 

517 f"Unable to set imaging to full frame. Current scan mode is {scan_mode}." 

518 ) 

519 return True 

520 

521 

522def beam_scan_resolution( 

523 beam: tbt.Beam, 

524 microscope: tbt.Microscope, 

525 resolution: tbt.Resolution, 

526 delay_s: float = 0.1, 

527) -> bool: 

528 """ 

529 Sets the scan resolution for the selected beam, with inputs in units of preset resolutions. 

530 

531 This function sets the scan resolution for the specified beam type on the microscope. 

532 It only works for preset resolutions and waits for the specified delay after setting the resolution. 

533 

534 Parameters 

535 ---------- 

536 beam : tbt.Beam 

537 The beam type to configure. 

538 microscope : tbt.Microscope 

539 The microscope object to configure. 

540 resolution : tbt.Resolution 

541 The desired scan resolution (must be a preset resolution). 

542 delay_s : float, optional 

543 The delay in seconds to wait after adjusting the resolution (default is 0.1). 

544 

545 Returns 

546 ------- 

547 bool 

548 True if the scan resolution is set successfully, False otherwise. 

549 

550 Raises 

551 ------ 

552 ValueError 

553 If a custom resolution is requested or if the resolution cannot be adjusted correctly. 

554 

555 Examples 

556 -------- 

557 >>> import pytribeam.types as tbt 

558 >>> import pytribeam.utility as ut 

559 >>> microscope = tbt.Microscope() 

560 >>> microscope.connect("localhost") 

561 Client connecting to [localhost:7520]... 

562 Client connected to [localhost:7520] 

563 >>> beam = tbt.ElectronBeam(settings=None) 

564 >>> resolution = tbt.PresetResolution.HIGH 

565 >>> success = beam_scan_resolution(beam, microscope, resolution) 

566 >>> print(success) 

567 True 

568 """ 

569 if not isinstance(resolution, tbt.PresetResolution): 

570 raise ValueError( 

571 f"Requested a custom resolution of {resolution.value}. Only preset resolutions allowed." 

572 ) 

573 

574 selected_beam = ut.beam_type(beam, microscope) 

575 selected_beam.scanning.resolution.value = resolution.value 

576 time.sleep(delay_s) 

577 if selected_beam.scanning.resolution.value != resolution.value: 

578 raise ValueError( 

579 f"""Could not correctly adjust scan resolution, 

580 requested {resolution}, current resolution is 

581 {selected_beam.scanning.resolution.value}""" 

582 ) 

583 

584 return True 

585 

586 

587def beam_scan_rotation( 

588 beam: tbt.Beam, 

589 microscope: tbt.Microscope, 

590 rotation_deg: float, 

591 delay_s: float = 0.1, 

592) -> bool: 

593 """ 

594 Sets the scan rotation for the selected beam, with inputs in units of degrees. 

595 

596 This function sets the scan rotation for the specified beam type on the microscope. 

597 It converts the rotation from degrees to radians, sets the scan rotation, and waits for the specified delay. 

598 

599 Parameters 

600 ---------- 

601 beam : tbt.Beam 

602 The beam type to configure. 

603 microscope : tbt.Microscope 

604 The microscope object to configure. 

605 rotation_deg : float 

606 The desired scan rotation in degrees. 

607 delay_s : float, optional 

608 The delay in seconds to wait after adjusting the scan rotation (default is 0.1). 

609 

610 Returns 

611 ------- 

612 bool 

613 True if the scan rotation is set successfully, False otherwise. 

614 

615 Raises 

616 ------ 

617 ValueError 

618 If the scan rotation cannot be adjusted correctly. 

619 

620 Examples 

621 -------- 

622 >>> import pytribeam.types as tbt 

623 >>> microscope = tbt.Microscope() 

624 >>> microscope.connect("localhost") 

625 Client connecting to [localhost:7520]... 

626 Client connected to [localhost:7520] 

627 >>> beam = tbt.ElectronBeam(settings=None) 

628 >>> success = beam_scan_rotation(beam, microscope, rotation_deg=45.0) 

629 >>> print(success) 

630 True 

631 """ 

632 selected_beam = ut.beam_type(beam, microscope) 

633 rotation_rad = rotation_deg * cs.Conversions.DEG_TO_RAD 

634 selected_beam.scanning.rotation.value = rotation_rad 

635 time.sleep(delay_s) 

636 if selected_beam.scanning.rotation.value != rotation_rad: 

637 raise ValueError( 

638 f"""Could not correctly adjust scan rotation, 

639 requested {rotation_rad} radians, current scan rotation is 

640 {selected_beam.scanning.rotation.value} radians""" 

641 ) 

642 

643 return True 

644 

645 

646def beam_voltage( 

647 beam: tbt.Beam, 

648 microscope: tbt.Microscope, 

649 voltage_kv: float, 

650 voltage_tol_kv: float, 

651 delay_s: float = 5.0, 

652) -> bool: 

653 """ 

654 Sets the voltage for a given beam type, with inputs in units of kilovolts. 

655 

656 This function sets the beam voltage for the specified beam type on the microscope. 

657 If the voltage difference exceeds the tolerance, it adjusts the beam voltage and waits for the specified delay. 

658 

659 Parameters 

660 ---------- 

661 beam : tbt.Beam 

662 The beam type to configure. 

663 microscope : tbt.Microscope 

664 The microscope object to configure. 

665 voltage_kv : float 

666 The desired beam voltage in kilovolts. 

667 voltage_tol_kv : float 

668 The tolerance for the beam voltage in kilovolts. 

669 delay_s : float, optional 

670 The delay in seconds to wait after adjusting the beam voltage (default is 5.0). 

671 

672 Returns 

673 ------- 

674 bool 

675 True if the beam voltage is set successfully, False otherwise. 

676 

677 Raises 

678 ------ 

679 ValueError 

680 If the beam voltage cannot be adjusted within the specified tolerance. 

681 If the beam is not controllable. 

682 

683 Examples 

684 -------- 

685 >>> import pytribeam.types as tbt 

686 >>> microscope = tbt.Microscope() 

687 >>> microscope.connect("localhost") 

688 Client connecting to [localhost:7520]... 

689 Client connected to [localhost:7520] 

690 >>> beam = tbt.ElectronBeam(settings=None) 

691 >>> success = beam_voltage(beam, microscope, voltage_kv=15.0, voltage_tol_kv=0.5) 

692 >>> print(success) 

693 True 

694 """ 

695 selected_beam = ut.beam_type(beam, microscope) 

696 

697 exisiting_voltage_v = selected_beam.high_voltage.value # volts 

698 delta_voltage_kv = abs( 

699 exisiting_voltage_v * cs.Conversions.V_TO_KV - voltage_kv 

700 ) # kilovolts 

701 if delta_voltage_kv > voltage_tol_kv: 

702 warnings.warn( 

703 "Requested beam current is not the current setting, imaging conditions may be non-ideal." 

704 ) 

705 beam_controllable = selected_beam.high_voltage.is_controllable 

706 

707 if not beam_controllable: 

708 raise ValueError( 

709 f"Unable to modify beam voltage, beam is not currently controllable." 

710 ) 

711 print("Adjusting beam voltage...") 

712 selected_beam.high_voltage.value = voltage_kv * cs.Conversions.KV_TO_V 

713 time.sleep(delay_s) 

714 new_voltage_kv = selected_beam.high_voltage.value * cs.Conversions.V_TO_KV 

715 voltage_diff_kv = abs(new_voltage_kv - voltage_kv) 

716 if voltage_diff_kv > voltage_tol_kv: 

717 raise ValueError( 

718 f"""Could not correctly adjust beam voltage, 

719 requested {voltage_kv} kV, current beam voltage is 

720 {new_voltage_kv} kV""" 

721 ) 

722 

723 return True 

724 

725 

726def beam_working_distance( 

727 beam: tbt.Beam, 

728 microscope: tbt.Microscope, 

729 wd_mm: float, 

730 delay_s: float = 0.1, 

731) -> bool: 

732 """ 

733 Sets the working distance for the selected beam, with inputs in units of millimeters. 

734 

735 This function sets the working distance (WD) for the specified beam type on the microscope. 

736 It converts the WD from millimeters to meters, sets the WD, and waits for the specified delay. 

737 This should be done before adjusting the horizontal field width. 

738 

739 Parameters 

740 ---------- 

741 beam : tbt.Beam 

742 The beam type to configure. 

743 microscope : tbt.Microscope 

744 The microscope object to configure. 

745 wd_mm : float 

746 The desired working distance in millimeters. 

747 delay_s : float, optional 

748 The delay in seconds to wait after adjusting the working distance (default is 0.1). 

749 

750 Returns 

751 ------- 

752 bool 

753 True if the working distance is set successfully, False otherwise. 

754 

755 Raises 

756 ------ 

757 ValueError 

758 If the working distance cannot be adjusted correctly. 

759 

760 Examples 

761 -------- 

762 >>> import pytribeam.types as tbt 

763 >>> microscope = tbt.Microscope() 

764 >>> microscope.connect("localhost") 

765 Client connecting to [localhost:7520]... 

766 Client connected to [localhost:7520] 

767 >>> beam = tbt.ElectronBeam(settings=None) 

768 >>> success = beam_working_dist(beam, microscope, wd_mm=5.0) 

769 >>> print(success) 

770 True 

771 """ 

772 selected_beam = ut.beam_type(beam, microscope) 

773 wd_m = wd_mm * cs.Conversions.MM_TO_M 

774 selected_beam.working_distance.value = wd_m 

775 time.sleep(delay_s) 

776 if selected_beam.working_distance.value != wd_m: 

777 raise ValueError( 

778 f"""Could not correctly adjust working distance, 

779 requested {wd_m} meters, current working distance is 

780 {selected_beam.working_distance.value} meters""" 

781 ) 

782 

783 return True 

784 

785 

786def detector_auto_cb( 

787 microscope: tbt.Microscope, 

788 beam: tbt.Beam, 

789 settings: tbt.ScanArea, 

790 delay_s: float = 0.1, 

791) -> bool: 

792 """ 

793 Detector auto contrast brightness. Currently only reduced scan area option is supported. 

794 

795 This function sets the scanning mode to reduced area, runs the auto contrast-brightness function, 

796 and then sets the scanning mode back to full frame. 

797 

798 Parameters 

799 ---------- 

800 microscope : tbt.Microscope 

801 The microscope object to configure. 

802 beam : tbt.Beam 

803 The beam type to configure. 

804 settings : tbt.ScanArea 

805 The scan area settings for the reduced area. 

806 delay_s : float, optional 

807 The delay in seconds to wait after each operation (default is 0.1). 

808 

809 Returns 

810 ------- 

811 bool 

812 True if the auto contrast-brightness is completed successfully, False otherwise. 

813 

814 Raises 

815 ------ 

816 SystemError 

817 If unable to set the scan mode to reduced area or full frame. 

818 

819 Examples 

820 -------- 

821 >>> import pytribeam.types as tbt 

822 >>> microscope = tbt.Microscope() 

823 >>> microscope.connect("localhost") 

824 Client connecting to [localhost:7520]... 

825 Client connected to [localhost:7520] 

826 >>> beam = tbt.ElectronBeam(settings=None) 

827 >>> auto_cb_settings = tbt.ScanArea(left=0.1, top=0.1, width=0.8, height=0.8) 

828 >>> success = detector_auto_cb(microscope, beam, auto_cb_settings) 

829 >>> print(success) 

830 True 

831 """ 

832 selected_beam = ut.beam_type(beam, microscope) 

833 selected_beam.scanning.mode.set_reduced_area( 

834 left=settings.left, 

835 top=settings.top, 

836 width=settings.width, 

837 height=settings.height, 

838 ) 

839 scan_mode = selected_beam.scanning.mode.value 

840 if not scan_mode == tbt.ScanMode.REDUCED_AREA.value: 

841 raise SystemError( 

842 f"Unable to set imaging to reduced area before auto contrast-brightness. Current scan mode is {scan_mode}." 

843 ) 

844 microscope.auto_functions.run_auto_cb() 

845 time.sleep(delay_s) 

846 selected_beam.scanning.mode.set_full_frame() 

847 time.sleep(delay_s) 

848 

849 new_scan_mode = selected_beam.scanning.mode.value 

850 if new_scan_mode != tbt.ScanMode.FULL_FRAME.value: 

851 raise SystemError( 

852 f"Unable to set imaging back to full frame after auto contrast-brightness. Current scan mode is {new_scan_mode}." 

853 ) 

854 return True 

855 

856 

857def detector_brightness( 

858 microscope: tbt.Microscope, 

859 brightness: float, 

860 delay_s: float = 0.1, 

861) -> bool: 

862 """ 

863 Sets the detector brightness with input from 0 to 1. 

864 

865 This function sets the brightness for the detector on the microscope and waits for the specified delay. 

866 

867 Parameters 

868 ---------- 

869 microscope : tbt.Microscope 

870 The microscope object to configure. 

871 brightness : float 

872 The desired brightness value (from 0 to 1). 

873 delay_s : float, optional 

874 The delay in seconds to wait after adjusting the brightness (default is 0.1). 

875 

876 Returns 

877 ------- 

878 bool 

879 True if the brightness is set successfully, False otherwise. 

880 

881 Raises 

882 ------ 

883 ValueError 

884 If the brightness cannot be adjusted correctly. 

885 

886 Examples 

887 -------- 

888 >>> import pytribeam.types as tbt 

889 >>> microscope = tbt.Microscope() 

890 >>> microscope.connect("localhost") 

891 Client connecting to [localhost:7520]... 

892 Client connected to [localhost:7520] 

893 >>> success = detector_brightness(microscope, brightness=0.5) 

894 >>> print(success) 

895 True 

896 """ 

897 microscope.detector.brightness.value = brightness 

898 current_detector = microscope.detector.type.value 

899 time.sleep(delay_s) 

900 if not math.isclose( 

901 microscope.detector.brightness.value, 

902 brightness, 

903 abs_tol=cs.Constants.contrast_brightness_tolerance, 

904 ): 

905 raise ValueError( 

906 f"""Could not correctly adjust detector brightness, 

907 requested {brightness} on {current_detector} detector,  

908 current brightness is {microscope.detector.brightness.value}""" 

909 ) 

910 return True 

911 

912 

913def detector_contrast( 

914 microscope: tbt.Microscope, 

915 contrast: float, 

916 delay_s: float = 0.1, 

917) -> bool: 

918 """ 

919 Sets the detector contrast with input from 0 to 1. 

920 

921 This function sets the contrast for the detector on the microscope and waits for the specified delay. 

922 

923 Parameters 

924 ---------- 

925 microscope : tbt.Microscope 

926 The microscope object to configure. 

927 contrast : float 

928 The desired contrast value (from 0 to 1). 

929 delay_s : float, optional 

930 The delay in seconds to wait after adjusting the contrast (default is 0.1). 

931 

932 Returns 

933 ------- 

934 bool 

935 True if the contrast is set successfully, False otherwise. 

936 

937 Raises 

938 ------ 

939 ValueError 

940 If the contrast cannot be adjusted correctly. 

941 

942 Examples 

943 -------- 

944 >>> import pytribeam.types as tbt 

945 >>> microscope = tbt.Microscope() 

946 >>> microscope.connect("localhost") 

947 Client connecting to [localhost:7520]... 

948 Client connected to [localhost:7520] 

949 >>> success = detector_contrast(microscope, contrast=0.5) 

950 >>> print(success) 

951 True 

952 """ 

953 microscope.detector.contrast.value = contrast 

954 current_detector = microscope.detector.type.value 

955 time.sleep(delay_s) 

956 if not math.isclose( 

957 microscope.detector.contrast.value, 

958 contrast, 

959 abs_tol=cs.Constants.contrast_brightness_tolerance, 

960 ): 

961 raise ValueError( 

962 f"""Could not correctly adjust detector contrast, 

963 requested {contrast} on {current_detector} detector,  

964 current contrast is {microscope.detector.contrast.value}""" 

965 ) 

966 return True 

967 

968 

969def detector_cb( 

970 microscope: tbt.Microscope, 

971 detector_settings: tbt.Detector, 

972 beam: tbt.Beam, 

973) -> bool: 

974 """ 

975 Sets detector contrast and brightness. 

976 

977 This function sets the contrast and brightness for the detector on the microscope. 

978 It also runs the auto contrast-brightness function if specified in the detector settings. 

979 Supports initial settings of contrast and brightness with fixed values before auto adjustment. 

980 

981 Parameters 

982 ---------- 

983 microscope : tbt.Microscope 

984 The microscope object to configure. 

985 detector_settings : tbt.Detector 

986 The detector settings, including contrast, brightness, and auto contrast-brightness settings. 

987 beam : tbt.Beam 

988 The beam type to configure. 

989 

990 Returns 

991 ------- 

992 bool 

993 True if the contrast and brightness are set successfully, False otherwise. 

994 

995 Examples 

996 -------- 

997 >>> import pytribeam.types as tbt 

998 >>> microscope = tbt.Microscope() 

999 >>> microscope.connect("localhost") 

1000 Client connecting to [localhost:7520]... 

1001 Client connected to [localhost:7520] 

1002 >>> beam = tbt.ElectronBeam(settings=None) 

1003 >>> detector_settings = tbt.Detector(contrast=None, brightness=None, auto_cb_settings=tbt.ScanArea(left=0.1, top=0.1, width=0.8, height=0.8)) 

1004 >>> success = detector_cb(microscope, detector_settings, beam) 

1005 >>> print(success) 

1006 True 

1007 

1008 >>> detector_settings = tbt.Detector(contrast=0.2, brightness=0.3, auto_cb_settings=None) 

1009 >>> success = detector_cb(microscope, detector_settings, beam) 

1010 >>> print(success) 

1011 True 

1012 >>> beam_brightness = microscope.detector.brightness.value 

1013 >>> print(beam_brightness) 

1014 0.3 

1015 >>> beam_contrast = microscope.detector.contrast.value 

1016 >>> print(beam_contrast) 

1017 0.2 

1018 """ 

1019 ### cannot ensure detector is the active one, will overwrite mode settings, so following line is not used 

1020 # microscope.detector.type.value = detector_settings.type.value 

1021 contrast = detector_settings.contrast 

1022 brightness = detector_settings.brightness 

1023 if contrast is not None: 

1024 detector_contrast(microscope=microscope, contrast=contrast) 

1025 if brightness is not None: 

1026 detector_brightness(microscope=microscope, brightness=brightness) 

1027 

1028 null_scan = tbt.ScanArea(left=None, top=None, width=None, height=None) 

1029 if not null_scan == detector_settings.auto_cb_settings: 

1030 detector_auto_cb( 

1031 microscope=microscope, 

1032 settings=detector_settings.auto_cb_settings, 

1033 beam=beam, 

1034 ) 

1035 return True 

1036 

1037 

1038def detector_mode( 

1039 microscope: tbt.Microscope, 

1040 detector_mode: tbt.DetectorMode, 

1041 delay_s: float = 0.1, 

1042) -> bool: 

1043 """ 

1044 Sets the detector mode. 

1045 

1046 This function sets the mode for the detector on the microscope and waits for the specified delay. 

1047 

1048 Parameters 

1049 ---------- 

1050 microscope : tbt.Microscope 

1051 The microscope object to configure. 

1052 detector_mode : tbt.DetectorMode 

1053 The desired detector mode. 

1054 delay_s : float, optional 

1055 The delay in seconds to wait after setting the detector mode (default is 0.1). 

1056 

1057 Returns 

1058 ------- 

1059 bool 

1060 True if the detector mode is set successfully, False otherwise. 

1061 

1062 Raises 

1063 ------ 

1064 ValueError 

1065 If the detector mode cannot be set correctly. 

1066 

1067 Examples 

1068 -------- 

1069 >>> import pytribeam.types as tbt 

1070 >>> microscope = tbt.Microscope() 

1071 >>> microscope.connect("localhost") 

1072 Client connecting to [localhost:7520]... 

1073 Client connected to [localhost:7520] 

1074 >>> mode = tbt.DetectorMode.SECONDARY_ELECTRONS 

1075 >>> success = detector_mode(microscope, mode) 

1076 >>> print(success) 

1077 True 

1078 """ 

1079 microscope.detector.mode.value = detector_mode.value 

1080 time.sleep(delay_s) 

1081 if microscope.detector.mode.value != detector_mode.value: 

1082 raise ValueError( 

1083 f"""Could not correctly set detector mode, 

1084 requested {detector_mode}, current mode is 

1085 {microscope.detector.mode.value}""" 

1086 ) 

1087 return True 

1088 

1089 

1090def detector_type( 

1091 microscope: tbt.Microscope, 

1092 detector: tbt.DetectorType, 

1093 delay_s: float = 0.1, 

1094) -> bool: 

1095 """ 

1096 Sets the detector type. 

1097 

1098 This function sets the type for the detector on the microscope and waits for the specified delay. 

1099 

1100 Parameters 

1101 ---------- 

1102 microscope : tbt.Microscope 

1103 The microscope object to configure. 

1104 detector : tbt.DetectorType 

1105 The desired detector type. 

1106 delay_s : float, optional 

1107 The delay in seconds to wait after setting the detector type (default is 0.1). 

1108 

1109 Returns 

1110 ------- 

1111 bool 

1112 True if the detector type is set successfully, False otherwise. 

1113 

1114 Raises 

1115 ------ 

1116 ValueError 

1117 If the detector type cannot be set correctly. 

1118 

1119 Examples 

1120 -------- 

1121 >>> import pytribeam.types as tbt 

1122 >>> microscope = tbt.Microscope() 

1123 >>> microscope.connect("localhost") 

1124 Client connecting to [localhost:7520]... 

1125 Client connected to [localhost:7520] 

1126 >>> detector = tbt.DetectorType.ETD 

1127 >>> success = detector_type(microscope, detector) 

1128 >>> print(success) 

1129 True 

1130 """ 

1131 microscope.detector.type.value = detector.value 

1132 time.sleep(delay_s) 

1133 if microscope.detector.type.value != detector.value: 

1134 raise ValueError( 

1135 f"""Could not correctly set detector type, 

1136 requested {detector}, current detector is 

1137 {microscope.detector.type.value}""" 

1138 ) 

1139 return True 

1140 

1141 

1142def grab_custom_resolution_frame( 

1143 img_settings: tbt.ImageSettings, 

1144 save_path: Path, 

1145) -> bool: 

1146 """ 

1147 Method for single frame imaging used with custom resolutions. 

1148 

1149 This function captures a single frame image using custom resolutions and saves it to the specified path. 

1150 

1151 Parameters 

1152 ---------- 

1153 img_settings : tbt.ImageSettings 

1154 The image settings, including the microscope and scan resolution. 

1155 save_path : Path 

1156 The path to save the captured image. 

1157 

1158 Returns 

1159 ------- 

1160 bool 

1161 True if the image is captured and saved successfully, False otherwise. 

1162 

1163 Examples 

1164 -------- 

1165 >>> import pytribeam.types as tbt 

1166 >>> from pathlib import Path 

1167 >>> microscope = tbt.Microscope() 

1168 >>> microscope.connect("localhost") 

1169 Client connecting to [localhost:7520]... 

1170 Client connected to [localhost:7520] 

1171 >>> img_settings = tbt.ImageSettings( 

1172 ... microscope=microscope, 

1173 ... beam=tbt.ElectronBeam(settings=None), 

1174 ... detector=tbt.Detector(), 

1175 ... scan=tbt.Scan(resolution=tbt.Resolution(width=1024, height=768)), 

1176 ... bit_depth=tbt.ColorDepth.BITS_8, 

1177 ... ) 

1178 >>> save_path = Path("/path/to/save/image.tif") 

1179 >>> success = grab_custom_resolution_frame(img_settings, save_path) 

1180 >>> print(success) 

1181 True 

1182 """ 

1183 microscope = img_settings.microscope 

1184 resolution = img_settings.scan.resolution 

1185 microscope.imaging.grab_frame_to_disk( 

1186 save_path.absolute().as_posix(), 

1187 file_format=tbt.ImageFileFormat.TIFF.value, 

1188 settings=tbt.GrabFrameSettings( 

1189 resolution=f"{resolution.width}x{resolution.height}" 

1190 ), 

1191 ) 

1192 return True 

1193 

1194 

1195def grab_preset_resolution_frame( 

1196 img_settings: tbt.ImageSettings, 

1197) -> tbt.AdornedImage: 

1198 """ 

1199 Method for single frame imaging used with preset resolutions. 

1200 

1201 This function captures a single frame image using preset resolutions. 

1202 

1203 Parameters 

1204 ---------- 

1205 img_settings : tbt.ImageSettings 

1206 The image settings, including the microscope, beam, detector, and scan resolution. 

1207 

1208 Returns 

1209 ------- 

1210 tbt.AdornedImage 

1211 The captured image. 

1212 

1213 Examples 

1214 -------- 

1215 >>> import pytribeam.types as tbt 

1216 >>> microscope = tbt.Microscope() 

1217 >>> microscope.connect("localhost") 

1218 Client connecting to [localhost:7520]... 

1219 Client connected to [localhost:7520] 

1220 >>> img_settings = tbt.ImageSettings( 

1221 ... microscope=microscope, 

1222 ... beam=tbt.ElectronBeam(settings=None), 

1223 ... detector=tbt.Detector(), 

1224 ... scan=tbt.Scan(resolution=tbt.PresetResolution.PRESET_768X512), 

1225 ... bit_depth=tbt.ColorDepth.BITS_8, 

1226 ... ) 

1227 >>> image = grab_preset_resolution_frame(img_settings) 

1228 >>> print(image) 

1229 AdornedImage(width=768, height=512, bit_depth=8) 

1230 """ 

1231 beam = img_settings.beam 

1232 microscope = img_settings.microscope 

1233 beam_scan_resolution( 

1234 beam=beam, 

1235 microscope=microscope, 

1236 resolution=img_settings.scan.resolution, 

1237 ) 

1238 return microscope.imaging.grab_frame( 

1239 tbt.GrabFrameSettings(bit_depth=img_settings.bit_depth) 

1240 ) 

1241 

1242 

1243def imaging_detector(img_settings: tbt.ImageSettings) -> bool: 

1244 """ 

1245 Prepares the detector and inserts it if applicable. 

1246 

1247 This function sets the detector type, inserts the detector if necessary, sets the detector mode, 

1248 and adjusts the contrast and brightness settings. It is important to set detector mode settings 

1249 right before contrast and brightness as any subsequent calls to a detector type can overwrite the mode. 

1250 

1251 Parameters 

1252 ---------- 

1253 img_settings : tbt.ImageSettings 

1254 The image settings, including the microscope, beam, and detector settings. 

1255 

1256 Returns 

1257 ------- 

1258 bool 

1259 True if the detector is prepared successfully, False otherwise. 

1260 

1261 Examples 

1262 -------- 

1263 >>> import pytribeam.types as tbt 

1264 >>> microscope = tbt.Microscope() 

1265 >>> microscope.connect("localhost") 

1266 Client connecting to [localhost:7520]... 

1267 Client connected to [localhost:7520] 

1268 >>> img_settings = tbt.ImageSettings( 

1269 ... microscope=microscope, 

1270 ... beam=tbt.ElectronBeam(settings=None), 

1271 ... detector=tbt.Detector( 

1272 ... type=tbt.DetectorType.ETD, 

1273 ... mode=tbt.DetectorMode.SECONDARY_ELECTRONS, 

1274 ... brightness=0.4, 

1275 ... contrast=0.2, 

1276 ... ), 

1277 ... scan=tbt.Scan(resolution=tbt.PresetResolution.PRESET_768X512), 

1278 ... bit_depth=tbt.ColorDepth.BITS_8, 

1279 ... ) 

1280 >>> success = imaging_detector(img_settings) 

1281 >>> print(success) 

1282 True 

1283 """ 

1284 microscope = img_settings.microscope 

1285 detector = img_settings.detector.type 

1286 detector_type( 

1287 microscope=microscope, 

1288 detector=detector, 

1289 ) 

1290 detector_state = devices.detector_state( 

1291 microscope=microscope, 

1292 detector=detector, 

1293 ) 

1294 if detector_state is not None: 

1295 devices.insert_detector( 

1296 microscope=microscope, 

1297 detector=detector, 

1298 ) 

1299 detector_mode( 

1300 microscope=microscope, 

1301 detector_mode=img_settings.detector.mode, 

1302 ) 

1303 detector_cb( 

1304 microscope=microscope, 

1305 detector_settings=img_settings.detector, 

1306 beam=img_settings.beam, 

1307 ) 

1308 return True 

1309 

1310 

1311def imaging_device( 

1312 microscope: tbt.Microscope, 

1313 beam: tbt.Beam, 

1314) -> bool: 

1315 """ 

1316 Prepares the imaging beam, viewing quad, and the beam voltage and current. 

1317 

1318 This function sets the beam device, ensures the beam is ready, sets the beam voltage and current, 

1319 and applies angular correction if the beam type is electron. 

1320 

1321 Parameters 

1322 ---------- 

1323 microscope : tbt.Microscope 

1324 The microscope object to configure. 

1325 beam : tbt.Beam 

1326 The beam type to configure, including its settings. 

1327 

1328 Returns 

1329 ------- 

1330 bool 

1331 True if the imaging device is prepared successfully, False otherwise. 

1332 """ 

1333 set_beam_device(microscope=microscope, device=beam.device) 

1334 beam_ready(beam=beam, microscope=microscope) 

1335 beam_voltage( 

1336 beam=beam, 

1337 microscope=microscope, 

1338 voltage_kv=beam.settings.voltage_kv, 

1339 voltage_tol_kv=beam.settings.voltage_tol_kv, 

1340 ) 

1341 beam_current( 

1342 beam=beam, 

1343 microscope=microscope, 

1344 current_na=beam.settings.current_na, 

1345 current_tol_na=beam.settings.current_tol_na, 

1346 ) 

1347 if beam.type == tbt.BeamType.ELECTRON: 

1348 beam_angular_correction( 

1349 microscope=microscope, 

1350 dynamic_focus=beam.settings.dynamic_focus, 

1351 tilt_correction=beam.settings.tilt_correction, 

1352 ) 

1353 return True 

1354 

1355 

1356def imaging_scan(img_settings: tbt.ImageSettings) -> bool: 

1357 """ 

1358 Sets all scan settings except for the resolution. 

1359 

1360 This function configures the scan settings for the specified image settings, including 

1361 setting the scan mode to full frame, scan rotation, working distance, horizontal field width, 

1362 and dwell time. 

1363 

1364 Parameters 

1365 ---------- 

1366 img_settings : tbt.ImageSettings 

1367 The image settings, including the microscope, beam, and scan settings. 

1368 

1369 Returns 

1370 ------- 

1371 bool 

1372 True if the scan settings are configured successfully, False otherwise. 

1373 """ 

1374 microscope = img_settings.microscope 

1375 beam = img_settings.beam 

1376 beam_scan_full_frame( 

1377 beam=beam, 

1378 microscope=microscope, 

1379 ) 

1380 beam_scan_rotation( 

1381 beam=beam, microscope=microscope, rotation_deg=img_settings.scan.rotation_deg 

1382 ) 

1383 beam_working_distance( 

1384 beam=beam, 

1385 microscope=microscope, 

1386 wd_mm=img_settings.beam.settings.working_dist_mm, 

1387 ) 

1388 beam_hfw(beam=beam, microscope=microscope, hfw_mm=img_settings.beam.settings.hfw_mm) 

1389 beam_dwell_time( 

1390 beam=beam, microscope=microscope, dwell_us=img_settings.scan.dwell_time_us 

1391 ) 

1392 return True 

1393 

1394 

1395def prepare_imaging(img_settings: tbt.ImageSettings) -> bool: 

1396 """ 

1397 Prepares various imaging settings. 

1398 

1399 This function prepares the imaging device, scan settings, and detector settings 

1400 based on the specified image settings. 

1401 

1402 Parameters 

1403 ---------- 

1404 img_settings : tbt.ImageSettings 

1405 The image settings, including the microscope, beam, scan, and detector settings. 

1406 

1407 Returns 

1408 ------- 

1409 bool 

1410 True if the imaging settings are prepared successfully, False otherwise. 

1411 """ 

1412 imaging_device(microscope=img_settings.microscope, beam=img_settings.beam) 

1413 imaging_scan(img_settings=img_settings) 

1414 imaging_detector(img_settings=img_settings) 

1415 return True 

1416 

1417 

1418def set_view( 

1419 microscope: tbt.Microscope, 

1420 quad: tbt.ViewQuad, 

1421) -> bool: 

1422 """ 

1423 Sets the active view to the specified quad. 

1424 

1425 This function sets the active imaging view to the specified quad on the microscope. 

1426 

1427 Parameters 

1428 ---------- 

1429 microscope : tbt.Microscope 

1430 The microscope object to configure. 

1431 quad : tbt.ViewQuad 

1432 The imaging view to select: 

1433 - 1 is upper left 

1434 - 2 is upper right 

1435 - 3 is lower left 

1436 - 4 is lower right 

1437 

1438 Returns 

1439 ------- 

1440 bool 

1441 True if the active view is set successfully, False otherwise. 

1442 """ 

1443 microscope.imaging.set_active_view(quad.value) 

1444 

1445 

1446def set_beam_device( 

1447 microscope: tbt.Microscope, 

1448 device: tbt.Device, 

1449 delay_s: float = 0.1, 

1450) -> bool: 

1451 """ 

1452 Sets the active imaging device. 

1453 

1454 This function sets the active imaging device on the microscope and waits for the specified delay. 

1455 

1456 Parameters 

1457 ---------- 

1458 microscope : tbt.Microscope 

1459 The microscope object to configure. 

1460 device : tbt.Device 

1461 The desired imaging device. 

1462 delay_s : float, optional 

1463 The delay in seconds to wait after setting the device (default is 0.1). 

1464 

1465 Returns 

1466 ------- 

1467 bool 

1468 True if the active device is set successfully, False otherwise. 

1469 

1470 Raises 

1471 ------ 

1472 ValueError 

1473 If the active device cannot be set correctly. 

1474 """ 

1475 microscope.imaging.set_active_device(device) 

1476 time.sleep(delay_s) 

1477 curr_device = tbt.Device(microscope.imaging.get_active_device()) 

1478 if curr_device != device.value: 

1479 raise ValueError( 

1480 f"""Could not set active device, 

1481 requested device {device.value} but current device is {curr_device}. 

1482 Device list: 

1483 {tbt.Device.ELECTRON_BEAM.value}: Electron Beam 

1484 {tbt.Device.ION_BEAM.value}: Ion Beam 

1485 {tbt.Device.CCD_CAMERA.value}: CCD Camera 

1486 {tbt.Device.IR_CAMERA.value}: IR Camera 

1487 {tbt.Device.NAV_CAM.value}: Nav Cam""" 

1488 ) 

1489 return True 

1490 

1491 

1492################# 

1493 

1494 

1495def collect_single_image( 

1496 save_path: Path, 

1497 img_settings: tbt.ImageSettings, 

1498) -> bool: 

1499 """ 

1500 Collects a single frame image with defined image settings. 

1501 

1502 This function prepares the imaging settings, sets the view, and captures a single frame image. 

1503 It saves the image to the specified path. If a non-preset resolution is requested, the image 

1504 will be saved at 8-bit color depth. 

1505 

1506 Parameters 

1507 ---------- 

1508 save_path : Path 

1509 The path to save the captured image. 

1510 img_settings : tbt.ImageSettings 

1511 The image settings, including the microscope, beam, scan, and detector settings. 

1512 

1513 Returns 

1514 ------- 

1515 bool 

1516 True if the image is captured and saved successfully, False otherwise. 

1517 """ 

1518 beam = img_settings.beam 

1519 microscope = img_settings.microscope 

1520 set_view(microscope=microscope, quad=beam.default_view) 

1521 prepare_imaging(img_settings=img_settings) 

1522 

1523 resolution = img_settings.scan.resolution 

1524 if isinstance(resolution, tbt.PresetResolution): 

1525 img = grab_preset_resolution_frame(img_settings=img_settings) 

1526 img.save(str(save_path)) 

1527 return True 

1528 warnings.warn( 

1529 f'Warning, non-preset resolution of "{img_settings.scan.resolution}" requested. Image will automatically be saved at 8-bit color depth' 

1530 ) 

1531 if img_settings.bit_depth != tbt.ColorDepth.BITS_8: 

1532 warnings.warn( 

1533 f"Warning, non-preset resolution necessitates images be stored at 8-bit color depth, requested bit-depth of {img_settings.bit_depth.value} will be ignored." 

1534 ) 

1535 grab_custom_resolution_frame( 

1536 img_settings=img_settings, 

1537 save_path=save_path, 

1538 ) 

1539 return True 

1540 

1541 

1542def collect_multiple_images( 

1543 multiple_img_settings: List[tbt.ImageSettings], num_frames: int 

1544) -> List[tbt.AdornedImage]: 

1545 """ 

1546 Sets up scanning for multiple frames. 

1547 

1548 This function is best used for collecting multiple segments on a single detector simultaneously. 

1549 It is limited to preset resolutions only. 

1550 

1551 Parameters 

1552 ---------- 

1553 multiple_img_settings : List[tbt.ImageSettings] 

1554 The list of image settings for each frame, including the microscope, beam, scan, and detector settings. 

1555 num_frames : int 

1556 The number of frames to collect. 

1557 

1558 Returns 

1559 ------- 

1560 List[tbt.AdornedImage] 

1561 The list of captured images. 

1562 

1563 Raises 

1564 ------ 

1565 ValueError 

1566 If a non-preset resolution is requested for simultaneous multiple frame imaging. 

1567 

1568 Notes 

1569 ----- 

1570 This method has not yet been tested. 

1571 

1572 """ 

1573 # TODO test this 

1574 warnings.warn("Method not yet tested") 

1575 views = [] 

1576 for quad in range(1, num_frames + 1): 

1577 img_settings = multiple_img_settings[quad] 

1578 resolution = img_settings.scan.resolution 

1579 if not isinstance(resolution, tbt.PresetResolution): 

1580 raise ValueError( 

1581 f'Only preset resolutions allowed for simultaneous multiple frame imaging, but resolution of "{resolution.width}x{resolution.height}" was requested.' 

1582 ) 

1583 

1584 microscope = img_settings.microscope 

1585 beam = img_settings.beam 

1586 views.append(quad) 

1587 set_view(microscope=microscope, quad=quad) 

1588 prepare_imaging(microscope=microscope, beam=beam, img_settings=img_settings) 

1589 frames = microscope.imaging.grab_multiple_frames( 

1590 tbt.GrabFrameSettings(bit_depth=img_settings.bit_depth, views=views) 

1591 ) 

1592 return frames 

1593 

1594 

1595# TODO 

1596# def image_method(dict): 

1597# """determine imaging method and return associated function""" 

1598# pass 

1599 

1600# TODO 

1601# def collect_tiling_image(): 

1602# for loop 

1603# collect_standard_image 

1604# #stage move 

1605 

1606 

1607# TODO add more complex imaging behavior, determine method in this function 

1608def image_operation( 

1609 step: tbt.Step, 

1610 image_settings: tbt.ImageSettings, 

1611 general_settings: tbt.GeneralSettings, 

1612 slice_number: int, 

1613) -> bool: 

1614 """ 

1615 Performs an image operation based on the specified settings. 

1616 

1617 This function collects an image, saves it to the specified directory, and turns off tilt correction and dynamic focus. 

1618 

1619 Parameters 

1620 ---------- 

1621 step : tbt.Step 

1622 The step information, including the name of the step. 

1623 image_settings : tbt.ImageSettings 

1624 The image settings, including the microscope, beam, scan, and detector settings. 

1625 general_settings : tbt.GeneralSettings 

1626 The general settings, including the experimental directory. 

1627 slice_number : int 

1628 The slice number for naming the saved image file. 

1629 

1630 Returns 

1631 ------- 

1632 bool 

1633 True if the image operation is performed successfully, False otherwise. 

1634 """ 

1635 print("\tCollecting image") 

1636 # create folder in same directory as experimental directory 

1637 image_directory = Path(general_settings.exp_dir).joinpath(step.name) 

1638 image_directory.mkdir(parents=True, exist_ok=True) 

1639 

1640 # TODO 

1641 # determine_image_method() 

1642 # collect_multiple_images() 

1643 

1644 # single image process: 

1645 save_path = image_directory.joinpath(f"{slice_number:04}.tif") 

1646 collect_single_image(save_path=save_path, img_settings=image_settings) 

1647 print(f"\tImage saved to {save_path}") 

1648 

1649 # turn off tilt correction and dynamic focus 

1650 beam_angular_correction( 

1651 microscope=image_settings.microscope, 

1652 dynamic_focus=False, 

1653 tilt_correction=False, 

1654 ) 

1655 

1656 return True