Completed
Push — pulsed_with_queued_connections ( 6b1460...30fbbf )
by
unknown
02:58
created

Pulse_Sequence.refresh_parameters()   C

Complexity

Conditions 9

Size

Total Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
dl 0
loc 54
rs 5.4234
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A PulseSequence.delete_ensemble() 0 7 1
A PulseSequence.append_ensemble() 0 17 2
A PulseSequence.replace_ensemble() 0 11 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
# -*- coding: utf-8 -*-
2
3
"""
4
This file contains the Qudi data object classes needed for pulse sequence generation.
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
import numpy as np
24
from collections import OrderedDict
25
26
27
class PulseBlockElement:
28
    """
29
    Object representing a single atomic element in a pulse block.
30
31
    This class can build waiting times, sine waves, etc. The pulse block may
32
    contain many Pulse_Block_Element Objects. These objects can be displayed in
33
    a GUI as single rows of a Pulse_Block.
34
    """
35
    def __init__(self, init_length_s, increment_s=0, pulse_function=None, digital_high=None,
36
                 parameters=None, use_as_tick=False):
37
        """
38
        The constructor for a Pulse_Block_Element needs to have:
39
40
        @param int init_length_s: an initial length of the element, this
41
                                 parameters should not be zero but must have a
42
                                 finite value.
43
        @param int increment_s: the number which will be incremented during
44
                               each repetition of this object
45
        @param list pulse_function: list of strings with name of the sampling
46
                                    function how to alter the points, the name
47
                                    of the function will be one of the sampling
48
                                    functions
49
        @param list digital_high: list of digital channels, which are for the
50
                              length of this Pulse_Block_Element are set either
51
                              to True (high) or to False (low). The length of
52
                              the marker list depends on the number of (active)
53
                              digital channels. For 4 digital channel it may
54
                              look like:
55
                              [True, False, False, False]
56
        @param list parameters: a list of dictionaries. The number of dictionaries
57
                           depends on the number of analog channels. The
58
                           number of entries within a dictionary depends on the
59
                           chosen sampling function. The key words of the
60
                           dictionary for the parameters will be those of the
61
                           sampling functions.
62
        @param bool use_as_tick: bool, indicates, whether the set length should
63
                           be used as a tick (i.e. the parameter for the x axis)
64
                           for the later plot in the analysis.
65
        """
66
        if parameters is None:
67
            parameters = []
68
        # FIXME: Sanity checks need to be implemented here
69
        self.init_length_s  = init_length_s
70
        self.increment_s    = increment_s
71
        self.pulse_function = pulse_function
72
        self.digital_high   = digital_high
73
        self.parameters     = parameters
74
        self.use_as_tick    = use_as_tick
75
        # calculate number of digital and analogue channels
76
        if pulse_function is not None:
77
            self.analog_channels = len(pulse_function)
78
        else:
79
            self.analog_channels = 0
80
        if digital_high is not None:
81
            self.digital_channels = len(digital_high)
82
        else:
83
            self.digital_channels = 0
84
85
86
class PulseBlock:
87
    """
88
    Collection of Pulse_Block_Elements which is called a Pulse_Block.
89
    """
90
    def __init__(self, name, element_list):
91
        """
92
        The constructor for a Pulse_Block needs to have:
93
94
        @param str name: chosen name for the Pulse_Block
95
        @param list element_list: which contains the Pulse_Block_Element Objects forming a
96
                                  Pulse_Block, e.g. [Pulse_Block_Element, Pulse_Block_Element, ...]
97
        """
98
        self.name = name
99
        self.element_list = element_list
100
        self.init_length_s = None
101
        self.increment_s = None
102
        self.analog_channels = None
103
        self.digital_channels = None
104
        self.use_as_tick = None
105
        self._refresh_parameters()
106
107
    def _refresh_parameters(self):
108
        """ Initialize the parameters which describe this Pulse_Block object.
109
110
        The information is gained from all the Pulse_Block_Element objects,
111
        which are attached in the element_list.
