TomoOptic.py 13.8 KB
Newer Older
1
2
3
4
5
6
7
8
import sys
import gevent
import numpy as np
import time

import PyTango

import bliss
9
from bliss import global_map
10
from bliss.common import session
Jibril Mammeri's avatar
Jibril Mammeri committed
11
from bliss.common.logtools import log_info, log_debug
12
13
from bliss.shell.cli.user_dialog import UserChoice, Container, UserYesNo, UserFloatInput
from bliss.shell.cli.pt_widgets import display, BlissDialog
14

15
from tomo.TomoParameters import TomoParameters
16

Jibril Mammeri's avatar
Jibril Mammeri committed
17

18
class TomoOptic(TomoParameters):
bliss administrator's avatar
bliss administrator committed
19
20
21
22
23
    """
    Base class for all tomo optic objects.
    The class implements all the standard methods to be implemented for every optic.
    
    **Attributes**:
24
25
    scintillator : string
        Name of the scintillator associated with the optic
bliss administrator's avatar
bliss administrator committed
26
27
28
    image_flipping_hor : boolean
        Implied horizontal image flipping by the objective
    image_flipping_vert : boolean
29
        Implied vertical image flipping by the objective
bliss administrator's avatar
bliss administrator committed
30
    
31
32
    rotc_motor : Bliss Axis object
        The camera rotation motor for the ojective
bliss administrator's avatar
bliss administrator committed
33
34
35
36
37
38
39
40
41
42
43
44
    focus_motor : Bliss Axis object
        The focus motor for the ojective
    focus_type : string
        The motion type of the focus motor. Can be "translation" or "rotation".
    focus_scan_range : float
        The scan range for the focus scan
    focus_scan_steps : int
        The number of scan steps for the focus scan. 
    focus_lim_pos : float
        Positive soft limit for the focus motor
    focus_lim_neg : float
        Negative soft limit for the focus motor
45
46
47
48
49
50
51
52
    manual_pixel_size : boolean
        Flag used to specify that pixel size is set manually
    unbinned_pixel_size : float
        User pixel size value
    image_pixel_size : float
        Pixel size value calculated with detector field of view and optic magnification
    tomo : Tomo object (ex: HrTomo) 
        contains all info about tomo (hardware, parameters)
bliss administrator's avatar
bliss administrator committed
53
    """
Jibril Mammeri's avatar
Jibril Mammeri committed
54

55
    def __init__(self, name, config, param_name=None, param_defaults=None):
56
        # init logging
Jibril Mammeri's avatar
Jibril Mammeri committed
57
        self.log_name = name + ".optic"
58
        global_map.register(self, tag=self.log_name)
Jibril Mammeri's avatar
Jibril Mammeri committed
59
60
        log_info(self, "__init__() entering")

61
62
        self.__name = name
        self.__config = config
63

64
65
66
        param_defaults["manual_pixel_size"] = False
        param_defaults["unbinned_pixel_size"] = 0
        param_defaults["image_pixel_size"] = 0
67
        param_defaults["scintillator"] = "None"
Jibril Mammeri's avatar
Jibril Mammeri committed
68

69
70
        # Initialise the TomoParameters class
        super().__init__(param_name, param_defaults)
Jibril Mammeri's avatar
Jibril Mammeri committed
71

72
        try:
Jibril Mammeri's avatar
Jibril Mammeri committed
73
            self.__scintillators = config["scintillator"]
74
75
        except:
            self.__scintillators = None
Jibril Mammeri's avatar
Jibril Mammeri committed
76

77
        try:
Jibril Mammeri's avatar
Jibril Mammeri committed
78
            self.__image_flipping_hor = config["image_flipping_hor"]
79
        except:
Jibril Mammeri's avatar
Jibril Mammeri committed
80
            self.__image_flipping_hor = False
81
        try:
82
            self.__image_flipping_vert = config["image_flipping_vert"]
83
        except:
84
            self.__image_flipping_vert = False
Jibril Mammeri's avatar
Jibril Mammeri committed
85

86
        try:
87
            self.__rotc_mot = config["rotc_motor"]
88
        except:
89
            self.__rotc_mot = None
Jibril Mammeri's avatar
Jibril Mammeri committed
90

91
        try:
92
            self.__focus_mot = config["focus_motor"]
93
        except:
94
            self.__focus_mot = None
Jibril Mammeri's avatar
Jibril Mammeri committed
95

96
        try:
97
            self.__focus_type = config["focus_type"]
98
        except:
99
            self.__focus_type = "unknown"
100
        try:
101
            self.__focus_scan_range = config["focus_scan_range"]
102
        except:
103
            self.__focus_scan_range = 0
104
        try:
105
            self.__focus_scan_steps = config["focus_scan_steps"]
106
        except:
107
            self.__focus_scan_steps = 0
