dls-dodal 1.69.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/METADATA +1 -1
  2. dls_dodal-2.0.0.dist-info/RECORD +354 -0
  3. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/WHEEL +1 -1
  4. dodal/_version.py +2 -2
  5. dodal/beamlines/__init__.py +10 -17
  6. dodal/beamlines/adsim.py +17 -17
  7. dodal/beamlines/b01_1.py +11 -0
  8. dodal/beamlines/b07.py +17 -21
  9. dodal/beamlines/b07_1.py +20 -22
  10. dodal/beamlines/b07_shared.py +12 -0
  11. dodal/beamlines/b16.py +1 -1
  12. dodal/beamlines/b21.py +15 -6
  13. dodal/beamlines/i02_1.py +3 -3
  14. dodal/beamlines/i02_2.py +1 -1
  15. dodal/beamlines/i03.py +4 -4
  16. dodal/beamlines/i04.py +16 -8
  17. dodal/beamlines/i05.py +7 -45
  18. dodal/beamlines/i05_1.py +4 -13
  19. dodal/beamlines/i05_shared.py +51 -0
  20. dodal/beamlines/i06_1.py +7 -5
  21. dodal/beamlines/{i06.py → i06_shared.py} +25 -14
  22. dodal/beamlines/i07.py +14 -16
  23. dodal/beamlines/i09.py +54 -51
  24. dodal/beamlines/i09_1.py +25 -64
  25. dodal/beamlines/i09_1_shared.py +61 -0
  26. dodal/beamlines/i09_2.py +6 -100
  27. dodal/beamlines/i09_2_shared.py +110 -0
  28. dodal/beamlines/i10.py +60 -54
  29. dodal/beamlines/i10_1.py +99 -10
  30. dodal/beamlines/{i10_optics.py → i10_shared.py} +80 -66
  31. dodal/beamlines/i11.py +31 -18
  32. dodal/beamlines/i13_1.py +1 -1
  33. dodal/beamlines/i15.py +6 -6
  34. dodal/beamlines/i15_1.py +6 -6
  35. dodal/beamlines/i17.py +37 -28
  36. dodal/beamlines/i18.py +3 -4
  37. dodal/beamlines/i19_1.py +95 -34
  38. dodal/beamlines/i19_2.py +68 -52
  39. dodal/beamlines/i19_optics.py +26 -13
  40. dodal/beamlines/i20_1.py +6 -14
  41. dodal/beamlines/i21.py +35 -28
  42. dodal/beamlines/i22.py +19 -4
  43. dodal/beamlines/i23.py +1 -2
  44. dodal/beamlines/i24.py +11 -10
  45. dodal/beamlines/k07.py +99 -5
  46. dodal/beamlines/p38.py +3 -3
  47. dodal/beamlines/p60.py +28 -17
  48. dodal/beamlines/p99.py +16 -15
  49. dodal/beamlines/training_rig.py +20 -12
  50. dodal/cli.py +36 -2
  51. dodal/common/beamlines/beamline_parameters.py +2 -1
  52. dodal/common/beamlines/beamline_utils.py +11 -9
  53. dodal/common/beamlines/commissioning_mode.py +6 -3
  54. dodal/common/coordination.py +12 -14
  55. dodal/common/crystal_metadata.py +5 -8
  56. dodal/common/device_utils.py +4 -3
  57. dodal/common/maths.py +28 -40
  58. dodal/common/udc_directory_provider.py +13 -8
  59. dodal/common/visit.py +18 -21
  60. dodal/common/watcher_utils.py +13 -12
  61. dodal/device_manager.py +94 -54
  62. dodal/devices/aperturescatterguard.py +26 -27
  63. dodal/devices/areadetector/plugins/cam.py +1 -3
  64. dodal/devices/areadetector/plugins/mjpg.py +6 -5
  65. dodal/devices/attenuator/attenuator.py +12 -11
  66. dodal/devices/beamlines/b07/__init__.py +3 -0
  67. dodal/devices/{b07_1 → beamlines/b07_1}/__init__.py +2 -2
  68. dodal/devices/{b07_1 → beamlines/b07_1}/ccmc.py +5 -10
  69. dodal/devices/{b16 → beamlines/b16}/detector.py +2 -3
  70. dodal/devices/{i02_1 → beamlines/i02_1}/fast_grid_scan.py +2 -3
  71. dodal/devices/{i02_1 → beamlines/i02_1}/sample_motors.py +1 -1
  72. dodal/devices/{i03 → beamlines/i03}/beamsize.py +11 -7
  73. dodal/devices/{i03 → beamlines/i03}/dcm.py +1 -2
  74. dodal/devices/{i03 → beamlines/i03}/undulator_dcm.py +4 -5
  75. dodal/devices/beamlines/i04/beam_centre.py +151 -0
  76. dodal/devices/{i04 → beamlines/i04}/beamsize.py +11 -7
  77. dodal/devices/{i04 → beamlines/i04}/murko_results.py +5 -5
  78. dodal/devices/{i04 → beamlines/i04}/transfocator.py +10 -15
  79. dodal/devices/beamlines/i05/__init__.py +3 -0
  80. dodal/devices/beamlines/i06_shared/__init__.py +3 -0
  81. dodal/devices/beamlines/i06_shared/i06_enum.py +7 -0
  82. dodal/devices/{i07 → beamlines/i07}/dcm.py +2 -3
  83. dodal/devices/{i07 → beamlines/i07}/id.py +8 -9
  84. dodal/devices/beamlines/i09/__init__.py +3 -0
  85. dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_energy.py +5 -6
  86. dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/hard_undulator_functions.py +19 -16
  87. dodal/devices/{i10 → beamlines/i10}/diagnostics.py +4 -3
  88. dodal/devices/{i10 → beamlines/i10}/i10_apple2.py +31 -45
  89. dodal/devices/{i10 → beamlines/i10}/rasor/rasor_current_amp.py +1 -24
  90. dodal/devices/{i10 → beamlines/i10}/rasor/rasor_motors.py +2 -2
  91. dodal/devices/{i10 → beamlines/i10}/slits.py +5 -3
  92. dodal/devices/beamlines/i10_1/__init__.py +9 -0
  93. dodal/devices/beamlines/i10_1/electromagnet/magnet.py +16 -0
  94. dodal/devices/beamlines/i10_1/electromagnet/stages.py +14 -0
  95. dodal/devices/beamlines/i10_1/scaler_cards.py +13 -0
  96. dodal/devices/{i11 → beamlines/i11}/cyberstar_blower.py +1 -1
  97. dodal/devices/{i11 → beamlines/i11}/diff_stages.py +4 -6
  98. dodal/devices/{i11 → beamlines/i11}/mythen.py +3 -4
  99. dodal/devices/{i11 → beamlines/i11}/nx100robot.py +6 -6
  100. dodal/devices/{i11 → beamlines/i11}/spinner.py +1 -1
  101. dodal/devices/{i13_1 → beamlines/i13_1}/merlin.py +1 -1
  102. dodal/devices/{i15 → beamlines/i15}/dcm.py +1 -2
  103. dodal/devices/{i15 → beamlines/i15}/focussing_mirror.py +5 -5
  104. dodal/devices/{i15 → beamlines/i15}/jack.py +2 -2
  105. dodal/devices/{i15 → beamlines/i15}/multilayer_mirror.py +1 -1
  106. dodal/devices/{i17 → beamlines/i17}/i17_apple2.py +10 -16
  107. dodal/devices/{i18 → beamlines/i18}/diode.py +1 -1
  108. dodal/devices/{i19 → beamlines/i19}/access_controlled/attenuator_motor_squad.py +12 -8
  109. dodal/devices/{i19 → beamlines/i19}/access_controlled/blueapi_device.py +16 -15
  110. dodal/devices/beamlines/i19/access_controlled/piezo_control.py +72 -0
  111. dodal/devices/{i19 → beamlines/i19}/access_controlled/shutter.py +11 -9
  112. dodal/devices/{i19 → beamlines/i19}/backlight.py +3 -1
  113. dodal/devices/{i19 → beamlines/i19}/mapt_configuration.py +2 -1
  114. dodal/devices/{i19 → beamlines/i19}/pin_col_stages.py +11 -8
  115. dodal/devices/beamlines/i19/pin_tip.py +32 -0
  116. dodal/devices/beamlines/i21/__init__.py +3 -0
  117. dodal/devices/{i22 → beamlines/i22}/dcm.py +1 -2
  118. dodal/devices/{i22 → beamlines/i22}/fswitch.py +1 -3
  119. dodal/devices/{i22 → beamlines/i22}/nxsas.py +5 -4
  120. dodal/devices/{i24 → beamlines/i24}/beam_center.py +1 -1
  121. dodal/devices/{i24 → beamlines/i24}/beamstop.py +2 -2
  122. dodal/devices/{i24 → beamlines/i24}/commissioning_jungfrau.py +3 -2
  123. dodal/devices/{i24 → beamlines/i24}/dcm.py +1 -3
  124. dodal/devices/{i24 → beamlines/i24}/dual_backlight.py +3 -3
  125. dodal/devices/{i24 → beamlines/i24}/pmac.py +9 -7
  126. dodal/devices/{p60 → beamlines/p60}/lab_xray_source.py +1 -1
  127. dodal/devices/beamlines/p99/__init__.py +0 -0
  128. dodal/devices/{p99 → beamlines/p99}/andor2_point.py +11 -15
  129. dodal/devices/bimorph_mirror.py +22 -20
  130. dodal/devices/collimation_table.py +3 -2
  131. dodal/devices/common_dcm.py +30 -20
  132. dodal/devices/controllers.py +2 -2
  133. dodal/devices/cryostream.py +8 -0
  134. dodal/devices/current_amplifiers/current_amplifier.py +16 -18
  135. dodal/devices/current_amplifiers/current_amplifier_detector.py +9 -10
  136. dodal/devices/current_amplifiers/femto.py +8 -9
  137. dodal/devices/current_amplifiers/sr570.py +16 -16
  138. dodal/devices/current_amplifiers/struck_scaler_counter.py +5 -5
  139. dodal/devices/detector/det_resolution.py +9 -8
  140. dodal/devices/detector/detector.py +4 -2
  141. dodal/devices/diamond_filter.py +3 -4
  142. dodal/devices/eiger.py +3 -3
  143. dodal/devices/eiger_odin.py +1 -1
  144. dodal/devices/electron_analyser/base/base_controller.py +13 -13
  145. dodal/devices/electron_analyser/base/base_detector.py +15 -20
  146. dodal/devices/electron_analyser/base/base_driver_io.py +39 -46
  147. dodal/devices/electron_analyser/base/base_region.py +27 -30
  148. dodal/devices/electron_analyser/base/base_util.py +18 -16
  149. dodal/devices/electron_analyser/base/energy_sources.py +13 -19
  150. dodal/devices/eurotherm.py +3 -2
  151. dodal/devices/fast_grid_scan.py +31 -34
  152. dodal/devices/fast_shutter.py +24 -21
  153. dodal/devices/flux.py +1 -1
  154. dodal/devices/focusing_mirror.py +29 -11
  155. dodal/devices/hutch_shutter.py +6 -6
  156. dodal/devices/insertion_device/__init__.py +8 -0
  157. dodal/devices/insertion_device/apple2_controller.py +51 -60
  158. dodal/devices/insertion_device/apple2_undulator.py +40 -64
  159. dodal/devices/insertion_device/apple_knot_controller.py +222 -0
  160. dodal/devices/insertion_device/energy.py +100 -27
  161. dodal/devices/insertion_device/energy_motor_lookup.py +20 -27
  162. dodal/devices/insertion_device/lookup_table_models.py +45 -50
  163. dodal/devices/insertion_device/polarisation.py +1 -1
  164. dodal/devices/ipin.py +1 -1
  165. dodal/devices/linkam3.py +7 -5
  166. dodal/devices/motors.py +107 -19
  167. dodal/devices/mx_phase1/beamstop.py +2 -4
  168. dodal/devices/oav/oav_calculations.py +20 -13
  169. dodal/devices/oav/oav_detector.py +28 -23
  170. dodal/devices/oav/oav_parameters.py +4 -9
  171. dodal/devices/oav/oav_to_redis_forwarder.py +22 -18
  172. dodal/devices/oav/pin_image_recognition/__init__.py +4 -6
  173. dodal/devices/oav/pin_image_recognition/manual_test.py +1 -2
  174. dodal/devices/oav/pin_image_recognition/utils.py +30 -32
  175. dodal/devices/oav/snapshots/grid_overlay.py +10 -9
  176. dodal/devices/oav/snapshots/snapshot_image_processing.py +15 -13
  177. dodal/devices/oav/utils.py +9 -12
  178. dodal/devices/p45.py +3 -9
  179. dodal/devices/pgm.py +8 -14
  180. dodal/devices/pressure_jump_cell.py +93 -32
  181. dodal/devices/qbpm.py +1 -3
  182. dodal/devices/robot.py +12 -4
  183. dodal/devices/s4_slit_gaps.py +1 -1
  184. dodal/devices/selectable_source.py +5 -2
  185. dodal/devices/slits.py +2 -5
  186. dodal/devices/smargon.py +2 -3
  187. dodal/devices/temperture_controller/lakeshore/lakeshore.py +38 -64
  188. dodal/devices/temperture_controller/lakeshore/lakeshore_io.py +21 -35
  189. dodal/devices/tetramm.py +7 -7
  190. dodal/devices/turbo_slit.py +8 -7
  191. dodal/devices/undulator.py +42 -56
  192. dodal/devices/util/adjuster_plans.py +2 -3
  193. dodal/devices/util/epics_util.py +10 -10
  194. dodal/devices/util/lookup_tables.py +17 -18
  195. dodal/devices/v2f.py +2 -3
  196. dodal/devices/watsonmarlow323_pump.py +1 -1
  197. dodal/devices/xbpm_feedback.py +3 -2
  198. dodal/devices/xspress3/xspress3.py +8 -11
  199. dodal/devices/xspress3/xspress3_channel.py +3 -6
  200. dodal/devices/zebra/zebra.py +6 -7
  201. dodal/devices/zebra/zebra_constants_mapping.py +11 -7
  202. dodal/devices/zebra/zebra_controlled_shutter.py +2 -1
  203. dodal/devices/zocalo/zocalo_interaction.py +14 -14
  204. dodal/devices/zocalo/zocalo_results.py +33 -33
  205. dodal/log.py +23 -20
  206. dodal/plan_stubs/check_topup.py +15 -15
  207. dodal/plan_stubs/data_session.py +6 -6
  208. dodal/plan_stubs/motor_utils.py +22 -18
  209. dodal/plan_stubs/pressure_jump_cell.py +18 -0
  210. dodal/plan_stubs/wrapped.py +40 -55
  211. dodal/plans/bimorph.py +63 -52
  212. dodal/plans/device_setup_plans/__init__.py +5 -0
  213. dodal/plans/device_setup_plans/setup_pin_tip_params.py +63 -0
  214. dodal/plans/preprocessors/verify_undulator_gap.py +10 -8
  215. dodal/plans/spec_path.py +3 -5
  216. dodal/plans/verify_undulator_gap.py +1 -2
  217. dodal/plans/wrapped.py +4 -3
  218. dodal/testing/electron_analyser/device_factory.py +5 -7
  219. dodal/testing/fixtures/devices/apple2.py +38 -0
  220. dodal/testing/fixtures/run_engine.py +3 -7
  221. dodal/testing/fixtures/utils.py +1 -2
  222. dodal/utils.py +60 -58
  223. dls_dodal-1.69.0.dist-info/RECORD +0 -338
  224. dodal/beamline_specific_utils/i05_shared.py +0 -14
  225. dodal/devices/b07/__init__.py +0 -3
  226. dodal/devices/i04/beam_centre.py +0 -84
  227. dodal/devices/i05/__init__.py +0 -3
  228. dodal/devices/i09/__init__.py +0 -3
  229. dodal/devices/i21/__init__.py +0 -5
  230. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/entry_points.txt +0 -0
  231. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/licenses/LICENSE +0 -0
  232. {dls_dodal-1.69.0.dist-info → dls_dodal-2.0.0.dist-info}/top_level.txt +0 -0
  233. /dodal/{beamline_specific_utils → devices/beamlines}/__init__.py +0 -0
  234. /dodal/devices/{b07 → beamlines/b07}/enums.py +0 -0
  235. /dodal/devices/{b07_1 → beamlines/b07_1}/enums.py +0 -0
  236. /dodal/devices/{b16 → beamlines/b16}/__init__.py +0 -0
  237. /dodal/devices/{i02_1 → beamlines/i02_1}/__init__.py +0 -0
  238. /dodal/devices/{i02_2 → beamlines/i02_2}/__init__.py +0 -0
  239. /dodal/devices/{i03 → beamlines/i03}/__init__.py +0 -0
  240. /dodal/devices/{i03 → beamlines/i03}/constants.py +0 -0
  241. /dodal/devices/{i04 → beamlines/i04}/__init__.py +0 -0
  242. /dodal/devices/{i04 → beamlines/i04}/constants.py +0 -0
  243. /dodal/devices/{i04 → beamlines/i04}/max_pixel.py +0 -0
  244. /dodal/devices/{i05 → beamlines/i05}/enums.py +0 -0
  245. /dodal/devices/{i07 → beamlines/i07}/__init__.py +0 -0
  246. /dodal/devices/{i09 → beamlines/i09}/enums.py +0 -0
  247. /dodal/devices/{i09_1 → beamlines/i09_1}/__init__.py +0 -0
  248. /dodal/devices/{i09_1 → beamlines/i09_1}/enums.py +0 -0
  249. /dodal/devices/{i09_1_shared → beamlines/i09_1_shared}/__init__.py +0 -0
  250. /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/__init__.py +0 -0
  251. /dodal/devices/{i09_2_shared → beamlines/i09_2_shared}/i09_apple2.py +0 -0
  252. /dodal/devices/{i10 → beamlines/i10}/__init__.py +0 -0
  253. /dodal/devices/{i10 → beamlines/i10}/i10_setting_data.py +0 -0
  254. /dodal/devices/{i10 → beamlines/i10}/mirrors.py +0 -0
  255. /dodal/devices/{i10 → beamlines/i10}/rasor/__init__.py +0 -0
  256. /dodal/devices/{i10 → beamlines/i10}/rasor/rasor_scaler_cards.py +0 -0
  257. /dodal/devices/{i11 → beamlines/i10_1/electromagnet}/__init__.py +0 -0
  258. /dodal/devices/{i13_1 → beamlines/i11}/__init__.py +0 -0
  259. /dodal/devices/{i15 → beamlines/i13_1}/__init__.py +0 -0
  260. /dodal/devices/{i13_1 → beamlines/i13_1}/merlin_controller.py +0 -0
  261. /dodal/devices/{i17 → beamlines/i15}/__init__.py +0 -0
  262. /dodal/devices/{i15 → beamlines/i15}/laue.py +0 -0
  263. /dodal/devices/{i15 → beamlines/i15}/motors.py +0 -0
  264. /dodal/devices/{i15 → beamlines/i15}/rail.py +0 -0
  265. /dodal/devices/{i18 → beamlines/i17}/__init__.py +0 -0
  266. /dodal/devices/{i19 → beamlines/i18}/__init__.py +0 -0
  267. /dodal/devices/{i18 → beamlines/i18}/kb_mirror.py +0 -0
  268. /dodal/devices/{i19/access_controlled → beamlines/i19}/__init__.py +0 -0
  269. /dodal/devices/{i20_1 → beamlines/i19/access_controlled}/__init__.py +0 -0
  270. /dodal/devices/{i19 → beamlines/i19}/access_controlled/hutch_access.py +0 -0
  271. /dodal/devices/{i19 → beamlines/i19}/beamstop.py +0 -0
  272. /dodal/devices/{i19 → beamlines/i19}/diffractometer.py +0 -0
  273. /dodal/devices/{i22 → beamlines/i20_1}/__init__.py +0 -0
  274. /dodal/devices/{i21 → beamlines/i21}/enums.py +0 -0
  275. /dodal/devices/{i24 → beamlines/i22}/__init__.py +0 -0
  276. /dodal/devices/{p99 → beamlines/i24}/__init__.py +0 -0
  277. /dodal/devices/{i24 → beamlines/i24}/aperture.py +0 -0
  278. /dodal/devices/{i24 → beamlines/i24}/focus_mirrors.py +0 -0
  279. /dodal/devices/{i24 → beamlines/i24}/vgonio.py +0 -0
  280. /dodal/devices/{p60 → beamlines/p60}/__init__.py +0 -0
  281. /dodal/devices/{p60 → beamlines/p60}/enums.py +0 -0
  282. /dodal/devices/{p99 → beamlines/p99}/sample_stage.py +0 -0