112
        """
113
        # the Pulse_Block parameter
114
        self.init_length_s = 0.0
115
        self.increment_s = 0.0
116
        self.analog_channels = 0
117
        self.digital_channels = 0
118
        self.use_as_tick = False
119
120
        # calculate the tick value for the whole block. Basically sum all the
121
        # init_length_bins which have the use_as_tick attribute set to True.
122
        self.measurement_tick_start = 0.0
123
        # make the same thing for the increment, to obtain the total increment
124
        # number for the block. This facilitates in calculating the measurement tick list.
125
        self.measurement_tick_increment = 0.0
126
127
        for elem in self.element_list:
128
            self.init_length_s += elem.init_length_s
129
            self.increment_s += elem.increment_s
130
            if elem.use_as_tick:
131
                self.use_as_tick = True
132
                self.measurement_tick_start += elem.init_length_s
133
                self.measurement_tick_increment += elem.increment_s
134
135
            if elem.analog_channels > self.analog_channels:
136
                self.analog_channels = elem.analog_channels
137
            if elem.digital_channels > self.digital_channels:
138
                self.digital_channels = elem.digital_channels
139
140
    def replace_element(self, position, element):
141
        self.element_list[position] = element
142
        self._refresh_parameters()
143
        return
144
145
    def delete_element(self, position):
146
        del(self.element_list[position])
147
        self._refresh_parameters()
148
        return
149
150
    def append_element(self, element, at_beginning=False):
151
        if at_beginning:
152
            self.element_list.insert(0, element)
153
        else:
154
            self.element_list.append(element)
155
        self._refresh_parameters()
156
        return
157
158
159
class PulseBlockEnsemble:
160
    """
161
    Represents a collection of Pulse_Block objects which is called a Pulse_Block_Ensemble.
162
163
    This object is used as a construction plan to create one sampled file.
164
    """
165 View Code Duplication
    def __init__(self, name, block_list, rotating_frame=True):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
166
        """
167
        The constructor for a Pulse_Block_Ensemble needs to have:
168
169
        @param str name: chosen name for the Pulse_Block_Ensemble
170
        @param list block_list: contains the Pulse_Block Objects with their number of repetitions,
171
                                e.g. [(Pulse_Block, repetitions), (Pulse_Block, repetitions), ...])
172
        @param bool rotating_frame: indicates whether the phase should be preserved for all the
173
                                    functions.
174
        """
175
        # FIXME: Sanity checking needed here
176
        self.name = name                    # Pulse_Block_Ensemble name
177
        self.block_list = block_list
178
        self.rotating_frame = rotating_frame
179
        self.length_s = 0
180
        self.analog_channels = 0
181
        self.digital_channels = 0
182
        self.measurement_ticks_list = np.array([])
183
        self._refresh_parameters()
184
        # these parameters can be set manually by the logic to recall the pulser settings upon
185
        # loading into channels. They are not crucial for waveform generation.
186
        self.sample_rate = None
187
        self.activation_config = None
188
        self.amplitude_dict = None
189
        self.laser_channel = None
190
        return
191
192
    def _refresh_parameters(self):
193
        self.length_s = 0
194
        self.analog_channels = 0
195
        self.digital_channels = 0
196
        # calculate the tick values for the whole block_ensemble.
197
        self.measurement_ticks_list = np.array([])
198
        for block, reps in self.block_list:
199
            # Get number of channels from the block information
200
            if block.analog_channels > self.analog_channels:
201
                self.analog_channels = block.analog_channels
202
            if block.digital_channels > self.digital_channels:
203
                self.digital_channels = block.digital_channels
204
205
            # Get and set information about the length of the ensemble
206
            self.length_s += (block.init_length_s * (reps+1) + block.increment_s * (reps*(reps+1)/2))
207
208
            # Calculate the measurement ticks list for this ensemble
209
            if block.use_as_tick:
210
                start = block.measurement_tick_start
211
                incr = block.measurement_tick_increment
212
                if incr == 0.0:
213
                    arr = np.array([])
214
                else:
215
                    arr = np.arange(start, start+(reps+1)*incr, incr)
216
                self.measurement_ticks_list = np.append(self.measurement_ticks_list, arr)
217
        return
218
219
    def replace_block(self, position, block):
220
        self.block_list[position] = block
221
        self._refresh_parameters()
222
        return
223
224
    def delete_block(self, position):
225
        del(self.block_list[position])
226
        self._refresh_parameters()
227
        return
228
229
    def append_block(self, block, at_beginning=False):
230
        if at_beginning:
231
            self.block_list.insert(0, block)
232
        else:
233
            self.block_list.append(block)
234
        self._refresh_parameters()
235
        return
236
237
238
class PulseSequence:
239
    """
240
    Higher order object for sequence capability.
241
242
    Represents a playback procedure for a number of Pulse_Block_Ensembles. Unused for pulse
243
    generator hardware without sequencing functionality.
244
    """
245 View Code Duplication
    def __init__(self, name, ensemble_param_list, rotating_frame=True):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
246
        """
247
        The constructor for a Pulse_Sequence objects needs to have:
248
249
        @param str name: the actual name of the sequence
250
        @param list ensemble_param_list: list containing a tuple of two entries:
251
                    (Pulse_Block_Ensemble, seq_param), (Pulse_Block_Ensemble, seq_param), ...
