Completed
Push — pulsed_with_queued_connections ( 8830a7...cddfb2 )
by
unknown
03:09
created

MagnetLogic._check_is_moving()   A

Complexity

Conditions 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
dl 0
loc 10
rs 9.4285
1
# -*- coding: utf-8 -*-
2
3
"""
4
This file contains the general logic for magnet control.
5
6
Qudi is free software: you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation, either version 3 of the License, or
9
(at your option) any later version.
10
11
Qudi is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
GNU General Public License for more details.
15
16
You should have received a copy of the GNU General Public License
17
along with Qudi. If not, see <http://www.gnu.org/licenses/>.
18
19
Copyright (c) the Qudi Developers. See the COPYRIGHT.txt file at the
20
top-level directory of this distribution and at <https://github.com/Ulm-IQO/qudi/>
21
"""
22
23
from qtpy import QtCore
24
import numpy as np
25
import time
26
import datetime
27
from collections import OrderedDict
28
29
from logic.generic_logic import GenericLogic
30
31
32
class MagnetLogic(GenericLogic):
33
    """ A general magnet logic to control an magnetic stage with an arbitrary
34
        set of axis.
35
36
    DISCLAIMER:
37
    ===========
38
39
    The current status of the magnet logic is highly experimental and not well
40
    tested. The implementation has some considerable imperfections. The state of
41
    this module is considered to be UNSTABLE.
42
43
    This module has two major issues:
44
        - a lack of proper documentation of all the methods
45
        - usage of tasks is not implemented and therefore direct connection to
46
          all the modules is used (I tried to compress as good as possible all
47
          the part, where access to other modules occurs so that a later
48
          replacement would be easier and one does not have to search throughout
49
          the whole file.)
50
51
    However, the 'high-level state maschine' for the alignment should be rather
52
    general and very powerful to use. The different state were divided in
53
    several consecutive methods, where each method can be implemented
54
    separately and can be extended for custom needs. (I have drawn a diagram,
55
    which is much more telling then the documentation I can write down here.)
56
57
    I am currently working on that and will from time to time improve the status
58
    of this module. So if you want to use it, be aware that there might appear
59
    drastic changes.
60
61
    ---
62
    Alexander Stark
63
    """
64
65
66
    _modclass = 'MagnetLogic'
67
    _modtype = 'logic'
68
69
    ## declare connectors
70
    _in = {'magnetstage': 'MagnetInterface',
71
           'optimizerlogic': 'OptimizerLogic',
72
           'counterlogic': 'CounterLogic',
73
           'odmrlogic': 'ODMRLogic',
74
           'savelogic': 'SaveLogic',
75
           'scannerlogic':'ScannerLogic',
76
           'traceanalysis':'TraceAnalysisLogic',
77
           'gatedcounterlogic': 'GatedCounterLogic',
78
           'sequencegeneratorlogic': 'SequenceGeneratorLogic'}
79
    _out = {'magnetlogic': 'MagnetLogic'}
80
81
    # General Signals, used everywhere:
82
    sigIdleStateChanged = QtCore.Signal(bool)
83
    sigPosChanged = QtCore.Signal(dict)
84
    sigVelChanged = QtCore.Signal(dict)
85
86
    sigMeasurementStarted = QtCore.Signal()
87
    sigMeasurementContinued = QtCore.Signal()
88
    sigMeasurementStopped = QtCore.Signal()
89
    sigMeasurementFinished = QtCore.Signal()
90
91
    # Signals for making the move_abs, move_rel and abort independent:
92
    sigMoveAbs = QtCore.Signal(dict)
93
    sigMoveRel = QtCore.Signal(dict)
94
    sigAbort = QtCore.Signal()
95
96
    # Alignment Signals, remember do not touch or connect from outer logic or
97
    # GUI to the leading underscore signals!
98
    _sigStepwiseAlignmentNext = QtCore.Signal()
99
    _sigContinuousAlignmentNext = QtCore.Signal()
100
    _sigInitializeMeasPos = QtCore.Signal(bool) # signal to go to the initial measurement position
101
    sigPosReached = QtCore.Signal()
102
103
    # signals if new data are writen to the data arrays (during measurement):
104
    sig1DMatrixChanged = QtCore.Signal()
105
    sig2DMatrixChanged = QtCore.Signal()
106
    sig3DMatrixChanged = QtCore.Signal()
107
108
    # signals if the axis for the alignment are changed/renewed (before a measurement):
109
    sig1DAxisChanged = QtCore.Signal()
110
    sig2DAxisChanged = QtCore.Signal()
111
    sig3DAxisChanged = QtCore.Signal()
112
113
    # signal for ODMR alignment
114
    sigODMRLowFreqChanged = QtCore.Signal()
115
    sigODMRHighFreqChanged = QtCore.Signal()
116
117
    sigTest = QtCore.Signal()
118
119
    def __init__(self, config, **kwargs):
120
        super().__init__(config=config, **kwargs)
121
122
        self._stop_measure = False
123
124
    def on_activate(self, e):
125
        """ Definition and initialisation of the GUI.
126
127
        @param object e: Fysom.event object from Fysom class.
128
                         An object created by the state machine module Fysom,
129
                         which is connected to a specific event (have a look in
130
                         the Base Class). This object contains the passed event,
131
                         the state before the event happened and the destination
132
                         of the state which should be reached after the event
133
                         had happened.
134
        """
135
        self._magnet_device = self.get_in_connector('magnetstage')
136
        self._save_logic = self.get_in_connector('savelogic')
137
138
        self.log.info('The following configuration was found.')
139
        # checking for the right configuration
140
        config = self.getConfiguration()
141
        for key in config.keys():
142
            self.log.info('{0}: {1}'.format(key,config[key]))
143
144
        #FIXME: THAT IS JUST A TEMPORARY SOLUTION! Implement the access on the
145
        #       needed methods via the TaskRunner!
146
        self._optimizer_logic = self.get_in_connector('optimizerlogic')
147
        self._confocal_logic = self.get_in_connector('scannerlogic')
148
        self._counter_logic = self.get_in_connector('counterlogic')
149
        self._odmr_logic = self.get_in_connector('odmrlogic')
150
151
        self._gc_logic = self.get_in_connector('gatedcounterlogic')
152
        self._ta_logic = self.get_in_connector('traceanalysis')
153
        self._odmr_logic = self.get_in_connector('odmrlogic')
154
155
        self._seq_gen_logic = self.get_in_connector('sequencegeneratorlogic')
156
157
        # EXPERIMENTAL:
158
        # connect now directly signals to the interface methods, so that
159
        # the logic object will be not blocks and can react on changes or abort
160
        self.sigMoveAbs.connect(self._magnet_device.move_abs)
161
        self.sigMoveRel.connect(self._magnet_device.move_rel)
162
        self.sigAbort.connect(self._magnet_device.abort)
163
164
        # signal connect for alignment:
165
166
        self._sigInitializeMeasPos.connect(self._move_to_curr_pathway_index)
167
        self._sigStepwiseAlignmentNext.connect(self._stepwise_loop_body,
168
                                               QtCore.Qt.QueuedConnection)
169
170
        self.pathway_modes = ['spiral-in', 'spiral-out', 'snake-wise', 'diagonal-snake-wise']
171
172
        if 'curr_2d_pathway_mode' in self._statusVariables:
173
            self.curr_2d_pathway_mode = self._statusVariables['curr_2d_pathway_mode']
174
        else:
175
            self.curr_2d_pathway_mode = 'snake-wise'    # choose that as default
176
177
        if '_checktime' in self._statusVariables:
178
            self._checktime = self._statusVariables['_checktime']
179
        else:
180
            self._checktime = 2.5 # in seconds
181
182
        self.sigTest.connect(self._do_premeasurement_proc)
183
184
        if '_1D_axis0_data' in self._statusVariables:
185
            self._1D_axis0_data = self._statusVariables['_1D_axis0_data']
186
        else:
187
            self._1D_axis0_data = np.zeros(2)
188
189
        if '_2D_axis0_data' in self._statusVariables:
190
            self._2D_axis0_data = self._statusVariables['_2D_axis0_data']
191
        else:
192
            self._2D_axis0_data = np.zeros(2)
193
194
        if '_2D_axis1_data' in self._statusVariables:
195
            self._2D_axis1_data = self._statusVariables['_2D_axis1_data']
196
        else:
197
            self._2D_axis1_data = np.zeros(2)
198
199
        if '_3D_axis0_data' in self._statusVariables:
200
            self._3D_axis0_data = self._statusVariables['_3D_axis0_data']
201
        else:
202
            self._3D_axis0_data = np.zeros(2)
203
204
        if '_3D_axis1_data' in self._statusVariables:
205
            self._3D_axis1_data = self._statusVariables['_3D_axis1_data']
206
        else:
207
            self._3D_axis1_data = np.zeros(2)
208
209
        if '_3D_axis2_data' in self._statusVariables:
210
            self._3D_axis2_data = self._statusVariables['_3D_axis2_data']
211
        else:
212
            self._3D_axis2_data = np.zeros(2)
213
214
        if '_1D_add_data_matrix' in self._statusVariables:
215
            self._1D_add_data_matrix = self._statusVariables['_1D_add_data_matrix']
216
        else:
217
            self._1D_add_data_matrix = np.zeros(shape=np.shape(self._1D_axis0_data), dtype=object)
218
219
220
        if '_2D_data_matrix' in self._statusVariables:
221
            self._2D_data_matrix = self._statusVariables['_2D_data_matrix']
222
        else:
223
            self._2D_data_matrix = np.zeros((2, 2))
224
225
        if '_2D_add_data_matrix' in self._statusVariables:
226
            self._2D_add_data_matrix = self._statusVariables['_2D_add_data_matrix']
227
        else:
228
            self._2D_add_data_matrix = np.zeros(shape=np.shape(self._2D_data_matrix), dtype=object)
229
230
        if '_3D_data_matrix' in self._statusVariables:
231
            self._3D_data_matrix = self._statusVariables['_3D_data_matrix']
232
        else:
233
            self._3D_data_matrix = np.zeros((2, 2, 2))
234
235
        if '_3D_add_data_matrix' in self._statusVariables:
236
            self._3D_add_data_matrix = self._statusVariables['_3D_add_data_matrix']
237
        else:
238
            self._3D_add_data_matrix = np.zeros(shape=np.shape(self._3D_data_matrix), dtype=object)
239
240
        if 'curr_alignment_method' in self._statusVariables:
241
            self.curr_alignment_method = self._statusVariables['curr_alignment_method']
242
        else:
243
            self.curr_alignment_method = '2d_fluorescence'
244
245
        self.alignment_methods = ['2d_fluorescence', '2d_odmr', '2d_nuclear']
246
247
        # Fluorescence alignment settings:
248
        if '_optimize_pos' in self._statusVariables:
249
            self._optimize_pos = self._statusVariables['_optimize_pos']
250
        else:
251
            self._optimize_pos = False
252
253
        if 'fluorescence_integration_time' in self._statusVariables:
254
            self.fluorescence_integration_time = self._statusVariables['fluorescence_integration_time']