108
        try:
Jibril Mammeri's avatar
Jibril Mammeri committed
109
            self.__focus_lim_pos = config["focus_lim_pos"]
110
        except:
Jibril Mammeri's avatar
Jibril Mammeri committed
111
            self.__focus_lim_pos = 0
112
        try:
Jibril Mammeri's avatar
Jibril Mammeri committed
113
            self.__focus_lim_neg = config["focus_lim_neg"]
114
        except:
Jibril Mammeri's avatar
Jibril Mammeri committed
115
116
117
118
119
            self.__focus_lim_neg = 0
        try:
            self.__scintillator = config["scintillator"]
        except:
            self.__scintillator = None
120
        try:
121
            self.__focus_steps_per_unit = config["focus_steps_per_unit"]
122
123
        except:
            self.__focus_steps_per_unit = 0
124

Jibril Mammeri's avatar
Jibril Mammeri committed
125
        log_info(self, "__init__() leaving")
126
127

    def manual_pixel_size_setup(self):
128
129
130
        """
        User dialog which allows to enter manually pixel size in micrometers
        """
Jibril Mammeri's avatar
Jibril Mammeri committed
131
132
133
        dlg1 = UserYesNo(label="Do you want to set pixel size manually?")
        ret = display(dlg1, title="Manual Pixel Size")

134
        if ret is True:
135
            self.manual_pixel_size = True
Jibril Mammeri's avatar
Jibril Mammeri committed
136
137
138
139
140
141
            dlg1 = UserFloatInput(
                label="Unbinned pixel size in um", defval=self.unbinned_pixel_size
            )
            ct1 = Container([dlg1], title="Unbinned Pixel Size")
            ret = BlissDialog([[ct1]], title="Pixel Size Setup").show()

142
            if ret is not False:
143
144
145
146
147
148
149
150
                self.unbinned_pixel_size = ret[dlg1]
                return True
            else:
                self.manual_pixel_size = False
                return False
        else:
            self.manual_pixel_size = False
            return False
Jibril Mammeri's avatar
Jibril Mammeri committed
151
152

    def correct_pixel_size_setup(self, detector):
153
154
        """
        User dialog which allows to modify pixel size (entered manually by user or deduced from optic magnification)
Jibril Mammeri's avatar
Jibril Mammeri committed
155
156
157
158
159
160
161
        """
        self.calculate_image_pixel_size(detector)
        dlg1 = UserYesNo(
            label=f"Do you want to correct this value? ({self.image_pixel_size} um)"
        )
        ret = display(dlg1, title="Correct Pixel Size")

162
        if ret is True:
Jibril Mammeri's avatar
Jibril Mammeri committed
163
164
165
166
167
168
169
            dlg1 = UserFloatInput(
                label="Corrected unbinned pixel size in um",
                defval=self.unbinned_pixel_size,
            )
            ct1 = Container([dlg1], title="Corrected Pixel Size")
            ret = BlissDialog([[ct1]], title="Pixel Size Setup").show()

170
            if ret is not False:
171
172
                self.unbinned_pixel_size = ret[dlg1]
            else:
Jibril Mammeri's avatar
Jibril Mammeri committed
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
                self.unbinned_pixel_size = (
                    self.image_pixel_size / detector.proxy.image_bin[1]
                )

        self.image_pixel_size = self.unbinned_pixel_size * detector.proxy.image_bin[1]

    def calculate_image_pixel_size(self, detector):
        """
        Calculate the sample image pixel size.
        Needs detector parameters and the optics magnification
        """
        ccd_ps = detector.proxy.camera_pixelsize[0]

        if detector.camera_type.lower() == "frelon":
            # conversion in microns
            ccd_ps *= 1e6

        if self.manual_pixel_size:
            self.magnification = round(ccd_ps / self.unbinned_pixel_size, 3)
        else:
            self.unbinned_pixel_size = round(ccd_ps / self.magnification, 3)

        ccd_bin = detector.proxy.image_bin[1]
        self.image_pixel_size = self.unbinned_pixel_size * ccd_bin

198
    def select_scintillator_setup(self):
199
200
201
        """
        User dialog which allows to select a scintillator among list of scintillators defined in optic config file
        """
202
203
204
        if self.__scintillators is not None:
            value_list = []
            for i in self.__scintillators:
Jibril Mammeri's avatar
Jibril Mammeri committed
205
206
                value_list.append((i, str(i)))

207
208
            # get the actual scintillator name as default
            default1 = 0
Jibril Mammeri's avatar
Jibril Mammeri committed
209
210
211
212
213
            for i in range(0, len(value_list)):
                if (
                    self.scintillator is not None
                    and self.scintillator == value_list[i][0]
                ):
214
                    default1 = i
Jibril Mammeri's avatar
Jibril Mammeri committed
215