252
                                          The seq_param is a dictionary, where the various sequence
253
                                          parameters are saved with their keywords and the
254
                                          according parameter (as item). What parameter will be in
255
                                          this dictionary will completely depend on the sequence
256
                                          parameter set of the pulsing device. But most certain the
257
                                          parameter 'reps' meaning repetitions will be presesnt in
258
                                          the sequence parameters.
259
                                          If only 'reps' are in the dictionary, than the dict will
260
                                          look like
261
                                                seq_param = {'reps': 12}
262
                                          if 12 was chosen as the number of repetitions.
263
        @param bool rotating_frame: indicates, whether the phase has to be preserved in all
264
                                    oscillating functions.
265
        """
266
        self.name = name
267
        self.ensemble_param_list = ensemble_param_list
268
        self.rotating_frame = rotating_frame
269
        self.length_s = 0.0
270
        self.analog_channels = 0
271
        self.digital_channels = 0
272
        self._refresh_parameters()
273
        self.sampled_ensembles = OrderedDict()
274
        # these parameters can be set manually by the logic to recall the pulser settings upon
275
        # loading into channels. They are not crucial for waveform generation.
276
        self.sample_rate = None
277
        self.activation_config = None
278
        self.amplitude_dict = None
279
        self.laser_channel = None
280
        return
281
282
    def _refresh_parameters(self):
283
        """ Generate the needed parameters from the passed object.
284
285
        Baiscally, calculate the length_bins and number of analog and digital
286
        channels.
287
        """
288
        self.length_bins = 0
289
        self.analog_channels = 0
290
        self.digital_channels = 0
291
        # here all DIFFERENT kind of ensembles will be saved in, i.e. with different names.
292
        self.different_ensembles_dict = dict()
293
        # here the measurement ticks will be saved:
294
        self.measurement_ticks_list = np.array([])
295
296
        # to make a resonable measurement tick list, the last biggest tick value after all
297
        # the repetitions of a block is used as the offset_time for the next block.
298
        offset_tick_bin = 0
299
        for ensemble, seq_dict in self.ensemble_param_list:
300
            for param in seq_dict:
301
                if 'reps' in param.lower() or 'repetition' in param.lower():
302
                    reps = seq_dict[param]
303
                    break
304
                else:
305
                    reps = 0
306
            self.length_s += (ensemble.length_s * (reps+1))
307
308
            if ensemble.analog_channels > self.analog_channels:
309
                self.analog_channels = ensemble.analog_channels
310
            if ensemble.digital_channels > self.digital_channels:
311
                self.digital_channels = ensemble.digital_channels
312
313
            if self.different_ensembles_dict.get(ensemble.name) is None:
314
                self.different_ensembles_dict[ensemble.name] = ensemble
315
316
            self.measurement_ticks_list = np.append(self.measurement_ticks_list,
317
                                                    offset_tick_bin + ensemble.measurement_ticks_list)
318
319
            # for the next repetition or pulse_block_ensemble, add last number from the
320
            # measurement_ticks_list as offset_tick_bin. Otherwise the measurement_ticks_list will
321
            # be a mess:
322
            if len(self.measurement_ticks_list) > 0:
323
                offset_tick_bin = self.measurement_ticks_list[-1]
324
        return
325
326
    def replace_ensemble(self, position, ensemble_param):
327
        """ Replace an ensemble at a given position.
328
329
        @param int position: position in a the ensemble list
330
        @param list ensemble_param: with entries
331
                                        (Pulse_Block_Ensemble, seq_param)
332
                                    which will replace the old one.
333
        """
334
        self.ensemble_param_list[position] = ensemble_param
335
        self._refresh_parameters()
336
        return
337
338
    def delete_ensemble(self, position):
339
        """ Delete an ensemble at a given position
340
341
        @param int position: position within the list self.ensemble_param_list.
342
        """
343
        del(self.ensemble_list[position])
344
        self._refresh_parameters()
345
346
    def append_ensemble(self, ensemble_param, at_beginning=False):
347
        """ Append either at the front or at the back an ensemble_param
348
349
        @param tuple ensemble_param: containing two entries:
350
                                        (Pulse_Block_Ensemble, seq_param)
351
                                     where Pulse_Block_Ensemble is the object
352
                                     and seq_param is the parameter set for that
353
                                     ensemble.
354
        @param bool at_beginning: If flase append to end (default), if true then
355
                                  inset at beginning.
356
        """
357
358
        if at_beginning:
359
            self.ensemble_list.insert(0, ensemble_param)
360
        else:
361
            self.ensemble_list.append(ensemble_param)
362
        self._refresh_parameters()
363