Dummy DetectorΒΆ
This section contains the dummydetector script.
Download file: dummydetector.py
1"""
2Dummy detector driver
3
4This file is part of lab-control-lib
5(c) 2023-2024 Pierre Thibault (pthibault@units.it)
6"""
7
8import numpy as np
9import time
10import os
11
12from lclib import register_driver, proxydevice
13from lclib.camera import CameraBase
14
15# BASE_PATH = "C:\\data\\"
16BASE_PATH = os.path.expanduser('~/dummylab-data/')
17os.makedirs(BASE_PATH, exist_ok=True)
18
19__all__ = ['Dummydetector']
20
21ADDRESS = ('localhost', 5060) # Address for the proxy driver
22
23@register_driver
24@proxydevice(address=ADDRESS)
25class Dummydetector(CameraBase):
26 """
27 Dummy detector class
28 """
29
30 DEFAULT_BROADCAST_ADDRESS = (ADDRESS[0], 9500) # address to broadcast images for viewers
31 BASE_PATH = BASE_PATH # All data is saved in subfolders of this one
32 PIXEL_SIZE = 50 # Physical pixel pitch in micrometers
33 SHAPE = (256, 512) # Native array shape (vertical, horizontal)
34 MAX_FPS = 15 # The real max FPS is higher (especially in binning mode) but this seems sufficient.
35 LOCAL_DEFAULT_CONFIG = {'binning':(1,1),
36 'save_path': 'snaps/',
37 'gain_mode':'high' # Example of camera-specific parameter
38 }
39
40 # python <3.9
41 DEFAULT_CONFIG = CameraBase.DEFAULT_CONFIG.copy()
42 DEFAULT_CONFIG.update(LOCAL_DEFAULT_CONFIG)
43
44 def __init__(self, broadcast_address=None):
45 """
46 Initialization.
47 """
48 super().__init__(broadcast_address=broadcast_address)
49
50 self.detector = None
51 self.init_device()
52
53 def init_device(self):
54 """
55 Initialize camera
56 """
57
58 self.detector = 'Would be some kind of API object'
59 self.logger.info('Detector is ready')
60
61 # Apply saved configuration
62 self.operation_mode = self.operation_mode # Calls getter/setter
63 self.exposure_time = self.config['exposure_time']
64 self.binning = self.config['binning']
65 self.exposure_number = self.config['exposure_number']
66 self.initialized = True
67
68 def _arm(self):
69 """
70 Prepare the camera for acquisition
71 """
72 self.logger.debug('Detector going live.')
73 # self.detector.go_live() # Or whatever
74
75 def _trigger(self):
76 """
77 Acquisition.
78 """
79 det = self.detector
80
81 n_exp = self.exposure_number
82 exp_time = self.exposure_time
83
84 self.logger.debug('Triggering detector.')
85 # det.software_trigger() # or whatever
86
87 self.logger.debug('Starting acquisition loop.')
88 frame_counter = 0
89 while True:
90 if not self.rolling:
91 self.print(f'\r{frame_counter}/{n_exp}', end='')
92
93 # Trigger metadata collection
94 self.grab_metadata.set()
95
96 # det.wait_for_new_frame() # or whatever
97 time.sleep(self.exposure_time)
98
99 # Get metadata
100 if not self.monitor.connected:
101 self.logger.error("Not connected to monitor! No metadata will available!")
102 self.metadata = {}
103 else:
104 self.metadata = self.monitor.return_meta(request_ID=self.name)
105
106 # Read out buffer
107 # frame, meta = det.read_buffer() # or whatever
108 frame = np.random.uniform(size=self.shape)
109 meta = {'frame_counter':frame_counter}
110 self.logger.debug(f'Acquired frame from buffer.')
111
112 # Add frame to the queue
113 self.enqueue_frame(frame, meta)
114
115 # increment count
116 frame_counter += 1
117
118 if frame_counter == n_exp:
119 # Exit if we have reached the requested nuber of exposures
120 break
121
122 if self.rolling and self.stop_rolling_flag:
123 # Exit if rolling and stop was requested
124 break
125
126 if self.abort_flag.is_set():
127 break
128 self.print()
129
130 def _disarm(self):
131 # self.detector.disarm() # or whatever
132 return
133
134 def _rearm(self):
135 # self.detector.rearm() # or whatever
136 return
137
138 def _get_exposure_time(self):
139 return self.config['exposure_time']
140
141 def _set_exposure_time(self, value):
142 # self.detector.set_exposure_time(value) # or whatever
143 self.config['exposure_time'] = value
144
145 def _get_exposure_number(self):
146 return self.config['exposure_number']
147
148 def _set_exposure_number(self, value):
149 # self.detector.set_num_of_exposures(value) # or whatever
150 self.config['exposure_number'] = value
151
152 def _get_operation_mode(self):
153 opmode = {'gain_mode': self.config['gain_mode']}
154 return opmode
155
156 def _set_operation_mode(self, opmode):
157 """
158 Set operation mode.
159 """
160 # self.detector.set_gain(opmode['gain_mode']) # or whatever
161 self.config.update(opmode)
162
163 def _get_binning(self):
164 return self.config['binning']
165
166 def _set_binning(self, value):
167 self.config['binning'] = tuple(value)
168
169 def _get_psize(self):
170 bins = tuple(self.binning)
171 if bins == (1,1):
172 return self.PIXEL_SIZE
173 elif bins == (2,2):
174 return 2*self.PIXEL_SIZE
175 else:
176 raise RuntimeError("Unknown (or not implemented) binning for pixel size calculation.")
177
178 def _get_shape(self) -> tuple:
179 bins = tuple(self.binning)
180 if bins == (1,1):
181 return self.SHAPE
182 elif bins == (2,2):
183 return self.SHAPE[0]//2, self.SHAPE[1]//2
184 else:
185 raise RuntimeError("Unknown (or not implemented) binning for shape calculation.")