216
            dlg1 = UserChoice(values=value_list, defval=default1)
Jibril Mammeri's avatar
Jibril Mammeri committed
217
218
219
            ct1 = Container([dlg1], title="Scintillator")
            ret = BlissDialog([[ct1]], title="Scintillator Setup").show()

220
            if ret != False:
Jibril Mammeri's avatar
Jibril Mammeri committed
221
                # get the scintillator chosen
222
223
                self.scintillator = ret[dlg1]
        else:
Jibril Mammeri's avatar
Jibril Mammeri committed
224
225
            self.scintillator = "None"

226
227
228
229
230
231
    @property
    def name(self):
        """
        The name of the optic
        """
        return self.__name
Jibril Mammeri's avatar
Jibril Mammeri committed
232

233
234
235
236
237
238
    @property
    def config(self):
        """
        The configuration of the optic
        """
        return self.__config
Jibril Mammeri's avatar
Jibril Mammeri committed
239

240
241
242
    @property
    def description(self):
        """
bliss administrator's avatar
bliss administrator committed
243
244
        The name string of the current optic.
        Musst be implemented for every optic.
245
246
        """
        pass
Jibril Mammeri's avatar
Jibril Mammeri committed
247

248
249
250
    @property
    def type(self):
        """
bliss administrator's avatar
bliss administrator committed
251
252
        The class name of the optic.
        Musst be implemented for every optic.
253
254
        """
        pass
Jibril Mammeri's avatar
Jibril Mammeri committed
255

256
    @property
257
258
259
260
261
    def scintillator(self):
        """
        Returns the name of the scintillator associated with the optic
        """
        return self.__scintillator
Jibril Mammeri's avatar
Jibril Mammeri committed
262

263
    @scintillator.setter
Jibril Mammeri's avatar
Jibril Mammeri committed
264
    def scintillator(self, value):
265
266
267
268
        """
        Sets the name of the scintillator associated with the optic
        """
        self.__scintillator = value
269
        self.parameters.scintillator = value
Jibril Mammeri's avatar
Jibril Mammeri committed
270

271
272
273
274
275
276
277
278
279
280
281
282
283
284
    @property
    def focus_steps_per_unit(self):
        """
        Returns the value of the focus steps per unit
        """
        return self.__focus_steps_per_unit

    @focus_steps_per_unit.setter
    def focus_steps_per_unit(self, value):
        """
        Sets the value of the focus steps per unit
        """
        self.__focus_steps_per_unit = value

285
286
287
288
289
290
    @property
    def manual_pixel_size(self):
        """
        Returns if pixel size is manually set or not
        """
        return self.__manual_pixel_size
Jibril Mammeri's avatar
Jibril Mammeri committed
291

292
    @manual_pixel_size.setter
Jibril Mammeri's avatar
Jibril Mammeri committed
293
    def manual_pixel_size(self, value):
294
295
296
297
        """
        Sets if pixel size is manually set or not
        """
        self.__manual_pixel_size = value
298
        self.parameters.manual_pixel_size = value
Jibril Mammeri's avatar
Jibril Mammeri committed
299

300
301
302
303
304
305
    @property
    def unbinned_pixel_size(self):
        """
        Returns pixel size value without binnning
        """
        return self.__unbinned_pixel_size
Jibril Mammeri's avatar
Jibril Mammeri committed
306

307
    @unbinned_pixel_size.setter
Jibril Mammeri's avatar
Jibril Mammeri committed
308
    def unbinned_pixel_size(self, value):
309
310
311
312
        """
        Sets pixel size without binnning to value
        """
        self.__unbinned_pixel_size = value
313
        self.parameters.unbinned_pixel_size = value
Jibril Mammeri's avatar
Jibril Mammeri committed
314

315
316
317
318
319
320
    @property
    def image_pixel_size(self):
        """
        Returns pixel size value without binnning
        """
        return self.__image_pixel_size
Jibril Mammeri's avatar
Jibril Mammeri committed
321

322
    @image_pixel_size.setter
Jibril Mammeri's avatar
Jibril Mammeri committed
323
    def image_pixel_size(self, value):
324
325
326
327
        """
        Sets pixel size without binnning to value
        """
        self.__image_pixel_size = value
328
        self.parameters.image_pixel_size = value
329

Jibril Mammeri's avatar
Jibril Mammeri committed
330
    @property
331
332
    def magnification(self):
        """
bliss administrator's avatar
bliss administrator committed
333
334
        Returns the magnification of the current optic used.
        Musst be implemented for every optic.
335
336
        """
        pass
Jibril Mammeri's avatar
Jibril Mammeri committed
337
338
339

    @magnification.setter
    def magnification(self, value):
340
341
342
343
344
        """
        Sets the magnification of the current optic used
        Musst be implemented for every optic.
        """
        pass