255
        else:
256
            self.fluorescence_integration_time = 5  # integration time in s
257
258
        # ODMR alignment settings (ALL IN SI!!!):
259
260
        if 'odmr_2d_low_center_freq' in self._statusVariables:
261
            self.odmr_2d_low_center_freq = self._statusVariables['odmr_2d_low_center_freq']
262
        else:
263
            self.odmr_2d_low_center_freq = 11028e6
264
265
        if 'odmr_2d_low_step_freq' in self._statusVariables:
266
            self.odmr_2d_low_step_freq = self._statusVariables['odmr_2d_low_step_freq']
267
        else:
268
            self.odmr_2d_low_step_freq = 0.15e6
269
270
        if 'odmr_2d_low_range_freq' in self._statusVariables:
271
            self.odmr_2d_low_range_freq = self._statusVariables['odmr_2d_low_range_freq']
272
        else:
273
            self.odmr_2d_low_range_freq = 25e6
274
275
        if 'odmr_2d_low_power' in self._statusVariables:
276
            self.odmr_2d_low_power = self._statusVariables['odmr_2d_low_power']
277
        else:
278
            self.odmr_2d_low_power = 4
279
280
        if 'odmr_2d_low_runtime' in self._statusVariables:
281
            self.odmr_2d_low_runtime = self._statusVariables['odmr_2d_low_runtime']
282
        else:
283
            self.odmr_2d_low_runtime = 40
284
285
        self.odmr_2d_low_fitfunction_list = self._odmr_logic.get_fit_functions()
286
287
        if 'odmr_2d_low_fitfunction' in self._statusVariables:
288
            self.odmr_2d_low_fitfunction = self._statusVariables['odmr_2d_low_fitfunction']
289
        else:
290
            self.odmr_2d_low_fitfunction = self.odmr_2d_low_fitfunction_list[1]
291
292
293
294
        if 'odmr_2d_high_center_freq' in self._statusVariables:
295
            self.odmr_2d_high_center_freq = self._statusVariables['odmr_2d_high_center_freq']
296
        else:
297
            self.odmr_2d_high_center_freq = 16768e6
298
299
        if 'odmr_2d_high_step_freq' in self._statusVariables:
300
            self.odmr_2d_high_step_freq = self._statusVariables['odmr_2d_high_step_freq']
301
        else:
302
            self.odmr_2d_high_step_freq = 0.15e6
303
304
        if 'odmr_2d_high_range_freq' in self._statusVariables:
305
            self.odmr_2d_high_range_freq = self._statusVariables['odmr_2d_high_range_freq']
306
        else:
307
            self.odmr_2d_high_range_freq = 25e6
308
309
        if 'odmr_2d_high_power' in self._statusVariables:
310
            self.odmr_2d_high_power = self._statusVariables['odmr_2d_high_power']
311
        else:
312
            self.odmr_2d_high_power = 2
313
314
        if 'odmr_2d_high_runtime' in self._statusVariables:
315
            self.odmr_2d_high_runtime = self._statusVariables['odmr_2d_high_runtime']
316
        else:
317
            self.odmr_2d_high_runtime = 40
318
319
        self.odmr_2d_high_fitfunction_list = self._odmr_logic.get_fit_functions()
320
321
        if 'odmr_2d_high_fitfunction' in self._statusVariables:
322
            self.odmr_2d_high_fitfunction = self._statusVariables['odmr_2d_high_fitfunction']
323
        else:
324
            self.odmr_2d_high_fitfunction = self.odmr_2d_high_fitfunction_list[1]
325
326
        if 'odmr_2d_save_after_measure' in self._statusVariables:
327
            self.odmr_2d_save_after_measure = self._statusVariables['odmr_2d_save_after_measure']
328
        else:
329
            self.odmr_2d_save_after_measure = True
330
331
        if 'odmr_2d_peak_axis0_move_ratio' in self._statusVariables:
332
            self.odmr_2d_peak_axis0_move_ratio = self._statusVariables['odmr_2d_peak_axis0_move_ratio']
333
        else:
334
            self.odmr_2d_peak_axis0_move_ratio = 0 # -13e6/ 0.01e-3    # in Hz/m
335
336
        if 'odmr_2d_peak_axis1_move_ratio' in self._statusVariables:
337
            self.odmr_2d_peak_axis1_move_ratio = self._statusVariables['odmr_2d_peak_axis1_move_ratio']
338
        else:
339
            self.odmr_2d_peak_axis1_move_ratio = 0 # -6e6/0.05e-3     # in Hz/m
340
341
        # that is just a normalization value, which is needed for the ODMR
342
        # alignment, since the colorbar cannot display values greater (2**32)/2.
343
        # A solution has to found for that!
344
        self.norm = 1000
345
346
        self.odmr_2d_single_trans = False   # use that if only one ODMR
347
                                            # transition is available.
348
349
        # single shot alignment on nuclear spin settings (ALL IN SI!!!):
350
        if 'nuclear_2d_rabi_periode' in self._statusVariables:
351
            self.nuclear_2d_rabi_periode = self._statusVariables['nuclear_2d_rabi_periode']
352
        else:
353
            self.nuclear_2d_rabi_periode = 1000e-9
354
355
        if 'nuclear_2d_mw_freq' in self._statusVariables:
356
            self.nuclear_2d_mw_freq = self._statusVariables['nuclear_2d_mw_freq']
357
        else:
358
            self.nuclear_2d_mw_freq = 100e6
359
360
        if 'nuclear_2d_mw_channel' in self._statusVariables:
361
            self.nuclear_2d_mw_channel = self._statusVariables['nuclear_2d_mw_channel']
362
        else:
363
            self.nuclear_2d_mw_channel = -1
364
365
        if 'nuclear_2d_mw_power' in self._statusVariables:
366
            self.nuclear_2d_mw_power = self._statusVariables['nuclear_2d_mw_power']
367
        else:
368
            self.nuclear_2d_mw_power = -30
369
370
        if 'nuclear_2d_laser_time' in self._statusVariables:
371
            self.nuclear_2d_laser_time = self._statusVariables['nuclear_2d_laser_time']
372
        else:
373
            self.nuclear_2d_laser_time = 900e-9
374
375
        if 'nuclear_2d_laser_channel' in self._statusVariables:
376
            self.nuclear_2d_laser_channel = self._statusVariables['nuclear_2d_laser_channel']
377
        else:
378
            self.nuclear_2d_laser_channel = 2
379
380
        if 'nuclear_2d_detect_channel' in self._statusVariables:
381
            self.nuclear_2d_detect_channel = self._statusVariables['nuclear_2d_detect_channel']
382
        else:
383
            self.nuclear_2d_detect_channel = 1
384
385
        if 'nuclear_2d_idle_time' in self._statusVariables:
386
            self.nuclear_2d_idle_time = self._statusVariables['nuclear_2d_idle_time']
387
        else:
388
            self.nuclear_2d_idle_time = 1500e-9
389
390
        if 'nuclear_2d_reps_within_ssr' in self._statusVariables:
391
            self.nuclear_2d_reps_within_ssr = self._statusVariables['nuclear_2d_reps_within_ssr']
392
        else:
393
            self.nuclear_2d_reps_within_ssr = 1000
394
395
        if 'nuclear_2d_num_ssr' in self._statusVariables:
396
            self.nuclear_2d_num_ssr = self._statusVariables['nuclear_2d_num_ssr']
397
        else:
398
            self.nuclear_2d_num_ssr = 3000
399
400
401
    def on_deactivate(self, e):
402
        """ Deactivate the module properly.
403
404
        @param object e: Fysom.event object from Fysom class. A more detailed
405
                         explanation can be found in the method activation.
406
        """
407
        self._statusVariables['optimize_pos'] =  self._optimize_pos
408
        self._statusVariables['fluorescence_integration_time'] =  self.fluorescence_integration_time
409
410
        self._statusVariables['odmr_2d_low_center_freq'] =  self.odmr_2d_low_center_freq
411
        self._statusVariables['odmr_2d_low_step_freq'] =  self.odmr_2d_low_step_freq
412
        self._statusVariables['odmr_2d_low_range_freq'] =  self.odmr_2d_low_range_freq
413
        self._statusVariables['odmr_2d_low_power'] =  self.odmr_2d_low_power
414
        self._statusVariables['odmr_2d_low_runtime'] =  self.odmr_2d_low_runtime
415
        self._statusVariables['odmr_2d_low_fitfunction'] =  self.odmr_2d_low_fitfunction
416
417
        self._statusVariables['odmr_2d_high_center_freq'] =  self.odmr_2d_high_center_freq
418
        self._statusVariables['odmr_2d_high_step_freq'] =  self.odmr_2d_high_step_freq
419
        self._statusVariables['odmr_2d_high_range_freq'] =  self.odmr_2d_high_range_freq
420
        self._statusVariables['odmr_2d_high_power'] =  self.odmr_2d_high_power
421
        self._statusVariables['odmr_2d_high_runtime'] =  self.odmr_2d_high_runtime
422
        self._statusVariables['odmr_2d_high_fitfunction'] =  self.odmr_2d_high_fitfunction
423
        self._statusVariables['odmr_2d_save_after_measure'] =  self.odmr_2d_save_after_measure
424
        self._statusVariables['odmr_2d_peak_axis0_move_ratio'] =  self.odmr_2d_peak_axis0_move_ratio
425
        self._statusVariables['odmr_2d_peak_axis1_move_ratio'] =  self.odmr_2d_peak_axis1_move_ratio
426
427
        self._statusVariables['nuclear_2d_rabi_periode'] =  self.nuclear_2d_rabi_periode
428
        self._statusVariables['nuclear_2d_mw_freq'] =  self.nuclear_2d_mw_freq
429
        self._statusVariables['nuclear_2d_mw_channel'] =  self.nuclear_2d_mw_channel
430
        self._statusVariables['nuclear_2d_mw_power'] =  self.nuclear_2d_mw_power
431
        self._statusVariables['nuclear_2d_laser_time'] =  self.nuclear_2d_laser_time
432
        self._statusVariables['nuclear_2d_laser_channel'] =  self.nuclear_2d_laser_channel
433
        self._statusVariables['nuclear_2d_detect_channel'] =  self.nuclear_2d_detect_channel
434
        self._statusVariables['nuclear_2d_idle_time'] =  self.nuclear_2d_idle_time
435
        self._statusVariables['nuclear_2d_reps_within_ssr'] =  self.nuclear_2d_reps_within_ssr
436
        self._statusVariables['nuclear_2d_num_ssr'] =  self.nuclear_2d_num_ssr
437
438
    def get_hardware_constraints(self):
439
        """ Retrieve the hardware constraints.
440
441
        @return dict: dict with constraints for the magnet hardware. The keys
442
                      are the labels for the axis and the items are again dicts
443
                      which contain all the limiting parameters.
444
        """
445
446
        return self._magnet_device.get_constraints()
447
448
    def move_rel(self, param_dict):
449
        """ Move the specified axis in the param_dict relative with an assigned
450
            value.
451
452
        @param dict param_dict: dictionary, which passes all the relevant
453
                                parameters. E.g., for a movement of an axis
454
                                labeled with 'x' by 23 the dict should have the
455
                                form:
456
                                    param_dict = { 'x' : 23 }
457
        """