@@ -19,12 +19,12 @@ def _add_parallel_lines_to_image(
19
19
  orientation=Orientation.horizontal,
20
20
  ):
21
21
  """Draws horizontal or vertical parallel lines on a given image.
22
- Draws a line of a given length and orientation starting from a given point; then \
23
- draws a given number of parallel lines equally spaced with a given spacing. \
24
- If the line is horizontal, the start point corresponds to left end of the initial \
25
- line and the other parallel lines will be drawn below the initial line; if \
26
- vertical, the start point corresponds to the top end of the initial line and the \
27
- other parallel lines will be drawn to the right of the initial line. (0,0) is the \
22
+ Draws a line of a given length and orientation starting from a given point; then
23
+ draws a given number of parallel lines equally spaced with a given spacing.
24
+ If the line is horizontal, the start point corresponds to left end of the initial
25
+ line and the other parallel lines will be drawn below the initial line; if
26
+ vertical, the start point corresponds to the top end of the initial line and the
27
+ other parallel lines will be drawn to the right of the initial line. (0,0) is the
28
28
  top left of the image.
29
29
 
30
30
  Args:
@@ -32,11 +32,12 @@ def _add_parallel_lines_to_image(
32
32
  start_x (int): The x coordinate (in pixels) of the start of the initial line.
33
33
  start_y (int): The y coordinate (in pixels) of the start of the initial line.
34
34
  line_length (int): The length of each of the parallel lines in pixels.
35
- spacing (float): The spacing, in pixels, between each parallel line. Strictly, \
35
+ spacing (float): The spacing, in pixels, between each parallel line. Strictly,
36
36
  there are spacing-1 pixels between each line
37
37
  num_lines (int): The total number of parallel lines to draw.
38
- orientation (Orientation): The orientation (horizontal or vertical) of the \
39
- parallel lines to draw."""
38
+ orientation (Orientation): The orientation (horizontal or vertical) of the
39
+ parallel lines to draw.
40
+ """
40
41
  lines = [
41
42
  (
42
43
  (
@@ -8,12 +8,12 @@ CROSSHAIR_FILL_COLOUR = "White"
8
8
 
9
9
 
10
10
  def draw_crosshair(image: Image.Image, beam_x: int, beam_y: int):
11
- """
12
- Draw a crosshair at the beam centre coordinates specified.
11
+ """Draw a crosshair at the beam centre coordinates specified.
12
+
13
13
  Args:
14
- image: The image to draw the crosshair onto. This is mutated.
15
- beam_x: The x-coordinate of the crosshair (pixels)
16
- beam_y: The y-coordinate of the crosshair (pixels)
14
+ image (Image): The image to draw the crosshair onto. This is mutated.
15
+ beam_x (int): The x-coordinate of the crosshair (pixels).
16
+ beam_y (int): The y-coordinate of the crosshair (pixels).
17
17
  """