Jibril Mammeri's avatar
Jibril Mammeri committed
345
346

    @property
347
348
349
350
    def image_flipping(self):
        """
        Returns the implied horizontal and vertical image flipping as a list
        """
351
        return [self.__image_flipping_hor, self.__image_flipping_vert]
Jibril Mammeri's avatar
Jibril Mammeri committed
352
353
354
355
356

    def optic_setup(self):
        pass

    def setup(self, detector):
357
        """
bliss administrator's avatar
bliss administrator committed
358
359
        Set-up the optic
        Musst be implemented for every optic
360
        """
Jibril Mammeri's avatar
Jibril Mammeri committed
361
362
363
364
365
366
367

        self.optic_setup()
        self.manual_pixel_size_setup()

        self.select_scintillator_setup()
        self.correct_pixel_size_setup(detector)

368
369
370
371
    def status(self):
        """
        Prints the current ojective in use and its magnification.
        If an optics cannot be determined, the reason gets printed.
bliss administrator's avatar
bliss administrator committed
372
        Musst be implemented for every optic
373
374
        """
        pass
Jibril Mammeri's avatar
Jibril Mammeri committed
375

376
377
378
    @property
    def objective(self):
        """
bliss administrator's avatar
bliss administrator committed
379
380
        Reads and sets the current objective.
        Must be implemented for optics with more than one objective.
381
382
        """
        return 1
Jibril Mammeri's avatar
Jibril Mammeri committed
383

384
385
386
    @objective.setter
    def objective(self, value):
        """
bliss administrator's avatar
bliss administrator committed
387
388
        Moves to an objective
        Must be implemented for optics with more than one objective.
389
390
        """
        pass
Jibril Mammeri's avatar
Jibril Mammeri committed
391
392

    def rotc_motor(self):
393
394
395
        """
        Returns the Bliss Axis object of the rotation motor to be used for the current objective
        """
Jibril Mammeri's avatar
Jibril Mammeri committed
396

397
        return self.__rotc_mot
398

399
400
401
402
403
    def set_focus_motor(self, motor):
        """
        Sets the current focus motor for optics with several focus motors
        """
        self.__focus_mot = motor
Jibril Mammeri's avatar
Jibril Mammeri committed
404
405

    def focus_motor(self):
406
407
408
        """
        Returns the Bliss Axis object of the focus motor to be used for the current optic
        """
409
        return self.__focus_mot
Jibril Mammeri's avatar
Jibril Mammeri committed
410

411
412
413
414
    def focus_scan_parameters(self):
        """
        Returns a dictionary with the paramters for a focus scan
        """
415
        if self.__focus_mot == None:
Jibril Mammeri's avatar
Jibril Mammeri committed
416
417
            raise ValueError("No focus motor defined for the optic!")

418
        scan_params = {}
Jibril Mammeri's avatar
Jibril Mammeri committed
419
420
421
422
423
424
        scan_params["focus_type"] = self.__focus_type
        scan_params["focus_scan_range"] = self.__focus_scan_range
        scan_params["focus_scan_steps"] = self.__focus_scan_steps
        scan_params["focus_lim_pos"] = self.__focus_lim_pos
        scan_params["focus_lim_neg"] = self.__focus_lim_neg

425
        return scan_params
Jibril Mammeri's avatar
Jibril Mammeri committed
426

427
    def focus_config_apply(self):
428
429
430
431
432
        """
        Update focus motor steps per unit
        """
        self.__focus_mot.steps_per_unit = self.__focus_steps_per_unit

433
    def __info__(self):
Jibril Mammeri's avatar
Jibril Mammeri committed
434
        info_str = f"{self.name} optic info:\n"
435
436
437
        info_str += f"  description   = {self.description}\n"
        info_str += f"  objective     = {self.objective} \n"
        info_str += f"  magnification = {self.magnification} \n"
438
439
440
441
442
443
444
445
        if self.rotc_motor() == None:
            info_str += f"  rotc motor    = None \n"
        else:
            info_str += f"  rotc motor    = {self.rotc_motor().name} \n"
        if self.focus_motor() == None:
            info_str += f"  focus motor   = None \n"
        else:
            info_str += f"  focus motor   = {self.focus_motor().name} \n"
446
447
448
449
450
451
        if self.focus_steps_per_unit == 0:
            info_str += f"  focus steps per unit = {self.focus_motor().steps_per_unit}\n"
        else:
            info_str += f"  focus motor  steps per unit = {self.focus_motor().steps_per_unit}\n"
            info_str += f"  focus config steps per unit = {self.__focus_steps_per_unit} \n"
            
452
        info_str += f"  focus scan    = {self.focus_scan_parameters()} \n"
Jibril Mammeri's avatar
Jibril Mammeri committed
453

454
        return info_str