458
459
        # self._magnet_device.move_rel(param_dict)
460
        # start_pos = self.get_pos(list(param_dict))
461
        # end_pos = dict()
462
        #
463
        # for axis_name in param_dict:
464
        #     end_pos[axis_name] = start_pos[axis_name] + param_dict[axis_name]
465
466
        # if the magnet is moving, then the move_rel command will be neglected.
467
        status_dict = self.get_status(list(param_dict))
468
        for axis_name in status_dict:
469
            if status_dict[axis_name][0] != 0:
470
                return
471
472
        self.sigMoveRel.emit(param_dict)
473
        # self._check_position_reached_loop(start_pos, end_pos)
474
        self.sigPosChanged.emit(param_dict)
475
476
477
    def get_pos(self, param_list=None):
478
        """ Gets current position of the stage.
479
480
        @param list param_list: optional, if a specific position of an axis
481
                                is desired, then the labels of the needed
482
                                axis should be passed as the param_list.
483
                                If nothing is passed, then from each axis the
484
                                position is asked.
485
486
        @return dict: with keys being the axis labels and item the current
487
                      position.
488
        """
489
490
        pos_dict = self._magnet_device.get_pos(param_list)
491
        return pos_dict
492
493
    def get_status(self, param_list=None):
494
        """ Get the status of the position
495
496
        @param list param_list: optional, if a specific status of an axis
497
                                is desired, then the labels of the needed
498
                                axis should be passed in the param_list.
499
                                If nothing is passed, then from each axis the
500
                                status is asked.
501
502
        @return dict: with the axis label as key and  a tuple of a status
503
                     number and a status dict as the item.
504
        """
505
        status = self._magnet_device.get_status(param_list)
506
        return status
507
508
    def move_abs(self, param_dict):
509
        """ Moves stage to absolute position (absolute movement)
510
511
        @param dict param_dict: dictionary, which passes all the relevant
512
                                parameters, which should be changed. Usage:
513
                                 {'axis_label': <a-value>}.
514
                                 'axis_label' must correspond to a label given
515
                                 to one of the axis.
516
        """
517
        # self._magnet_device.move_abs(param_dict)
518
        start_pos = self.get_pos(list(param_dict))
519
        self.sigMoveAbs.emit(param_dict)
520
521
        # self._check_position_reached_loop(start_pos, param_dict)
522
523
        self.sigPosChanged.emit(param_dict)
524
525
    def stop_movement(self):
526
        """ Stops movement of the stage. """
527
        self._stop_measure = True
528
        self.sigAbort.emit()
529
        # self._magnet_device.abort()
530
531
532
    def set_velocity(self, param_dict=None):
533
        """ Write new value for velocity.
534
535
        @param dict param_dict: dictionary, which passes all the relevant
536
                                parameters, which should be changed. Usage:
537
                                 {'axis_label': <the-velocity-value>}.
538
                                 'axis_label' must correspond to a label given
539
                                 to one of the axis.
540
        """
541
        self._magnet_device.set_velocity(param_dict)
542
543
544
545
    def _create_1d_pathway(self, axis_name, axis_range, axis_step, axis_vel):
546
        """  Create a path along with the magnet should move with one axis
547
548
        @param str axis_name:
549
        @param float axis_range:
550
        @param float axis_step:
551
552
        @return:
553
554
        Here you can also create fancy 1D pathways, not only linear but also
555
        in any kind on nonlinear fashion.
556
        """
557
        pass
558
559
    def _create_2d_pathway(self, axis0_name, axis0_range, axis0_step,
560
                           axis1_name, axis1_range, axis1_step, init_pos,
561
                           axis0_vel=None, axis1_vel=None):
562
        """ Create a path along with the magnet should move.
563
564
        @param str axis0_name:
565
        @param float axis0_range:
566
        @param float axis0_step:
567
        @param str axis1_name:
568
        @param float axis1_range:
569
        @param float axis1_step:
570
571
        @return array: 1D np.array, which has dictionary as entries. In this
572
                       dictionary, it will be specified, how the magnet is going
573
                       from the present point to the next.
574
575
        That should be quite a general function, which maps from a given matrix
576
        and axes information a 2D array into a 1D path with steps being the
577
        relative movements.
578
579
        All kind of standard and fancy pathways through the array should be
580
        implemented here!
581
        The movement is not restricted to relative movements!
582
        The entry dicts have the following structure:
583
584
           pathway =  [ dict1, dict2, dict3, ...]
585
586
        whereas the dictionary can only have one or two key entries:
587
             dict1[axis0_name] = {'move_rel': 123, 'move_vel': 3 }
588
             dict1[axis1_name] = {'move_abs': 29.5}
589
590
        Note that the entries may either have a relative OR an absolute movement!
591
        Never both! Absolute movement will be taken always before relative
592
        movement. Moreover you can specify in each movement step the velocity
593
        and the acceleration of the movement.
594
        E.g. if no velocity is specified, then nothing will be changed in terms
595
        of speed during the move.
596
        """
597
598
        # calculate number of steps (those are NOT the number of points!)