18
18
  draw = ImageDraw.Draw(image)
19
19
  outline_width = 1
@@ -51,16 +51,18 @@ def compute_beam_centre_pixel_xy_for_mm_position(
51
51
  beam_pos_at_origin_px: Pixel,
52
52
  microns_per_pixel: tuple[float, float],
53
53
  ) -> Pixel:
54
- """
55
- Compute the location of the beam centre in pixels on a reference image.
54
+ """Compute the location of the beam centre in pixels on a reference image.
55
+
56
56
  Args:
57
- sample_pos_mm: x, y location of the sample in mm relative to when the reference image
58
- was taken.
59
- beam_pos_at_origin_px: x, y position of the beam centre in the reference image (pixels)
60
- microns_per_pixel: x, y scaling factor relating the sample position to the position in the image.
61
- Returns:
62
- x, y location of the beam centre (pixels)
57
+ sample_pos_mm (tuple[float, float]): x, y location of the sample in mm relative
58
+ to when the reference image was taken.
59
+ beam_pos_at_origin_px (Pixel): x, y position of the beam centre in the reference
60
+ image (pixels).
61
+ microns_per_pixel (tuple[float, float]): x, y scaling factor relating the sample
62
+ position to the position in the image.
63
63
 
64
+ Returns:
65
+ Pixel: x, y location of the beam centre (pixels).
64
66
  """
65
67
 
66
68
  def centre(sample_pos, beam_pos, um_per_px) -> int:
@@ -41,9 +41,7 @@ def bottom_right_from_top_left(
41
41
 
42
42
 
43
43
  class EdgeOutputArrayImageType(IntEnum):
44
- """
45
- Enum to store the types of image to tweak the output array. We use Original.
46
- """
44
+ """Enum to store the types of image to tweak the output array. We use Original."""
47
45
 
48
46
  ORIGINAL = 0
49
47
  GREYSCALE = 1
@@ -57,8 +55,9 @@ def get_move_required_so_that_beam_is_at_pixel(
57
55
  pixel: Pixel,
58
56
  oav: OAV,
59
57
  ) -> Generator[Msg, None, np.ndarray]:
60
- """Calculate the required move so that the given pixel is in the centre of the beam."""
61
-
58
+ """Calculate the required move so that the given pixel is in the centre of the
59
+ beam.
60
+ """
62
61
  current_motor_xyz = np.array(
63
62
  [
64
63
  (yield from bps.rd(gonio.x)),
@@ -95,7 +94,7 @@ def calculate_x_y_z_of_pixel(
95
94
  microns_per_pixel: tuple[float, float],
96
95
  xyz_direction: tuple[int, int, int],
97
96
  ) -> np.ndarray:
98
- """Get the x, y, z position of a pixel in mm"""
97
+ """Get the x, y, z position of a pixel in mm."""
99
98
  beam_distance_px: Pixel = calculate_beam_distance(beam_centre, *pixel)
100
99
 
101
100
  return current_x_y_z + camera_coordinates_to_xyz_mm(
@@ -123,13 +122,11 @@ def wait_for_tip_to_be_found(
123
122
 
124
123
 
125
124
  def convert_to_gray_and_blur(data: cv2.typing.MatLike) -> cv2.typing.MatLike:
125
+ """Preprocess the image array data (convert to grayscale and apply a gaussian blur)
126
+ Image is converted to grayscale (using a weighted mean as green contributes more to
127
+ brightness) as we aren't interested in data relating to colour. A blur is then
128
+ applied to mitigate errors due to rogue hot pixels.
126
129
  """
127
- Preprocess the image array data (convert to grayscale and apply a gaussian blur)
128
- Image is converted to grayscale (using a weighted mean as green contributes more to brightness)
129
- as we aren't interested in data relating to colour. A blur is then applied to mitigate
130
- errors due to rogue hot pixels.
131
- """
132
-
133
130
  # kernel size describes how many of the neighbouring pixels are used for the blur,
134
131
  # higher kernal size means more of a blur effect
135
132
  kernel_size = (7, 7)
dodal/devices/p45.py CHANGED
@@ -3,9 +3,7 @@ from ophyd_async.epics.motor import Motor
3
3
 
4
4
 
5
5
  class SampleY(StandardReadable):
6
- """
7
- Motors for controlling the sample's y position and stretch in the y axis.
8
- """
6
+ """Motors for controlling the sample's y position and stretch in the y axis."""
9
7
 
10
8
  def __init__(self, prefix: str, name: str = ""):
11
9
  with self.add_children_as_readables():
@@ -17,9 +15,7 @@ class SampleY(StandardReadable):
17
15
 
18
16
 
19
17
  class SampleTheta(StandardReadable):
20
- """
21
- Motors for controlling the sample's theta position and skew
22
- """
18
+ """Motors for controlling the sample's theta position and skew."""
23
19
 
24
20
  def __init__(self, prefix: str, name: str = ""):
25
21
  with self.add_children_as_readables():
@@ -31,9 +27,7 @@ class SampleTheta(StandardReadable):
31
27
 
32
28
 
33
29
  class TomoStageWithStretchAndSkew(StandardReadable):
34
- """
35
- Grouping of motors for the P45 tomography stage
36
- """
30
+ """Grouping of motors for the P45 tomography stage."""
37
31
 