599
        axis0_num_of_steps = int(axis0_range//axis0_step)
600
        axis1_num_of_steps = int(axis1_range//axis1_step)
601
602
        # make an array of movement steps
603
        axis0_steparray = [axis0_step] * axis0_num_of_steps
604
        axis1_steparray = [axis1_step] * axis1_num_of_steps
605
606
        pathway = []
607
608
        #FIXME: create these path modes:
609
        if self.curr_2d_pathway_mode == 'spiral-in':
610
            self.log.error('The pathway creation method "{0}" through the '
611
                    'matrix is not implemented yet!\nReturn an empty '
612
                    'patharray.'.format(self.curr_2d_pathway_mode))
613
            return [], []
614
615
        elif self.curr_2d_pathway_mode == 'spiral-out':
616
            self.log.error('The pathway creation method "{0}" through the '
617
                    'matrix is not implemented yet!\nReturn an empty '
618
                    'patharray.'.format(self.curr_2d_pathway_mode))
619
            return [], []
620
621
        elif self.curr_2d_pathway_mode == 'diagonal-snake-wise':
622
            self.log.error('The pathway creation method "{0}" through the '
623
                    'matrix is not implemented yet!\nReturn an empty '
624
                    'patharray.'.format(self.current_2d_pathway_mode))
625
            return [], []
626
627
        elif self.curr_2d_pathway_mode == 'selected-points':
628
            self.log.error('The pathway creation method "{0}" through the '
629
                    'matrix is not implemented yet!\nReturn an empty '
630
                    'patharray.'.format(self.current_2d_pathway_mode))
631
            return [], []
632
633
        # choose the snake-wise as default for now.
634
        else:
635
636
            # create a snake-wise stepping procedure through the matrix:
637
            axis0_pos = round(init_pos[axis0_name] - axis0_range/2, 7)
638
            axis1_pos = round(init_pos[axis1_name] - axis1_range/2, 7)
639
640
            # append again so that the for loop later will run once again
641
            # through the axis0 array but the last value of axis1_steparray will
642
            # not be performed.
643
            axis1_steparray.append(axis1_num_of_steps)
644
645
            # step_config is the dict containing the commands for one pathway
646
            # entry. Move at first to start position:
647
            step_config = dict()
648
649
            if axis0_vel is None:
650
                step_config[axis0_name] = {'move_abs': axis0_pos}
651
            else:
652
                step_config[axis0_name] = {'move_abs': axis0_pos, 'move_vel': axis0_vel}
653
654
            if axis1_vel is None:
655
                step_config[axis1_name] = {'move_abs': axis1_pos}
656
            else:
657
                step_config[axis1_name] = {'move_abs': axis1_pos, 'move_vel': axis1_vel}
658
659
            pathway.append(step_config)
660
661
            path_index = 0
662
663
            # these indices should be used to facilitate the mapping to a 2D
664
            # array, since the
665
            axis0_index = 0
666
            axis1_index = 0
667
668
            # that is a map to transform a pathway index value back to an
669
            # absolute position and index. That will be important for saving the
670
            # data corresponding to a certain path_index value.
671
            back_map = dict()
672
            back_map[path_index] = {axis0_name: axis0_pos,
673
                                    axis1_name: axis1_pos,
674
                                    'index': (axis0_index, axis1_index)}
675
676
            path_index += 1
677
            # axis0_index += 1
678
679
            go_pos_dir = True
680
            for step_in_axis1 in axis1_steparray:
681
682
                if go_pos_dir:
683
                    go_pos_dir = False
684
                    direction = +1
685
                else:
686
                    go_pos_dir = True
687
                    direction = -1
688
689
                for step_in_axis0 in axis0_steparray:
690
691
                    axis0_index += direction
692
                    # make move along axis0:
693
                    step_config = dict()
694
695
                    # relative movement:
696
                    # step_config[axis0_name] = {'move_rel': direction*step_in_axis0}
697
698
                    # absolute movement:
699
                    axis0_pos =round(axis0_pos + direction*step_in_axis0, 7)
700
701
                    # if axis0_vel is None:
702
                    #     step_config[axis0_name] = {'move_abs': axis0_pos}
703
                    #     step_config[axis1_name] = {'move_abs': axis1_pos}
704
                    # else:
705
                    #     step_config[axis0_name] = {'move_abs': axis0_pos,
706
                    #                                'move_vel': axis0_vel}
707 View Code Duplication
                    if axis1_vel is None and axis0_vel is None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
708
                        step_config[axis0_name] = {'move_abs': axis0_pos}
709
                        step_config[axis1_name] = {'move_abs': axis1_pos}
710
                    else:
711
                        step_config[axis0_name] = {'move_abs': axis0_pos}
712
                        step_config[axis1_name] = {'move_abs': axis1_pos}
713
714
                        if axis0_vel is not None:
715
                            step_config[axis0_name] = {'move_abs': axis0_pos, 'move_vel': axis0_vel}
716
717
                        if axis1_vel is not None:
718
                            step_config[axis1_name] = {'move_abs': axis1_pos, 'move_vel': axis1_vel}
719
720
                    # append to the pathway
721
                    pathway.append(step_config)
722
                    back_map[path_index] = {axis0_name: axis0_pos,
723
                                            axis1_name: axis1_pos,
724
                                            'index': (axis0_index, axis1_index)}
725
                    path_index += 1
726
727
                if (axis1_index+1) >= len(axis1_steparray):
728
                    break
729
730
                # make a move along axis1:
731
                step_config = dict()
732
733
                # relative movement:
734
                # step_config[axis1_name] = {'move_rel' : step_in_axis1}
735
736
                # absolute movement:
737
                axis1_pos = round(axis1_pos + step_in_axis1, 7)
738
739 View Code Duplication
                if axis1_vel is None and axis0_vel is None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
740
                    step_config[axis0_name] = {'move_abs': axis0_pos}
741
                    step_config[axis1_name] = {'move_abs': axis1_pos}
742
                else:
743
                    step_config[axis0_name] = {'move_abs': axis0_pos}
744
                    step_config[axis1_name] = {'move_abs': axis1_pos}
745
746
                    if axis0_vel is not None:
747
                        step_config[axis0_name] = {'move_abs': axis0_pos, 'move_vel': axis0_vel}
748
749
                    if axis1_vel is not None:
750
                        step_config[axis1_name] = {'move_abs': axis1_pos, 'move_vel': axis1_vel}
751
752
                pathway.append(step_config)
753
                axis1_index += 1
754
                back_map[path_index] = {axis0_name: axis0_pos,
755
                                        axis1_name: axis1_pos,
756
                                        'index': (axis0_index, axis1_index)}
757
                path_index += 1
758
759
760
761
        return pathway, back_map
762
763
764
    def _create_2d_cont_pathway(self, pathway):
765
766
        # go through the passed 1D path and reduce the whole movement just to
767
        # corner points
768
769
        pathway_cont = dict()
770
771
        return pathway_cont
772
773
    def _prepare_2d_graph(self, axis0_start, axis0_range, axis0_step,
774
                          axis1_start, axis1_range, axis1_step):
775
        # set up a matrix where measurement points are save to
776
        # general method to prepare 2d images, and their axes.
777
778
        # that is for the matrix image. +1 because number of points and not
779
        # number of steps are needed:
780
        num_points_axis0 = (axis0_range//axis0_step) + 1
781
        num_points_axis1 = (axis1_range//axis1_step) + 1
782
        matrix = np.zeros((num_points_axis0, num_points_axis1))
783
784
        # data axis0:
785
786
        data_axis0 = np.arange(axis0_start, axis0_start + ((axis0_range//axis0_step)+1)*axis0_step, axis0_step)
787
788
        # data axis1:
789
        data_axis1 = np.arange(axis1_start, axis1_start + ((axis1_range//axis1_step)+1)*axis1_step, axis1_step)
790
791
        return matrix, data_axis0, data_axis1
792
793
794
795
796
    def _prepare_1d_graph(self, axis_range, axis_step):
797
        pass
798
799
800
801
802
803
    def start_1d_alignment(self, axis_name, axis_range, axis_step, axis_vel,
804
                                 stepwise_meas=True, continue_meas=False):
805
806
807
        # actual measurement routine, which is called to start the measurement
808
809
810
        if not continue_meas:
811
812
            # to perform the '_do_measure_after_stop' routine from the beginning
813
            # (which means e.g. an optimize pos)
814
815
            self._prepare_1d_graph()
816
817
            self._pathway = self._create_1d_pathway()
818
819
            if stepwise_meas:
820
                # just make it to an empty dict
821
                self._pathway_cont = dict()
822
823
            else:
824
                # create from the path_points the continoues points
825
                self._pathway_cont = self._create_1d_cont_pathway(self._pathway)
826
827
        else:
828
            # tell all the connected instances that measurement is continuing:
829
            self.sigMeasurementContinued.emit()
830
831
        # run at first the _move_to_curr_pathway_index method to go to the
832
        # index position:
833
        self._sigInitializeMeasPos.emit(stepwise_meas)
834
835
836
837
    def start_2d_alignment(self, axis0_name, axis0_range, axis0_step,
838
                                 axis1_name, axis1_range, axis1_step,
839
                                 axis0_vel=None, axis1_vel=None,
840
                                 stepwise_meas=True, continue_meas=False):
841
842
        # before starting the measurement you should convince yourself that the
843
        # passed traveling range is possible. Otherwise the measurement will be
844
        # aborted and an error is raised.
845
        #
846
        # actual measurement routine, which is called to start the measurement
847
848
        self._start_measurement_time = datetime.datetime.now()
849
        self._stop_measurement_time = None
850
851
        self._stop_measure = False
852
853
        self._axis0_name = axis0_name
854
        self._axis1_name = axis1_name
855
856
        # save only the position of the axis, which are going to be moved
857
        # during alignment, the return will be a dict!
858
        self._saved_pos_before_align = self.get_pos([axis0_name, axis1_name])
859
860
861
        if not continue_meas:
862
863
            self.sigMeasurementStarted.emit()
864
865
            # the index, which run through the _pathway list and selects the
866
            # current measurement point
867
            self._pathway_index = 0
868
869
            self._pathway, self._backmap = self._create_2d_pathway(axis0_name, axis0_range,
870
                                                                   axis0_step, axis1_name, axis1_range,
871
                                                                   axis1_step, self._saved_pos_before_align,
872
                                                                   axis0_vel, axis1_vel)
873
874
            # determine the start point, either relative or absolute!
875
            # Now the absolute position will be used:
876
            axis0_start = self._backmap[0][axis0_name]
877
            axis1_start = self._backmap[0][axis1_name]
878
879
            self._2D_data_matrix, \
880
            self._2D_axis0_data,\
881
            self._2D_axis1_data = self._prepare_2d_graph(axis0_start, axis0_range,
882
                                                      axis0_step, axis1_start,
883
                                                      axis1_range, axis1_step)
884
885
            self._2D_add_data_matrix = np.zeros(shape=np.shape(self._2D_data_matrix), dtype=object)
886
887
888
            if stepwise_meas:
889
                # just make it to an empty dict
890
                self._pathway_cont = dict()
891
892
            else:
893
                # create from the path_points the continuous points
894
                self._pathway_cont = self._create_2d_cont_pathway(self._pathway)
895
896
        # TODO: include here another mode, where a new defined pathway can be
897
        #       created, along which the measurement should be repeated.
898
        #       You have to follow the procedure:
899
        #           - Create for continuing the measurement just a proper
900
        #             pathway and a proper back_map in self._create_2d_pathway,
901
        #       => Then the whole measurement can be just run with the new
902
        #          pathway and back_map, and you do not have to adjust other
903
        #          things.
904
905
        else:
906
            # tell all the connected instances that measurement is continuing:
907
            self.sigMeasurementContinued.emit()
908
909
        # run at first the _move_to_curr_pathway_index method to go to the
910
        # index position:
911
        self._sigInitializeMeasPos.emit(stepwise_meas)
912
913
914
    def _move_to_curr_pathway_index(self, stepwise_meas):
915
916
        # move to the passed pathway index in the list _pathway and start the
917
        # proper loop for that:
918
919
        # move absolute to the index position, which is currently given
920
921
        move_dict_vel, \
922
        move_dict_abs, \
923
        move_dict_rel = self._move_to_index(self._pathway_index, self._pathway)
924
925
        self.set_velocity(move_dict_vel)
926
        self.move_abs(move_dict_abs)
927
        # self.move_rel(move_dict_rel)
928
929
930
931
        # this function will return to this function if position is reached:
932
        start_pos = self._saved_pos_before_align
933
        end_pos = dict()
934
        for axis_name in self._saved_pos_before_align:
935
            end_pos[axis_name] = self._backmap[self._pathway_index][axis_name]
936
937
        while self._check_is_moving():
938
            time.sleep(self._checktime)
939
            self.log.debug("Went into while loop in _move_to_curr_pathway_index")
940
941
        self.log.debug("(first movement) magnet moving ? {0}".format(self._check_is_moving()))
942
943
944
        if stepwise_meas:
945
            # start the Stepwise alignment loop body self._stepwise_loop_body:
946
            self._sigStepwiseAlignmentNext.emit()
947
        else:
948
            # start the continuous alignment loop body self._continuous_loop_body:
949
            self._sigContinuousAlignmentNext.emit()
950
951
952
    def _stepwise_loop_body(self):
953
        """ Go one by one through the created path
954
        @return:
955
        The loop body goes through the 1D array
956
        """
957
958
        if self._stop_measure:
959
            return
960
961
        self._do_premeasurement_proc()
962
        pos = self._magnet_device.get_pos()
963
        self.log.debug("Current magnetic field before alignment measurement rho:{0}".format(pos['rho'])
964
                       + "phi: {0}".format(pos['phi']) + "theta: {0}".format(pos['theta']))
965
        # perform here one of the chosen alignment measurements
966
        meas_val, add_meas_val = self._do_alignment_measurement()
967
968
        # set the measurement point to the proper array and the proper position:
969
        # save also all additional measurement information, which have been
970
        # done during the measurement in add_meas_val.
971
        self._set_meas_point(meas_val, add_meas_val, self._pathway_index, self._backmap)
972
973
        # increase the index
974
        self._pathway_index += 1
975
976
        if (self._pathway_index) < len(self._pathway):
977
978
            #
979
            self._do_postmeasurement_proc()
980
            move_dict_vel, \
981
            move_dict_abs, \
982
            move_dict_rel = self._move_to_index(self._pathway_index, self._pathway)
983
984
            self.set_velocity(move_dict_vel)
985
            self.move_abs(move_dict_abs)
986
987
            # this function will return to this function if position is reached:
988
            start_pos = dict()
989
            end_pos = dict()
990
            for axis_name in self._saved_pos_before_align:
991
                start_pos[axis_name] = self._backmap[self._pathway_index - 1][axis_name]
992
                end_pos[axis_name] = self._backmap[self._pathway_index][axis_name]
993
994
            while self._check_is_moving():
995
                time.sleep(self._checktime)
996
                self.log.debug("Went into while loop in stepwise_loop_body")
997
998
            self.log.debug("stepwise_loop_body reports magnet moving ? {0}".format(self._check_is_moving()))
999
1000
            # rerun this loop again
1001
            self._sigStepwiseAlignmentNext.emit()
1002
1003
        else:
1004
            self._end_alignment_procedure()
1005
1006
1007
    def _continuous_loop_body(self):
1008
        """ Go as much as possible in one direction
1009
1010
        @return:
1011
1012
        The loop body goes through the 1D array
1013
        """
1014
        pass
1015
1016
1017
1018
    def stop_alignment(self):
1019
        """ Stops any kind of ongoing alignment measurement by setting a flag.
1020
        """
1021
1022
        self._stop_measure = True
1023
1024
        # abort the movement or check whether immediate abortion of measurement
1025
        # was needed.
1026
1027
        # check whether an alignment measurement is currently going on and send
1028
        # a signal to stop that.
1029
1030
    def _end_alignment_procedure(self):
1031
1032
        # 1 check if magnet is moving and stop it
1033
1034
        # move back to the first position before the alignment has started:
1035
        #
1036
        constraints = self.get_hardware_constraints()
1037
1038
        last_pos = dict()
1039
        for axis_name in self._saved_pos_before_align:
1040
            last_pos[axis_name] = self._backmap[self._pathway_index-1][axis_name]
1041
1042
        self.move_abs(self._saved_pos_before_align)
1043
1044
        while self._check_is_moving():
1045
            time.sleep(self._checktime)
1046
1047
        self.sigMeasurementFinished.emit()
1048
1049
        self._pathway_index = 0
1050
        self._stop_measurement_time = datetime.datetime.now()
1051
1052
        self.log.info('Alignment Complete!')
1053
1054
        pass
1055
1056
1057
    def _check_position_reached_loop(self, start_pos_dict, end_pos_dict):
1058
        """ Perform just a while loop, which checks everytime the conditions
1059
1060
        @param dict start_pos_dict: the position in this dictionary must be
1061
                                    absolute positions!
1062
        @param dict end_pos_dict:
1063
        @param float checktime: the checktime in seconds
1064
1065
        @return:
1066
1067
        Whenever the magnet has passed 95% of the way, the method will return.
1068
1069
        Check also whether the difference in position increases again, and if so
1070
        stop the measurement and raise an error, since either the velocity was
1071
        too fast or the magnet does not move further.
1072
        """
1073
1074
1075
        distance_init = 0.0
1076
        constraints = self.get_hardware_constraints()
1077
        minimal_distance = 0.0
1078
        for axis_label in start_pos_dict:
1079
            distance_init = (end_pos_dict[axis_label] - start_pos_dict[axis_label])**2
1080
            minimal_distance = minimal_distance + (constraints[axis_label]['pos_step'])**2
1081
        distance_init = np.sqrt(distance_init)
1082
        minimal_distance = np.sqrt(minimal_distance)
1083
1084
        # take 97% distance tolerance:
1085
        distance_tolerance = 0.03 * distance_init
1086
1087
        current_dist = 0.0
1088
1089
        while True:
1090
            time.sleep(self._checktime)
1091
1092
            curr_pos = self.get_pos(list(end_pos_dict))
1093
1094
            for axis_label in start_pos_dict:
1095
                current_dist = (end_pos_dict[axis_label] - curr_pos[axis_label])**2
1096
1097
            current_dist = np.sqrt(current_dist)
1098
1099
            self.sigPosChanged.emit(curr_pos)
1100
1101
            if (current_dist <= distance_tolerance) or (current_dist <= minimal_distance) or self._stop_measure:
1102
                self.sigPosReached.emit()
1103
1104
                break
1105
1106
        #return either pos reached signal of check position
1107
1108
    def _check_is_moving(self):
1109
        """
1110
1111
        @return bool: True indicates the magnet is moving, False the magnet stopped movement
1112
        """
1113
        # get axis names
1114
        axes = [i for i in self._magnet_device.get_constraints()]
1115
        state = self._magnet_device.get_status()
1116
1117
        return (state[axes[0]][0] or state[axes[1]][0] or state[axes[2]][0]) is (1 or -1)
1118
1119
1120
    def _set_meas_point(self, meas_val, add_meas_val, pathway_index, back_map):
1121
1122
        # is it point for 1d meas or 2d meas?
1123
1124
        # map the point back to the position in the measurement array
1125
        index_array = back_map[pathway_index]['index']
1126
1127
        # then index_array is actually no array, but just a number. That is the
1128
        # 1D case:
1129
        if np.shape(index_array) == ():
1130
1131
            #FIXME: Implement the 1D save
1132
1133
            self.sig1DMatrixChanged.emit()
1134
1135
        elif np.shape(index_array)[0] == 2:
1136
1137
            self._2D_data_matrix[index_array] = meas_val
1138
            self._2D_add_data_matrix[index_array] = add_meas_val
1139
1140
            # self.log.debug('Data "{0}", saved at intex "{1}"'.format(meas_val, index_array))
1141
1142
            self.sig2DMatrixChanged.emit()
1143
1144
        elif np.shape(index_array)[0] == 3:
1145
1146
1147
            #FIXME: Implement the 3D save
1148
            self.sig3DMatrixChanged.emit()
1149
        else:
1150
            self.log.error('The measurement point "{0}" could not be set in '
1151
                    'the _set_meas_point routine, since either a 1D, a 2D or '
1152
                    'a 3D index array was expected, but an index array "{1}" '
1153
                    'was given in the passed back_map. Correct the '
1154
                    'back_map creation in the routine '
1155
                    '_create_2d_pathway!'.format(meas_val, index_array))
1156
1157
1158
1159
1160
        pass
1161
1162
    def _do_premeasurement_proc(self):
1163
        # do a selected pre measurement procedure, like e.g. optimize position.
1164
1165
1166
        # first attempt of an optimizer usage:
1167
        if self._optimize_pos:
1168
            self._do_optimize_pos()
1169
1170
        return
1171
1172
    def _do_optimize_pos(self):
1173
1174
        curr_pos = self._confocal_logic.get_position()
1175
1176
        self._optimizer_logic.start_refocus(curr_pos, caller_tag='magnet_logic')
1177
1178
        # check just the state of the optimizer
1179
        while self._optimizer_logic.getState() != 'idle' and not self._stop_measure:
1180
            time.sleep(0.5)
1181
1182
        # use the position to move the scanner
1183
        self._confocal_logic.set_position('magnet_logic',
1184
                                          self._optimizer_logic.optim_pos_x,
1185
                                          self._optimizer_logic.optim_pos_y,
1186
                                          self._optimizer_logic.optim_pos_z)
1187
1188
    def _do_alignment_measurement(self):
1189
        """ That is the main method which contains all functions with measurement routines.
1190
1191
        Each measurement routine has to output the measurement value, but can
1192
        also provide a dictionary with additional measurement parameters, which
1193
        have been measured either as a pre-requisition for the measurement or
1194
        are results of the measurement.
1195
1196
        Save each measured value as an item to a keyword string, i.e.
1197
            {'ODMR frequency (MHz)': <the_parameter>, ...}
1198
        The save routine will handle the additional information and save them
1199
        properly.
1200
1201
1202
        @return tuple(float, dict): the measured value is of type float and the
1203
                                    additional parameters are saved in a
1204
                                    dictionary form.
1205
        """
1206
1207
        # perform here one of the selected alignment measurements and return to
1208
        # the loop body the measured values.
1209
1210
1211
        # self.alignment_methods = ['fluorescence_pointwise',
1212
        #                           'fluorescence_continuous',
1213
        #                           'odmr_splitting',
1214
        #                           'odmr_hyperfine_splitting',
1215
        #                           'nuclear_spin_measurement']
1216
1217
        if self.curr_alignment_method == '2d_fluorescence':
1218
            data, add_data = self._perform_fluorescence_measure()
1219
1220
        elif self.curr_alignment_method == '2d_odmr':
1221
            if self.odmr_2d_single_trans:
1222
                data, add_data = self._perform_single_trans_contrast_measure()
1223
            else:
1224
                data, add_data = self._perform_odmr_measure()
1225
1226
        elif self.curr_alignment_method == '2d_nuclear':
1227
            data, add_data = self._perform_nuclear_measure()
1228
        # data, add_data = self._perform_odmr_measure(11100e6, 1e6, 11200e6, 5, 10, 'Lorentzian', False,'')
1229
1230
1231
        return data, add_data
1232
1233
1234
    def _perform_fluorescence_measure(self):
1235
1236
        #FIXME: that should be run through the TaskRunner! Implement the call
1237
        #       by not using this connection!
1238
1239
        if self._counter_logic.get_counting_mode != 'continuous':
1240
            self._counter_logic.set_counting_mode(mode='continuous')
1241
1242
        self._counter_logic.start_saving()
1243
        time.sleep(self.fluorescence_integration_time)
1244
        data_array, parameters = self._counter_logic.save_data(to_file=False)
1245
1246
        data_array = np.array(data_array)[:, 1]
1247
1248
        return data_array.mean(), parameters
1249
1250
    def _perform_odmr_measure(self):
1251
        """ Perform the odmr measurement.
1252
1253
        @return:
1254
        """
1255
1256
        store_dict = {}
1257
1258
        # optimize at first the position:
1259
        self._do_optimize_pos()
1260
1261
1262
        # correct the ODMR alignment the shift of the ODMR lines due to movement
1263
        # in axis0 and axis1, therefore find out how much you will move in each
1264
        # distance:
1265
        if self._pathway_index == 0:
1266
            axis0_pos_start = self._saved_pos_before_align[self._axis0_name]
1267
            axis0_pos_stop = self._backmap[self._pathway_index][self._axis0_name]
1268
1269
            axis1_pos_start = self._saved_pos_before_align[self._axis1_name]
1270
            axis1_pos_stop = self._backmap[self._pathway_index][self._axis1_name]
1271
        else:
1272
            axis0_pos_start = self._backmap[self._pathway_index-1][self._axis0_name]
1273
            axis0_pos_stop = self._backmap[self._pathway_index][self._axis0_name]
1274
1275
            axis1_pos_start = self._backmap[self._pathway_index-1][self._axis1_name]
1276
            axis1_pos_stop = self._backmap[self._pathway_index][self._axis1_name]
1277
1278
        # that is the current distance the magnet has moved:
1279 View Code Duplication
        axis0_move = axis0_pos_stop - axis0_pos_start
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1280
        axis1_move = axis1_pos_stop - axis1_pos_start
1281
        print('axis0_move', axis0_move, 'axis1_move', axis1_move)
1282
1283
        # in essence, get the last measurement value for odmr freq and calculate
1284
        # the odmr peak shift for axis0 and axis1 based on the already measured
1285
        # peaks and update the values odmr_2d_peak_axis0_move_ratio and
1286
        # odmr_2d_peak_axis1_move_ratio:
1287
        if self._pathway_index > 1:
1288
            # in essence, get the last measurement value for odmr freq:
1289 View Code Duplication
            if self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']].get('low_freq_Frequency') is not None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1290
                low_odmr_freq1 = self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']]['low_freq_Frequency']['value']*1e6
1291
                low_odmr_freq2 = self._2D_add_data_matrix[self._backmap[self._pathway_index-2]['index']]['low_freq_Frequency']['value']*1e6
1292
            elif self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']].get('low_freq_Freq. 1') is not None:
1293
                low_odmr_freq1 = self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']]['low_freq_Freq. 1']['value']*1e6
1294
                low_odmr_freq2 = self._2D_add_data_matrix[self._backmap[self._pathway_index-2]['index']]['low_freq_Freq. 1']['value']*1e6
1295
            else:
1296
                self.log.error('No previous saved lower odmr freq found in '
1297
                        'ODMR alignment data! Cannot do the ODMR Alignment!')
1298
1299
            if self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']].get('high_freq_Frequency') is not None:
1300
                high_odmr_freq1 = self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']]['high_freq_Frequency']['value']*1e6
1301
                high_odmr_freq2 = self._2D_add_data_matrix[self._backmap[self._pathway_index-2]['index']]['high_freq_Frequency']['value']*1e6
1302
            elif self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']].get('high_freq_Freq. 1') is not None:
1303
                high_odmr_freq1 = self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']]['high_freq_Freq. 1']['value']*1e6
1304
                high_odmr_freq2 = self._2D_add_data_matrix[self._backmap[self._pathway_index-2]['index']]['high_freq_Freq. 1']['value']*1e6
1305
            else:
1306
                self.log.error('No previous saved higher odmr freq found in '
1307
                        'ODMR alignment data! Cannot do the ODMR Alignment!')
1308
1309
            # only if there was a non zero movement, the if make sense to
1310
            # calculate the shift for either the axis0 or axis1.
1311
            # BE AWARE THAT FOR A MOVEMENT IN AXIS0 AND AXIS1 AT THE SAME TIME
1312
            # NO PROPER CALCULATION OF THE OMDR LINES CAN BE PROVIDED!
1313
            if not np.isclose(axis0_move, 0.0):
1314
                # update the correction ratio:
1315
                low_peak_axis0_move_ratio = (low_odmr_freq1 - low_odmr_freq2)/axis0_move
1316
                high_peak_axis0_move_ratio = (high_odmr_freq1 - high_odmr_freq2)/axis0_move
1317
1318
                # print('low_odmr_freq2', low_odmr_freq2, 'low_odmr_freq1', low_odmr_freq1)
1319
                # print('high_odmr_freq2', high_odmr_freq2, 'high_odmr_freq1', high_odmr_freq1)
1320
1321
                # calculate the average shift of the odmr lines for the lower
1322
                # and the upper transition:
1323
                self.odmr_2d_peak_axis0_move_ratio = (low_peak_axis0_move_ratio +high_peak_axis0_move_ratio)/2
1324
1325
                # print('new odmr_2d_peak_axis0_move_ratio', self.odmr_2d_peak_axis0_move_ratio/1e12)
1326
            if not np.isclose(axis1_move, 0.0):
1327
                # update the correction ratio:
1328
                low_peak_axis1_move_ratio = (low_odmr_freq1 - low_odmr_freq2)/axis1_move
1329
                high_peak_axis1_move_ratio = (high_odmr_freq1 - high_odmr_freq2)/axis1_move
1330
1331
                # calculate the average shift of the odmr lines for the lower
1332
                # and the upper transition:
1333
                self.odmr_2d_peak_axis1_move_ratio = (low_peak_axis1_move_ratio + high_peak_axis1_move_ratio)/2
1334
1335
                # print('new odmr_2d_peak_axis1_move_ratio', self.odmr_2d_peak_axis1_move_ratio/1e12)
1336
1337
        # Measurement of the lower transition:
1338
        # -------------------------------------
1339
1340
        freq_shift_low_axis0 = axis0_move * self.odmr_2d_peak_axis0_move_ratio
1341
        freq_shift_low_axis1 = axis1_move * self.odmr_2d_peak_axis1_move_ratio
1342
1343
        # correct here the center freq with the estimated corrections:
1344
        self.odmr_2d_low_center_freq += (freq_shift_low_axis0 + freq_shift_low_axis1)
1345
        # print('self.odmr_2d_low_center_freq',self.odmr_2d_low_center_freq)
1346
1347
        # create a unique nametag for the current measurement:
1348
        name_tag = 'low_trans_index_'+str(self._backmap[self._pathway_index]['index'][0]) \
1349
                   +'_'+ str(self._backmap[self._pathway_index]['index'][1])
1350
1351
        # of course the shift of the ODMR peak is not linear for a movement in
1352
        # axis0 and axis1, but we need just an estimate how to set the boundary
1353
        # conditions for the first scan, since the first scan will move to a
1354
        # start position and then it need to know where to search for the ODMR
1355
        # peak(s).
1356
1357
        # calculate the parameters for the odmr scan:
1358
        low_start_freq = self.odmr_2d_low_center_freq - self.odmr_2d_low_range_freq/2
1359
        low_step_freq = self.odmr_2d_low_step_freq
1360
        low_stop_freq = self.odmr_2d_low_center_freq + self.odmr_2d_low_range_freq/2
1361
1362
        param = self._odmr_logic.perform_odmr_measurement(low_start_freq,
1363
                                                          low_step_freq,
1364
                                                          low_stop_freq,
1365
                                                          self.odmr_2d_low_power,
1366
                                                          self.odmr_2d_low_runtime,
1367
                                                          self.odmr_2d_low_fitfunction,
1368
                                                          self.odmr_2d_save_after_measure,
1369
                                                          name_tag)
1370
1371
        # restructure the output parameters:
1372
        for entry in param:
1373
            store_dict['low_freq_'+str(entry)] = param[entry]
1374
1375
        # extract the frequency meausure:
1376
        if param.get('Frequency') is not None:
1377
            odmr_low_freq_meas = param['Frequency']['value']*1e6
1378
        elif param.get('Freq. 1') is not None:
1379
            odmr_low_freq_meas = param['Freq. 1']['value']*1e6
1380
        else:
1381
            # a default value for testing and debugging:
1382
            odmr_low_freq_meas = 1000e6
1383
1384
        self.odmr_2d_low_center_freq = odmr_low_freq_meas
1385
        # Measurement of the higher transition:
1386
        # -------------------------------------
1387
1388
1389
        freq_shift_high_axis0 = axis0_move * self.odmr_2d_peak_axis0_move_ratio
1390
        freq_shift_high_axis1 = axis1_move * self.odmr_2d_peak_axis1_move_ratio
1391
1392
        # correct here the center freq with the estimated corrections:
1393
        self.odmr_2d_high_center_freq += (freq_shift_high_axis0 + freq_shift_high_axis1)
1394
1395
        # create a unique nametag for the current measurement:
1396
        name_tag = 'high_trans_index_'+str(self._backmap[self._pathway_index]['index'][0]) \
1397
                   +'_'+ str(self._backmap[self._pathway_index]['index'][1])
1398
1399
        # of course the shift of the ODMR peak is not linear for a movement in
1400
        # axis0 and axis1, but we need just an estimate how to set the boundary
1401
        # conditions for the first scan, since the first scan will move to a
1402
        # start position and then it need to know where to search for the ODMR
1403
        # peak(s).
1404
1405
        # calculate the parameters for the odmr scan:
1406
        high_start_freq = self.odmr_2d_high_center_freq - self.odmr_2d_high_range_freq/2
1407
        high_step_freq = self.odmr_2d_high_step_freq
1408
        high_stop_freq = self.odmr_2d_high_center_freq + self.odmr_2d_high_range_freq/2
1409
1410
        param = self._odmr_logic.perform_odmr_measurement(high_start_freq,
1411
                                                          high_step_freq,
1412
                                                          high_stop_freq,
1413
                                                          self.odmr_2d_high_power,
1414
                                                          self.odmr_2d_high_runtime,
1415
                                                          self.odmr_2d_high_fitfunction,
1416
                                                          self.odmr_2d_save_after_measure,
1417
                                                          name_tag)
1418
        # restructure the output parameters:
1419
        for entry in param:
1420
            store_dict['high_freq_'+str(entry)] = param[entry]
1421
1422
        # extract the frequency meausure:
1423
        if param.get('Frequency') is not None:
1424
            odmr_high_freq_meas = param['Frequency']['value']*1e6
1425
        elif param.get('Freq. 1') is not None:
1426
            odmr_high_freq_meas = param['Freq. 1']['value']*1e6
1427
        else:
1428
            # a default value for testing and debugging:
1429
            odmr_high_freq_meas = 2000e6
1430
1431
        # correct the estimated center frequency by the actual measured one.
1432
        self.odmr_2d_high_center_freq = odmr_high_freq_meas
1433
1434
        #FIXME: the normalization is just done for the display to view the
1435
        #       value properly! There is right now a bug in the colorbad
1436
        #       display, which need to be solved.
1437
        diff = (abs(odmr_high_freq_meas - odmr_low_freq_meas)/2)/self.norm
1438
1439
        while self._odmr_logic.getState() != 'idle' and not self._stop_measure:
1440
            time.sleep(0.5)
1441
1442
        return diff, store_dict
1443
1444
    def _perform_single_trans_contrast_measure(self):
1445
        """ Make an ODMR measurement on one single transition and use the
1446
            contrast as a measure.
1447
        """
1448
1449
        store_dict = {}
1450
1451
        # optimize at first the position:
1452
        self._do_optimize_pos()
1453
1454
        # correct the ODMR alignment the shift of the ODMR lines due to movement
1455
        # in axis0 and axis1, therefore find out how much you will move in each
1456
        # distance:
1457
        if self._pathway_index == 0:
1458
            axis0_pos_start = self._saved_pos_before_align[self._axis0_name]
1459
            axis0_pos_stop = self._backmap[self._pathway_index][self._axis0_name]
1460
1461
            axis1_pos_start = self._saved_pos_before_align[self._axis1_name]
1462
            axis1_pos_stop = self._backmap[self._pathway_index][self._axis1_name]
1463
        else:
1464
            axis0_pos_start = self._backmap[self._pathway_index-1][self._axis0_name]
1465
            axis0_pos_stop = self._backmap[self._pathway_index][self._axis0_name]
1466
1467
            axis1_pos_start = self._backmap[self._pathway_index-1][self._axis1_name]
1468
            axis1_pos_stop = self._backmap[self._pathway_index][self._axis1_name]
1469
1470
        # that is the current distance the magnet has moved:
1471 View Code Duplication
        axis0_move = axis0_pos_stop - axis0_pos_start
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1472
        axis1_move = axis1_pos_stop - axis1_pos_start
1473
        # print('axis0_move', axis0_move, 'axis1_move', axis1_move)
1474
1475
        # in essence, get the last measurement value for odmr freq and calculate
1476
        # the odmr peak shift for axis0 and axis1 based on the already measured
1477
        # peaks and update the values odmr_2d_peak_axis0_move_ratio and
1478
        # odmr_2d_peak_axis1_move_ratio:
1479
        if self._pathway_index > 1:
1480
            # in essence, get the last measurement value for odmr freq:
1481
            if self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']].get('Frequency') is not None:
1482
                odmr_freq1 = self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']]['Frequency']['value']*1e6
1483
                odmr_freq2 = self._2D_add_data_matrix[self._backmap[self._pathway_index-2]['index']]['Frequency']['value']*1e6
1484
            elif self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']].get('Freq. 1') is not None:
1485
                odmr_freq1 = self._2D_add_data_matrix[self._backmap[self._pathway_index-1]['index']]['Freq. 1']['value']*1e6
1486
                odmr_freq2 = self._2D_add_data_matrix[self._backmap[self._pathway_index-2]['index']]['Freq. 1']['value']*1e6
1487
            else:
1488
                self.log.error('No previous saved lower odmr freq found in '
1489
                            'ODMR alignment data! Cannot do the ODMR '
1490
                            'Alignment!')
1491
1492
1493
            # only if there was a non zero movement, the if make sense to
1494
            # calculate the shift for either the axis0 or axis1.
1495
            # BE AWARE THAT FOR A MOVEMENT IN AXIS0 AND AXIS1 AT THE SAME TIME
1496
            # NO PROPER CALCULATION OF THE OMDR LINES CAN BE PROVIDED!
1497
            if not np.isclose(axis0_move, 0.0):
1498
                # update the correction ratio:
1499
                peak_axis0_move_ratio = (odmr_freq1 - odmr_freq2)/axis0_move
1500
1501
                # calculate the average shift of the odmr lines for the lower
1502
                # and the upper transition:
1503
                self.odmr_2d_peak_axis0_move_ratio = peak_axis0_move_ratio
1504
1505
                print('new odmr_2d_peak_axis0_move_ratio', self.odmr_2d_peak_axis0_move_ratio/1e12)
1506
            if not np.isclose(axis1_move, 0.0):
1507
                # update the correction ratio:
1508
                peak_axis1_move_ratio = (odmr_freq1 - odmr_freq2)/axis1_move
1509
1510
1511
                # calculate the shift of the odmr lines for the transition:
1512
                self.odmr_2d_peak_axis1_move_ratio = peak_axis1_move_ratio
1513
1514
        # Measurement of one transition:
1515
        # -------------------------------------
1516
1517
        freq_shift_axis0 = axis0_move * self.odmr_2d_peak_axis0_move_ratio
1518
        freq_shift_axis1 = axis1_move * self.odmr_2d_peak_axis1_move_ratio
1519
1520
        # correct here the center freq with the estimated corrections:
1521
        self.odmr_2d_low_center_freq += (freq_shift_axis0 + freq_shift_axis1)
1522
        # print('self.odmr_2d_low_center_freq',self.odmr_2d_low_center_freq)
1523
1524
        # create a unique nametag for the current measurement:
1525
        name_tag = 'trans_index_'+str(self._backmap[self._pathway_index]['index'][0]) \
1526
                   +'_'+ str(self._backmap[self._pathway_index]['index'][1])
1527
1528
        # of course the shift of the ODMR peak is not linear for a movement in
1529
        # axis0 and axis1, but we need just an estimate how to set the boundary
1530
        # conditions for the first scan, since the first scan will move to a
1531
        # start position and then it need to know where to search for the ODMR
1532
        # peak(s).
1533
1534
        # calculate the parameters for the odmr scan:
1535
        start_freq = self.odmr_2d_low_center_freq - self.odmr_2d_low_range_freq/2
1536
        step_freq = self.odmr_2d_low_step_freq
1537
        stop_freq = self.odmr_2d_low_center_freq + self.odmr_2d_low_range_freq/2
1538
1539
        param = self._odmr_logic.perform_odmr_measurement(start_freq,
1540
                                                          step_freq,
1541
                                                          stop_freq,
1542
                                                          self.odmr_2d_low_power,
1543
                                                          self.odmr_2d_low_runtime,
1544
                                                          self.odmr_2d_low_fitfunction,
1545
                                                          self.odmr_2d_save_after_measure,
1546
                                                          name_tag)
1547
1548
        param['ODMR peak/Magnet move ratio axis0'] = self.odmr_2d_peak_axis0_move_ratio
1549
        param['ODMR peak/Magnet move ratio axis1'] = self.odmr_2d_peak_axis1_move_ratio
1550
1551
        # extract the frequency meausure:
1552
        if param.get('Frequency') is not None:
1553
            odmr_freq_meas = param['Frequency']['value']*1e6
1554
            cont_meas = param['Contrast']['value']
1555
        elif param.get('Freq. 1') is not None:
1556
            odmr_freq_meas = param['Freq. 1']['value']*1e6
1557
            cont_meas = param['Contrast 0']['value'] + param['Contrast 1']['value'] + param['Contrast 2']['value']
1558
        else:
1559
            # a default value for testing and debugging:
1560
            odmr_freq_meas = 1000e6
1561
            cont_meas = 0.0
1562
1563
        self.odmr_2d_low_center_freq = odmr_freq_meas
1564
1565
        while self._odmr_logic.getState() != 'idle' and not self._stop_measure:
1566
            time.sleep(0.5)
1567
1568
        return cont_meas, param
1569
1570
    def _perform_nuclear_measure(self):
1571
        """ Make a single shot alignment. """
1572
1573
        # possible parameters for the nuclear measurement:
1574
        # self.nuclear_2d_rabi_periode
1575
        # self.nuclear_2d_mw_freq
1576
        # self.nuclear_2d_mw_channel
1577
        # self.nuclear_2d_mw_power
1578
        # self.nuclear_2d_laser_time
1579
        # self.nuclear_2d_laser_channel
1580
        # self.nuclear_2d_detect_channel
1581
        # self.nuclear_2d_idle_time
1582
        # self.nuclear_2d_reps_within_ssr
1583
        # self.nuclear_2d_num_ssr
1584
        self._load_pulsed_odmr()
1585
        self._pulser_on()
1586
1587
        # self.odmr_2d_low_center_freq
1588
        # self.odmr_2d_low_step_freq
1589
        # self.odmr_2d_low_range_freq
1590
        #
1591
        # self.odmr_2d_low_power,
1592
        # self.odmr_2d_low_runtime,
1593
        # self.odmr_2d_low_fitfunction,
1594
        # self.odmr_2d_save_after_measure,
1595
1596
        # Use the parameters from the ODMR alignment!
1597
        cont_meas, param = self._perform_single_trans_contrast_measure()
1598
1599
        odmr_freq = param['Freq. ' + str(self.nuclear_2d_mw_on_peak-1)]['value']*1e6
1600
1601
        self._set_cw_mw(switch_on=True, freq=odmr_freq, power=self.nuclear_2d_mw_power)
1602
        self._load_nuclear_spin_readout()
1603
        self._pulser_on()
1604
1605
        # Check whether proper mode is active and if not activated that:
1606
        if self._gc_logic.get_counting_mode() != 'finite-gated':
1607
            self._gc_logic.set_counting_mode(mode='finite-gated')
1608
1609
        # Set the count length for the single shot and start counting:
1610
        self._gc_logic.set_count_length(self.nuclear_2d_num_ssr)
1611
1612
        self._run_gated_counter()
1613
1614
        self._set_cw_mw(switch_on=False)
1615
1616
        # try with single poissonian:
1617
1618
1619
        num_bins = (self._gc_logic.countdata.max() - self._gc_logic.countdata.min())
1620
        self._ta_logic.set_num_bins_histogram(num_bins)
1621
1622
        hist_fit_x, hist_fit_y, param_single_poisson = self._ta_logic.do_fit('Poisson')
1623
1624
1625
        param['chi_sqr_single'] = param_single_poisson['chi_sqr']['value']
1626
1627
1628
        # try with normal double poissonian:
1629
1630
        # better performance by starting with half of number of bins:
1631
        num_bins = int((self._gc_logic.countdata.max() - self._gc_logic.countdata.min())/2)
1632
        self._ta_logic.set_num_bins_histogram(num_bins)
1633
1634
        flip_prob, param2 = self._ta_logic.analyze_flip_prob(self._gc_logic.countdata, num_bins)
1635
1636
        # self._pulser_off()
1637
        #
1638
        # self._load_pulsed_odmr()
1639
        # self._pulser_on()
1640
1641
        out_of_range = (param2['\u03BB0']['value'] < self._gc_logic.countdata.min() or param2['\u03BB0']['value'] > self._gc_logic.countdata.max()) or \
1642
                       (param2['\u03BB1']['value'] < self._gc_logic.countdata.min() or param2['\u03BB1']['value'] > self._gc_logic.countdata.max())
1643
1644
        while (np.isnan(param2['fidelity'] or out_of_range) and num_bins > 4):
1645
            # Reduce the number of bins if the calculation yields an invalid
1646
            # number
1647
            num_bins = int(num_bins/2)
1648
            self._ta_logic.set_num_bins_histogram(num_bins)
1649
            flip_prob, param2 = self._ta_logic.analyze_flip_prob(self._gc_logic.countdata, num_bins)
1650
1651
1652
            # reduce the number of bins by one, so that the fitting algorithm
1653
            # work. Eventually, that has to go in the fit constaints of the
1654
            # algorithm.
1655
1656
            out_of_range = (param2['\u03BB0']['value'] < self._gc_logic.countdata.min() or param2['\u03BB0']['value'] > self._gc_logic.countdata.max()) or \
1657
                           (param2['\u03BB1']['value'] < self._gc_logic.countdata.min() or param2['\u03BB1']['value'] > self._gc_logic.countdata.max())
1658
1659
            if out_of_range:
1660
                num_bins = num_bins-1
1661
                self._ta_logic.set_num_bins_histogram(num_bins)
1662
                self.log.warning('Fitted values {0},{1} are out of range [{2},{3}]! '
1663
                            'Change the histogram a '
1664
                            'bit.'.format(param2['\u03BB0']['value'],
1665
                                          param2['\u03BB1']['value'],
1666
                                          self._gc_logic.countdata.min(),
1667
                                          self._gc_logic.countdata.max()))
1668
1669
                flip_prob, param2 = self._ta_logic.analyze_flip_prob(self._gc_logic.countdata, num_bins)
1670
1671
        # run the lifetime calculatiion:
1672
        #        In order to calculate the T1 time one needs the length of one SingleShot readout
1673
        dt = (self.nuclear_2d_rabi_periode/2 + self.nuclear_2d_laser_time + self.nuclear_2d_idle_time) * self.nuclear_2d_reps_within_ssr
1674
        # param_lifetime = self._ta_logic.analyze_lifetime(self._gc_logic.countdata, dt, self.nuclear_2d_estimated_lifetime)
1675
        # param.update(param_lifetime)
1676
1677
1678
        # If everything went wrong, then put at least a reasonable number:
1679
        if np.isnan(param2['fidelity']):
1680
            param2['fidelity'] = 0.5    # that fidelity means that
1681
1682
        # add the flip probability as a parameter to the parameter dict and add
1683
        # also all the other parameters to that dict:
1684
        param['flip_probability'] =  flip_prob
1685
        param.update(param2)
1686
1687
        if self.nuclear_2d_use_single_poisson:
1688
            # print(param)
1689
            # print(param['chi_sqr'])
1690
            return param['chi_sqr_single'], param
1691
1692
        else:
1693
            return param['fidelity'], param
1694
1695
    def _run_gated_counter(self):
1696
1697
        self._gc_logic.startCount()
1698
        time.sleep(2)
1699
1700
        # wait until the gated counter is done
1701
        while self._gc_logic.getState() != 'idle' and not self._stop_measure:
1702
            # print('in SSR measure')
1703
            time.sleep(1)
1704
1705
1706
    def _set_cw_mw(self, switch_on, freq=2.87e9, power=-40):
1707
1708
        if switch_on:
1709
            self._odmr_logic.set_frequency(freq)
1710
            self._odmr_logic.set_power(power)
1711
            self._odmr_logic.MW_on()
1712
        else:
1713
            self._odmr_logic.MW_off()
1714
1715
    def _load_pulsed_odmr(self):
1716
        """ Load a pulsed ODMR asset. """
1717
        #FIXME: Move this creation routine to the tasks!
1718
1719
        self._seq_gen_logic.load_asset(asset_name='PulsedODMR')
1720
1721
    def _load_nuclear_spin_readout(self):
1722
        """ Load a nuclear spin readout asset. """
1723
        #FIXME: Move this creation routine to the tasks!
1724
1725
        self._seq_gen_logic.load_asset(asset_name='SSR')
1726
1727
    def _pulser_on(self):
1728
        """ Switch on the pulser output. """
1729 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1730
        self._set_channel_activation(active=True, apply_to_device=True)
1731
        self._seq_gen_logic.pulser_on()
1732
1733
    def _pulser_off(self):
1734
        """ Switch off the pulser output. """
1735
1736
        self._set_channel_activation(active=False, apply_to_device=False)
1737
        self._seq_gen_logic.pulser_off()
1738
1739
    def _set_channel_activation(self, active=True, apply_to_device=False):
1740
        """ Set the channels according to the current activation config to be either active or not.
1741
1742
        @param bool active: the activation according to the current activation
1743
                            config will be checked and if channel
1744
                            is not active and active=True, then channel will be
1745
                            activated. Otherwise if channel is active and
1746
                            active=False channel will be deactivated.
1747
                            All other channels, which are not in activation
1748
                            config will be deactivated if they are not already
1749
                            deactivated.
1750
        @param bool apply_to_device: Apply the activation or deactivation of the
1751
                                     current activation_config either to the
1752
                                     device and the viewboxes, or just to the
1753
                                     viewboxes.
1754
        """
1755
1756
        pulser_const = self._seq_gen_logic.get_hardware_constraints()
1757
1758
        curr_config_name = self._seq_gen_logic.current_activation_config_name
1759
        activation_config = pulser_const['activation_config'][curr_config_name]
1760
1761
        # here is the current activation pattern of the pulse device:
1762
        active_ch = self._seq_gen_logic.get_active_channels()
1763
1764
        ch_to_change = {} # create something like  a_ch = {1:True, 2:True} to switch
1765
1766
        # check whether the correct channels are already active, and if not
1767
        # correct for that and activate and deactivate the appropriate ones:
1768
        available_ch = self._get_available_ch()
1769
        for ch_name in available_ch:
1770
1771
            # if the channel is in the activation, check whether it is active:
1772
            if ch_name in activation_config:
1773
1774
                if apply_to_device:
1775
                    # if channel is not active but activation is needed (active=True),
1776
                    # then add that to ch_to_change to change the state of the channels:
1777
                    if not active_ch[ch_name] and active:
1778
                        ch_to_change[ch_name] = active
1779
1780
                    # if channel is active but deactivation is needed (active=False),
1781
                    # then add that to ch_to_change to change the state of the channels:
1782
                    if active_ch[ch_name] and not active:
1783 View Code Duplication
                        ch_to_change[ch_name] = active
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1784
1785
1786
            else:
1787
                # all other channel which are active should be deactivated:
1788
                if active_ch[ch_name]:
1789
                    ch_to_change[ch_name] = False
1790
1791
        self._seq_gen_logic.set_active_channels(ch_to_change)
1792
1793
    def _get_available_ch(self):
1794
        """ Helper method to get a list of all available channels.
1795
1796
        @return list: entries are the generic string names of the channels.
1797
        """
1798
        config = self._seq_gen_logic.get_hardware_constraints()['activation_config']
1799
1800
        available_ch = []
1801
        all_a_ch = []
1802
        all_d_ch = []
1803
        for conf in config:
1804
1805
            # extract all analog channels from the config
1806
            curr_a_ch = [entry for entry in config[conf] if 'a_ch' in entry]
1807
            curr_d_ch = [entry for entry in config[conf] if 'd_ch' in entry]
1808
1809
            # append all new analog channels to a temporary array
1810
            for a_ch in curr_a_ch:
1811
                if a_ch not in all_a_ch:
1812
                    all_a_ch.append(a_ch)
1813
1814
            # append all new digital channels to a temporary array
1815
            for d_ch in curr_d_ch:
1816
                if d_ch not in all_d_ch:
1817
                    all_d_ch.append(d_ch)
1818
1819
        all_a_ch.sort()
1820
        all_d_ch.sort()
1821
        available_ch.extend(all_a_ch)
1822
        available_ch.extend(all_d_ch)
1823
1824
        return available_ch
1825
1826
    def _do_postmeasurement_proc(self):
1827
1828
        # do a selected post measurement procedure,
1829
1830
        return
1831
1832
1833
    def get_available_odmr_peaks(self):
1834
        """ Retrieve the information on which odmr peak the microwave can be
1835
            applied.
1836
1837
        @return list: with string entries denoting the peak number
1838
        """
1839
        return [1, 2, 3]
1840
1841
    def save_1d_data(self):
1842
1843
1844
        # save also all kinds of data, which are the results during the
1845
        # alignment measurements
1846
1847
        pass
1848
1849
1850
    def save_2d_data(self, tag=None, timestamp=None):
1851
        """ Save the data of the  """
1852
1853
        filepath = self._save_logic.get_path_for_module(module_name='Magnet')
1854
1855
        if timestamp is None:
1856
            timestamp = datetime.datetime.now()
1857
1858
        # if tag is not None and len(tag) > 0:
1859
        #     filelabel = tag + '_magnet_alignment_data'
1860
        #     filelabel2 = tag + '_magnet_alignment_add_data'
1861
        # else:
1862
        #     filelabel = 'magnet_alignment_data'
1863
        #     filelabel2 = 'magnet_alignment_add_data'
1864
1865
        if tag is not None and len(tag) > 0:
1866
            filelabel = tag + '_magnet_alignment_data'
1867
            filelabel2 = tag + '_magnet_alignment_add_data'
1868
            filelabel3 = tag + '_magnet_alignment_data_table'
1869
        else:
1870
            filelabel = 'magnet_alignment_data'
1871
            filelabel2 = 'magnet_alignment_add_data'
1872
            filelabel3 = 'magnet_alignment_data_table'
1873
1874
        # prepare the data in a dict or in an OrderedDict:
1875
1876
        # here is the matrix saved
1877
        matrix_data = OrderedDict()
1878
1879
        # here are all the parameters, which are saved for a certain matrix
1880
        # entry, mainly coming from all the other logic modules except the magnet logic:
1881
        add_matrix_data = OrderedDict()
1882
1883
        # here are all supplementary information about the measurement, mainly
1884
        # from the magnet logic
1885
        supplementary_data = OrderedDict()
1886
1887
        axes_names = list(self._saved_pos_before_align)
1888
1889
1890
        matrix_data['Alignment Matrix'] = self._2D_data_matrix
1891
1892
        parameters = OrderedDict()
1893
        parameters['Measurement start time'] = self._start_measurement_time
1894
        if self._stop_measurement_time is not None:
1895
            parameters['Measurement stop time'] = self._stop_measurement_time
1896
        parameters['Time at Data save'] = timestamp
1897
        parameters['Pathway of the magnet alignment'] = 'Snake-wise steps'
1898
1899
        for index, entry in enumerate(self._pathway):
1900
            parameters['index_'+str(index)] = entry
1901
1902
        parameters['Backmap of the magnet alignment'] = 'Index wise display'
1903
1904
        for entry in self._backmap:
1905
            parameters['related_intex_'+str(entry)] = self._backmap[entry]
1906
1907
1908
1909
        self._save_logic.save_data(matrix_data, filepath, parameters=parameters,
1910
                                   filelabel=filelabel, timestamp=timestamp,
1911
                                   as_text=True)
1912
1913
        self.log.debug('Magnet 2D data saved to:\n{0}'.format(filepath))
1914
1915
        # prepare the data in a dict or in an OrderedDict:
1916
        add_data = OrderedDict()
1917
        axis0_data = np.zeros(len(self._backmap))
1918
        axis1_data = np.zeros(len(self._backmap))
1919
        param_data = np.zeros(len(self._backmap), dtype='object')
1920
1921
        for backmap_index in self._backmap:
1922
            axis0_data[backmap_index] = self._backmap[backmap_index][self._axis0_name]
1923
            axis1_data[backmap_index] = self._backmap[backmap_index][self._axis1_name]
1924
            param_data[backmap_index] = str(self._2D_add_data_matrix[self._backmap[backmap_index]['index']])
1925
1926
        constr = self.get_hardware_constraints()
1927
        units_axis0 = constr[self._axis0_name]['unit']
1928
        units_axis1 = constr[self._axis1_name]['unit']
1929
1930
        add_data['{0} values ({1})'.format(self._axis0_name, units_axis0)] = axis0_data
1931
        add_data['{0} values ({1})'.format(self._axis1_name, units_axis1)] = axis1_data
1932
        add_data['all measured additional parameter'] = param_data
1933
1934
1935
1936
        self._save_logic.save_data(add_data, filepath,
1937
                                   filelabel=filelabel2, timestamp=timestamp,
1938
                                   as_text=True)
1939
        # save the data table
1940
1941
        count_data = self._2D_data_matrix
1942
        x_val = self._2D_axis0_data
1943
        y_val = self._2D_axis1_data
1944
        save_dict = OrderedDict()
1945
        axis0_key = '{0} values ({1})'.format(self._axis0_name, units_axis0)
1946
        axis1_key = '{0} values ({1})'.format(self._axis1_name, units_axis1)
1947
        counts_key = 'counts (c/s)'
1948
        save_dict[axis0_key] = []
1949
        save_dict[axis1_key] = []
1950
        save_dict[counts_key] = []
1951
1952
        for ii, columns in enumerate(count_data):
1953
            for jj, col_counts in enumerate(columns):
1954
                # x_list = [x_val[ii]] * len(countlist)
1955
                save_dict[axis0_key].append(x_val[ii])
1956
                save_dict[axis1_key].append(y_val[jj])
1957
                save_dict[counts_key].append(col_counts)
1958
1959
        self._save_logic.save_data(save_dict, filepath,
1960
                                   filelabel=filelabel3, timestamp=timestamp,
1961
                                   as_text=True)
1962
1963
1964
    def _move_to_index(self, pathway_index, pathway):
1965
1966
        # make here the move and set also for the move the velocity, if
1967
        # specified!
1968
1969
        move_commmands = pathway[pathway_index]
1970
1971
        move_dict_abs = dict()
1972
        move_dict_rel = dict()
1973
        move_dict_vel = dict()
1974
1975
        for axis_name in move_commmands:
1976
1977
            if move_commmands[axis_name].get('vel') is not None:
1978
                    move_dict_vel[axis_name] = move_commmands[axis_name]['vel']
1979
1980
            if move_commmands[axis_name].get('move_abs') is not None:
1981
                move_dict_abs[axis_name] = move_commmands[axis_name]['move_abs']
1982
            elif move_commmands[axis_name].get('move_rel') is not None:
1983
                move_dict_rel[axis_name] = move_commmands[axis_name]['move_rel']
1984
1985
        return move_dict_vel, move_dict_abs, move_dict_rel
1986
1987
    def set_pos_checktime(self, checktime):
1988
        if not np.isclose(0, checktime) and checktime>0:
1989
            self._checktime = checktime
1990
        else:
1991
            self.log.warning('Could not set a new value for checktime, since '
1992
                    'the passed value "{0}" is either zero or negative!\n'
1993
                    'Choose a proper checktime value in seconds, the old '
1994
                    'value will be kept!')
1995
1996
1997
    def get_2d_data_matrix(self):
1998
        return self._2D_data_matrix
1999
2000
    def get_2d_axis_arrays(self):
2001
        return self._2D_axis0_data, self._2D_axis1_data
2002
2003
    def set_optimize_pos(self, state=True):
2004
        """ Activate the optimize position option. """
2005
        self._optimize_pos = state
2006
2007
    def get_optimize_pos(self):
2008
        """ Retrieve whether the optimize position is set.
2009
2010
        @return bool: whether the optimize_pos is set or not.
2011
        """
2012
        return self._optimize_pos
2013