38
32
  def __init__(self, prefix: str, name: str = ""):
39
33
  with self.add_children_as_readables():
dodal/devices/pgm.py CHANGED
@@ -8,8 +8,14 @@ from ophyd_async.epics.motor import Motor
8
8
 
9
9
 
10
10
  class PlaneGratingMonochromator(StandardReadable):
11
- """
12
- Plane grating monochromator, it is use in soft x-ray beamline to generate monochromic beam.
11
+ """Plane grating monochromator, it is use in soft x-ray beamline to generate
12
+ monochromic beam.
13
+
14
+ Args:
15
+ prefix (str): Beamline specific part of the PV.
16
+ grating (type[StrictEnum]): The Enum for the grating table.
17
+ grating_pv (str): The suffix PV part of grating PV.
18
+ name (str, optional): Name of the device.
13
19
  """
14
20
 
15
21
  def __init__(
@@ -19,18 +25,6 @@ class PlaneGratingMonochromator(StandardReadable):
19
25
  grating_pv: str = "GRATINGSELECT:SELECT",
20
26
  name: str = "",
21
27
  ) -> None:
22
- """
23
- Parameters
24
- ----------
25
- prefix:
26
- Beamline specific part of the PV
27
- grating:
28
- The Enum for the grating table.
29
- grating_pv:
30
- The suffix pv part of grating Pv
31
- name:
32
- Name of the device
33
- """
34
28
  with self.add_children_as_readables():
35
29
  self.energy = Motor(prefix + "ENERGY")
36
30
  with self.add_children_as_readables(StandardReadableFormat.CONFIG_SIGNAL):
@@ -2,14 +2,21 @@ import asyncio
2
2
  from enum import IntEnum
3
3
  from typing import Generic, TypeVar
4
4
 
5
- from bluesky.protocols import Movable
5
+ from bluesky.protocols import Movable, Stoppable, Triggerable
6
6
  from ophyd_async.core import (
7
7
  AsyncStatus,
8
+ DeviceMock,
8
9
  DeviceVector,
10
+ Reference,
9
11
  SignalR,
10
12
  StandardReadable,
11
13
  StandardReadableFormat,
12
14
  StrictEnum,
15
+ callback_on_mock_put,
16
+ default_mock_class,
17
+ set_and_wait_for_other_value,
18
+ set_mock_value,
19
+ wait_for_value,
13
20
  )
14
21
  from ophyd_async.epics.core import epics_signal_r, epics_signal_rw
15
22
 
@@ -22,11 +29,6 @@ class PumpState(StrictEnum):
22
29
  AUTO_POSITION = "Auto Position"
23
30
 
24
31
 
25
- class StopState(StrictEnum):
26
- CONTINUE = "CONTINUE"
27
- STOP = "STOP"
28
-
29
-
30
32
  class ValveControlRequest(StrictEnum):
31
33
  OPEN = "Open"
32
34
  CLOSE = "Close"
@@ -99,10 +101,8 @@ class ValveControl(
99
101
 
100
102
 
101
103
  class AllValvesControl(StandardReadable):
102
- """
103
- The default IOC for this device only controls
104
- specific valves. Other valves are under manual
105
- control.
104
+ """The default IOC for this device only controls specific valves. Other valves are
105
+ under manual control.
106
106
  """
107
107
 
108
108
  def __init__(
@@ -163,12 +163,10 @@ class Pump(StandardReadable):
163
163
 
164
164
 
165
165
  class PressureTransducer(StandardReadable):
166
- """
167
- Pressure transducer for a high pressure X-ray cell.
166
+ """Pressure transducer for a high pressure X-ray cell.
168
167
  This is the chamber and there are three of them.
169
168
  1 is the start, 3 is where the sample is.
170
169
  NOTE: the distinction between the adc prefix and the cell prefix is kept here.
171
-
172
170
  """
173
171
 
174
172
  def __init__(
@@ -201,34 +199,99 @@ class PressureTransducer(StandardReadable):
201
199
  super().__init__(name)
202
200
 
203
201
 
204
- class PressureJumpCellController(StandardReadable):
205
- """
206
- Top-level control for a fixed pressure or pressure jumps.
207
- """
202
+ class DoJump(StandardReadable, Triggerable):
203
+ def __init__(
204
+ self,
205
+ prefix: str,
206
+ busy_signal: Reference,
207
+ timeout_signal: Reference,
208
+ name: str = "",
209
+ ):
210
+ self._busy_signal = busy_signal
211
+ self._timeout_signal = timeout_signal
208
212
 
209
- def __init__(self, prefix: str, name: str = "") -> None:
210
213
  with self.add_children_as_readables():
211
- self.stop = epics_signal_rw(StopState, f"{prefix}STOP")
214
+ self.set_jump = epics_signal_rw(bool, f"{prefix}SETJUMP")
215
+
216
+ self._name = name
217
+ super().__init__(name)
218
+
219
+ @AsyncStatus.wrap
220
+ async def trigger(self):
221
+ await set_and_wait_for_other_value(
222
+ self.set_jump, True, self._busy_signal(), True
223
+ )
224
+
225
+ timeout_value = await self._timeout_signal().get_value()
226
+ await wait_for_value(self._busy_signal(), False, timeout_value)
212
227
 
213
- self.target_pressure = epics_signal_rw(float, f"{prefix}TARGET")
214
- self.timeout = epics_signal_rw(float, f"{prefix}TIMER.HIGH")
215
- self.go = epics_signal_rw(bool, f"{prefix}GO")
216
228
 
217
- self.from_pressure = epics_signal_rw(float, f"{prefix}JUMPF")
218
- self.to_pressure = epics_signal_rw(float, f"{prefix}JUMPT")
219
- self.jump_ready = epics_signal_rw(bool, f"{prefix}SETJUMP")
229
+ class PressureJumpCellController(StandardReadable, Movable, Stoppable):
230
+ """Top-level control for a fixed pressure or pressure jumps."""
220
231
 
232
+ def __init__(self, prefix: str, name: str = "") -> None:
233
+ with self.add_children_as_readables():
234
+ # Common
235
+ self.busy = epics_signal_r(bool, f"{prefix}GOTOBUSY")
221
236
  self.result = epics_signal_r(str, f"{prefix}RESULT")
237
+ self.timeout = epics_signal_rw(float, f"{prefix}TIMER.HIGH")
222
238
 
239
+ # Constant pressure
240
+ self.target_pressure = epics_signal_rw(int, f"{prefix}TARGET")
241
+ self.go = epics_signal_rw(bool, f"{prefix}GO")
242
+
243
+ # Pressure jump
244
+ self.from_pressure = epics_signal_rw(int, f"{prefix}JUMPF")
245
+ self.to_pressure = epics_signal_rw(int, f"{prefix}JUMPT")
246
+ self.do_jump = DoJump(
247
+ prefix, Reference(self.busy), Reference(self.timeout), name
248
+ )
249
+
250
+ # Internal
251
+ self._stop = epics_signal_rw(bool, f"{prefix}STOP")
223
252
  self._name = name
253
+
224
254
  super().__init__(name)
225
255
 
256
+ @AsyncStatus.wrap
257
+ async def set(self, value: int):
258
+ """Sets the desired pressure waiting for the device to complete the
259
+ operation.
260
+ """
261
+ timeout = await self.timeout.get_value()
262
+
263
+ await self.target_pressure.set(value)
264
+ await set_and_wait_for_other_value(self.go, True, self.busy, True)
265
+
266
+ await wait_for_value(self.busy, False, timeout) # Change complete
226
267
 
268
+ @AsyncStatus.wrap
269
+ async def stop(self, success=True):
270
+ await self._stop.set(True)
271
+
272
+
273
+ class BusyMock(DeviceMock["PressureJumpCell"]):
274
+ async def connect(self, device) -> None:
275
+ async def busy(*_, **__):
276
+ async def busy_idle():
277
+ await asyncio.sleep(0)
278
+ set_mock_value(device.control.busy, True)
279
+ await asyncio.sleep(0)
280
+ set_mock_value(device.control.busy, False)
281
+
282
+ asyncio.create_task(busy_idle())
283
+
284
+ callback_on_mock_put(device.control.go, busy)
285
+ callback_on_mock_put(device.control.do_jump.set_jump, busy)
286
+
287
+
288
+ @default_mock_class(BusyMock)
227
289
  class PressureJumpCell(StandardReadable):
228
- """
229
- High pressure X-ray cell, used to apply pressure or pressure jumps to a sample.
230
- prefix: str
231
- The prefix of beamline - SPECIAL - unusual that the cell prefix is computed separately
290
+ """High pressure X-ray cell, used to apply pressure or pressure jumps to a sample.
291
+
292
+ Args:
293
+ prefix (str): The prefix of beamline - SPECIAL - unusual that the cell prefix is
294
+ computed separately.
232
295
  """
233
296
 
234
297
  def __init__(
@@ -241,9 +304,7 @@ class PressureJumpCell(StandardReadable):
241
304
  self.all_valves_control = AllValvesControl(f"{prefix}{cell_prefix}", name)
242
305
  self.pump = Pump(f"{prefix}{cell_prefix}", name)
243
306
 
244
- self.controller = PressureJumpCellController(
245
- f"{prefix}{cell_prefix}CTRL:", name
246
- )
307
+ self.control = PressureJumpCellController(f"{prefix}{cell_prefix}CTRL:", name)
247
308
 
248
309
  with self.add_children_as_readables():
249
310
  self.pressure_transducers: DeviceVector[PressureTransducer] = DeviceVector(
dodal/devices/qbpm.py CHANGED
@@ -3,9 +3,7 @@ from ophyd_async.epics.core import epics_signal_r
3
3
 
4
4
 
5
5
  class QBPM(StandardReadable):
6
- """
7
- A beam position monitor that gives a position and intensity of the beam.
8
- """
6
+ """A beam position monitor that gives a position and intensity of the beam."""
9
7
 
10
8
  def __init__(
11
9
  self,
dodal/devices/robot.py CHANGED
@@ -87,6 +87,11 @@ class BartRobot(StandardReadable, Movable[SampleLocation]):
87
87
  # How far the gonio position can be out before loading will fail
88
88
  LOAD_TOLERANCE_MM = 0.02
89
89
 
90
+ # mode constants for CRYO_MODE - TODO when https://jira.diamond.ac.uk/browse/I03-1056
91
+ # is completed these should be made into a proper enum
92
+ CRYO_MODE_WARM = 0.0
93
+ CRYO_MODE_CRYO = 1.0
94
+
90
95
  def __init__(self, prefix: str, name: str = "") -> None:
91
96
  with self.add_children_as_readables(StandardReadableFormat.HINTED_SIGNAL):
92
97
  self.barcode = epics_signal_r(str, prefix + "BARCODE")
@@ -194,12 +199,15 @@ class BartRobot(StandardReadable, Movable[SampleLocation]):
194
199
 
195
200
  @AsyncStatus.wrap
196
201
  async def set(self, value: SampleLocation):
197
- """
198
- Perform a sample load from the specified sample location
202
+ """Perform a sample load from the specified sample location.
203
+
199
204
  Args:
200
- value: The pin and puck to load, or SAMPLE_LOCATION_EMPTY to unload the sample.
205
+ value (SampleLocation): The pin and puck to load, or SAMPLE_LOCATION_EMPTY
206
+ to unload the sample.
207
+
201
208
  Raises:
202
- RobotLoadError if a timeout occurs, or if an error occurs loading the sample.
209
+ RobotLoadError: If a timeout occurs, or if an error occurs loading the
210
+ sample.
203
211
  """
204
212
  try:
205
213
  if value != SAMPLE_LOCATION_EMPTY:
@@ -3,7 +3,7 @@ from ophyd_async.epics.motor import Motor
3
3
 
4
4
 
5
5
  class S4SlitGaps(StandardReadable):
6
- """Note that the S4 slits have a different PV fromat to other beamline slits"""
6
+ """Note that the S4 slits have a different PV fromat to other beamline slits."""
7
7
 
8
8
  def __init__(self, prefix: str, name="") -> None:
9
9
  with self.add_children_as_readables():
@@ -14,7 +14,8 @@ T = TypeVar("T")
14
14
 
15
15
  def get_obj_from_selected_source(selected_source: SelectedSource, s1: T, s2: T) -> T:
16
16
  """Util function that maps enum values for SelectedSource to two objects. It then
17
- returns one of the objects that corrosponds to the selected_source value."""
17
+ returns one of the objects that corrosponds to the selected_source value.
18
+ """
18
19
  match selected_source:
19
20
  case SelectedSource.SOURCE1:
20
21
  return s1
@@ -24,7 +25,9 @@ def get_obj_from_selected_source(selected_source: SelectedSource, s1: T, s2: T)
24
25
 
25
26
  class SourceSelector(StandardReadable, Movable[SelectedSource]):
26
27
  """Device that holds a selected_source signal enum of SelectedSource. Useful for
27
- beamlines with multiple sources to coordinate which energy source or shutter to use."""
28
+ beamlines with multiple sources to coordinate which energy source or shutter to
29
+ use.
30
+ """
28
31
 
29
32
  def __init__(self, name: str = ""):
30
33
  with self.add_children_as_readables():
dodal/devices/slits.py CHANGED
@@ -19,8 +19,7 @@ class MinimalSlits(StandardReadable):
19
19
 
20
20
 
21
21
  class Slits(MinimalSlits):
22
- """
23
- Representation of a 4-blade set of slits. Allows control/readout of the gap
22
+ """Representation of a 4-blade set of slits. Allows control/readout of the gap
24
23
  between each pair of blades.
25
24
  """
26
25
 
@@ -40,9 +39,7 @@ class Slits(MinimalSlits):
40
39
 
41
40
 
42
41
  class SlitsY(StandardReadable):
43
- """
44
- Representation of a 2-blade slits.
45
- """
42
+ """Representation of a 2-blade slits."""
46
43
 
47
44
  def __init__(
48
45
  self,
dodal/devices/smargon.py CHANGED
@@ -68,7 +68,7 @@ class DeferMoves(StrictEnum):
68
68
 
69
69
 
70
70
  class CombinedMove(TypedDict, total=False):
71
- """A move on multiple axes at once using a deferred move"""
71
+ """A move on multiple axes at once using a deferred move."""
72
72
 
73
73
  x: float | None
74
74
  y: float | None
@@ -79,8 +79,7 @@ class CombinedMove(TypedDict, total=False):
79
79
 
80
80
 
81
81
  class Smargon(XYZOmegaStage, Movable):
82
- """
83
- Real motors added to allow stops following pin load (e.g. real_x1.stop() )
82
+ """Real motors added to allow stops following pin load (e.g. real_x1.stop() )
84
83
  X1 and X2 real motors provide compound chi motion as well as the compound X travel,
85
84
  increasing the gap between x1 and x2 changes chi, moving together changes virtual x.
86
85
  Robot loading can nudge these and lead to errors.
@@ -24,31 +24,28 @@ class Heater336Settings(StrictEnum):
24
24
 
25
25
 
26
26
  class Lakeshore(LakeshoreBaseIO, StandardReadable, Movable[float]):
27
- """
28
- Device for controlling and reading from a Lakeshore temperature controller.
27
+ """Device for controlling and reading from a Lakeshore temperature controller.
29
28
  It supports multiple channels and PID control.
30
29
 
31
- Attributes
32
- ----------
33
- temperature : LakeshoreBaseIO
34
- Temperature IO interface.
35
- PID : PIDBaseIO
36
- PID IO interface.
37
- control_channel : derived_signal_rw
38
- Signal for selecting the control channel,
39
- optional readback as hinted signal
40
- (default readback channel is the same as control channel).
41
-
42
- temperature_high_limit: soft_signal_rw
43
- Signal to store the soft high temperature limit.
44
- temperature_low_limit: soft_signal_rw
45
- Signal to store the soft low temperature limit.
46
-
47
-
48
- Methods
49
- -------
50
- set(value: float)
51
- Set the temperature setpoint for the selected control channel.
30
+ Attributes:
31
+ temperature (LakeshoreBaseIO): Temperature IO interface.
32
+ PID (PIDBaseIO): PID IO interface.
33
+ control_channel (derived_signal_rw): Signal for selecting the control channel,
34
+ optional readback as hinted signal (default readback channel is the same
35
+ as control channel).
36
+ temperature_high_limit (soft_signal_rw): Signal to store the soft high
37
+ temperature limit.
38
+ temperature_low_limit (soft_signal_rw): Signal to store the soft low
39
+ temperature limit.
40
+
41
+ Args:
42
+ prefix (str): The EPICS prefix for the device.
43
+ no_channels (int): Number of temperature channels.
44
+ heater_setting (type[SignalDatatypeT]): Enum type for heater settings.
45
+ control_channel (int, optional): The initial control channel (default is 1).
46
+ single_control_channel (bool, optional): Whether to use a single control
47
+ channel (default is False).
48
+ name (str, optional): Name of the device.
52
49
  """
53
50
 
54
51
  def __init__(
@@ -60,22 +57,6 @@ class Lakeshore(LakeshoreBaseIO, StandardReadable, Movable[float]):
60
57
  single_control_channel: bool = False,
61
58
  name: str = "",
62
59
  ):
63
- """
64
- Parameters
65
- ----------
66
- prefix : str
67
- The EPICS prefix for the device.
68
- no_channels : int
69
- Number of temperature channels.
70
- heater_setting : type[SignalDatatypeT]
71
- Enum type for heater settings.
72
- control_channel : int, optional
73
- The initial control channel (default is 1).
74
- single_control_channel : bool, optional
75
- Whether to use a single control channel (default is False).
76
- name : str, optional
77
- Name of the device.
78
- """
79
60
  self._control_channel = soft_signal_rw(int, initial_value=control_channel)
80
61
  self.temperature_high_limit = soft_signal_rw(float, initial_value=400)
81
62
  self.temperature_low_limit = soft_signal_rw(float, initial_value=0)
@@ -113,9 +94,7 @@ class Lakeshore(LakeshoreBaseIO, StandardReadable, Movable[float]):
113
94
 
114
95
  @AsyncStatus.wrap
115
96
  async def set(self, value: float) -> None:
116
- """
117
- Set the temperature setpoint for the active control channel.
118
- """
97
+ """Set the temperature setpoint for the active control channel."""
119
98
  high, low = await gather(
120
99
  self.temperature_high_limit.get_value(),
121
100
  self.temperature_low_limit.get_value(),
@@ -149,22 +128,20 @@ class Lakeshore(LakeshoreBaseIO, StandardReadable, Movable[float]):
149
128
 
150
129
 
151
130
  class Lakeshore336(Lakeshore):
131
+ """Lakeshore 336 temperature controller. With 4 readback and control channels.
132
+ Heater settings are: Off, Low, Medium, High.
133
+
134
+ Args:
135
+ prefix (str): The EPICS prefix for the device.
136
+ control_channel (int, optional): The initial control channel (default is 1).
137
+ """
138
+
152
139
  def __init__(
153
140
  self,
154
141
  prefix: str,
155
142
  control_channel: int = 1,
156
143
  name: str = "",
157
144
  ):
158
- """
159
- Lakeshore 336 temperature controller. With 4 readback and control channels.
160
- Heater settings are: Off, Low, Medium, High.
161
- Parameters
162
- ----------
163
- prefix : str
164
- The EPICS prefix for the device.
165
- control_channel : int, optional
166
- The initial control channel (default is 1).
167
- """
168
145
  super().__init__(
169
146
  prefix=prefix,
170
147
  num_readback_channel=4,
@@ -176,24 +153,21 @@ class Lakeshore336(Lakeshore):
176
153
 
177
154
 
178
155
  class Lakeshore340(Lakeshore):
156
+ """Lakeshore 340 temperature controller. With 4 readback channels and a single
157
+ control channel.
158
+ Heater settings are in power from 0 to 5. 0 is 0 watt, 5 is 50 watt.
159
+
160
+ Args:
161
+ prefix (str): The EPICS prefix for the device.
162
+ control_channel (int, optional): The initial control channel (default is 1).
163
+ """
164
+
179
165
  def __init__(
180
166
  self,
181
167
  prefix: str,
182
168
  control_channel: int = 1,
183
169
  name: str = "",
184
170
  ):
185
- """Lakeshore 340 temperature controller. With 4 readback channels and a single
186
- control channel.
187
- Heater settings are in power from 0 to 5. 0 is 0 watt, 5 is 50 watt.
188
-
189
- Parameters
190
- ----------
191
- prefix : str
192
- The EPICS prefix for the device.
193
- control_channel : int, optional
194
- The initial control channel (default is 1).
195
- """
196
-
197
171
  super().__init__(
198
172
  prefix=prefix,
199
173
  num_readback_channel=4,