Completed
Push — master ( 03e693...63d2e0 )
by
unknown
13s
created

FastComtec._change_filename()   A

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
1
# -*- coding: utf-8 -*-
2
3
"""
4
This file contains the Qudi hardware file implementation for FastComtec p7887 .
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
#TODO: start stop works but pause does not work, i guess gui/logic problem
24
#TODO: What does get status do or need as return?
25
#TODO: Check if there are more modules which are missing, and more settings for FastComtec which need to be put, should we include voltage threshold?
26
27
from core.module import Base, ConfigOption
28
from core.util.modules import get_main_dir
29
from interface.fast_counter_interface import FastCounterInterface
30
import time
31
import os
32
import numpy as np
33
import ctypes
34
35
36
37
"""
38
Remark to the usage of ctypes:
39
All Python types except integers (int), strings (str), and bytes (byte) objects
40
have to be wrapped in their corresponding ctypes type, so that they can be
41
converted to the required C data type.
42
43
ctypes type     C type                  Python type
44
----------------------------------------------------------------
45
c_bool          _Bool                   bool (1)
46
c_char          char                    1-character bytes object
47
c_wchar         wchar_t                 1-character string
48
c_byte          char                    int
49
c_ubyte         unsigned char           int
50
c_short         short                   int
51
c_ushort        unsigned short          int
52
c_int           int                     int
53
c_uint          unsigned int            int
54
c_long          long                    int
55
c_ulong         unsigned long           int
56
c_longlong      __int64 or
57
                long long               int
58
c_ulonglong     unsigned __int64 or
59
                unsigned long long      int
60
c_size_t        size_t                  int
61
c_ssize_t       ssize_t or
62
                Py_ssize_t              int
63
c_float         float                   float
64
c_double        double                  float
65
c_longdouble    long double             float
66
c_char_p        char *
67
                (NUL terminated)        bytes object or None
68
c_wchar_p       wchar_t *
69
                (NUL terminated)        string or None
70
c_void_p        void *                  int or None
71
72
"""
73
# Reconstruct the proper structure of the variables, which can be extracted
74
# from the header file 'struct.h'.
75
76
class AcqStatus(ctypes.Structure):
77
    """ Create a structured Data type with ctypes where the dll can write into.
78
79
    This object handles and retrieves the acquisition status data from the
80
    Fastcomtec.
81
82
    int started;                // acquisition status: 1 if running, 0 else
83
    double runtime;             // running time in seconds
84
    double totalsum;            // total events
85
    double roisum;              // events within ROI
86
    double roirate;             // acquired ROI-events per second
87
    double nettosum;            // ROI sum with background subtracted
88
    double sweeps;              // Number of sweeps
89
    double stevents;            // Start Events
90
    unsigned long maxval;       // Maximum value in spectrum
91
    """
92
    _fields_ = [('started', ctypes.c_int),
93
                ('runtime', ctypes.c_double),
94
                ('totalsum', ctypes.c_double),
95
                ('roisum', ctypes.c_double),
96
                ('roirate', ctypes.c_double),
97
                ('ofls', ctypes.c_double),
98
                ('sweeps', ctypes.c_double),
99
                ('stevents', ctypes.c_double),
100 View Code Duplication
                ('maxval', ctypes.c_ulong), ]
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
101
102
class AcqSettings(ctypes.Structure):
103
    """ Create a structured Data type with ctypes where the dll can write into.
104
105
    This object handles and retrieves the acquisition settings of the Fastcomtec.
106
    """
107
108
    _fields_ = [('range',       ctypes.c_ulong),
109
                ('prena',       ctypes.c_long),
110
                ('cftfak',      ctypes.c_long),
111
                ('roimin',      ctypes.c_ulong),
112
                ('roimax',      ctypes.c_ulong),
113
                ('eventpreset', ctypes.c_double),
114
                ('timepreset',  ctypes.c_double),
115
                ('savedata',    ctypes.c_long),
116
                ('fmt',         ctypes.c_long),
117
                ('autoinc',     ctypes.c_long),
118
                ('cycles',      ctypes.c_long),
119
                ('sweepmode',   ctypes.c_long),
120
                ('syncout',     ctypes.c_long),
121
                ('bitshift',    ctypes.c_long),
122
                ('digval',      ctypes.c_long),
123
                ('digio',       ctypes.c_long),
124
                ('dac0',        ctypes.c_long),
125
                ('dac1',        ctypes.c_long),
126
                ('swpreset',    ctypes.c_double),
127
                ('nregions',    ctypes.c_long),
128
                ('caluse',      ctypes.c_long),
129
                ('fstchan',     ctypes.c_double),
130
                ('active',      ctypes.c_long),
131
                ('calpoints',   ctypes.c_long), ]
132
133
class ACQDATA(ctypes.Structure):
134
    """ Create a structured Data type with ctypes where the dll can write into.
135
136
    This object handles and retrieves the acquisition data of the Fastcomtec.
137
    """
138
    _fields_ = [('s0', ctypes.POINTER(ctypes.c_ulong)),
139
                ('region', ctypes.POINTER(ctypes.c_ulong)),
140
                ('comment', ctypes.c_char_p),
141
                ('cnt', ctypes.POINTER(ctypes.c_double)),
142
                ('hs0', ctypes.c_int),
143
                ('hrg', ctypes.c_int),
144
                ('hcm', ctypes.c_int),
145
                ('hct', ctypes.c_int), ]
146
147
148
149
class FastComtec(Base, FastCounterInterface):
150
    """
151
    unstable: Jochen Scheuer, Simon Schmitt
152
153
    Hardware Class for the FastComtec Card.
154
    """
155
    _modclass = 'FastComtec'
156
    _modtype = 'hardware'
157
    gated = ConfigOption('gated', False, missing='warn')
158
    trigger_safety = ConfigOption('trigger_safety', 200e-9, missing='warn')
159
    aom_delay = ConfigOption('aom_delay', 400e-9, missing='warn')
160
    minimal_binwidth = ConfigOption('minimal_binwidth', 0.25e-9, missing='warn')
161
162
    def __init__(self, config, **kwargs):
163
        super().__init__(config=config, **kwargs)
164
165
        self.log.debug('The following configuration was found.')
166
167
        # checking for the right configuration
168
        for key in config.keys():
169
            self.log.info('{0}: {1}'.format(key,config[key]))
170
        #this variable has to be added because there is no difference
171
        #in the fastcomtec it can be on "stopped" or "halt"
172
        self.stopped_or_halt = "stopped"
173
        self.timetrace_tmp = []
174
175
    def on_activate(self):
176
        """ Initialisation performed during activation of the module.
177
        """
178
        self.dll = ctypes.windll.LoadLibrary('dp7887.dll')
179
        if self.gated:
180
            self.change_sweep_mode(gated=True)
181
        else:
182
            self.change_sweep_mode(gated=False)
183
        return
184
185
186
    def on_deactivate(self):
187
        """ Deinitialisation performed during deactivation of the module.
188
        """
189
        return
190
191
    def get_constraints(self):
192
        """ Retrieve the hardware constrains from the Fast counting device.
193
194
        @return dict: dict with keys being the constraint names as string and
195
                      items are the definition for the constaints.
196
197
         The keys of the returned dictionary are the str name for the constraints
198
        (which are set in this method).
199
200
                    NO OTHER KEYS SHOULD BE INVENTED!
201
202
        If you are not sure about the meaning, look in other hardware files to
203
        get an impression. If still additional constraints are needed, then they
204
        have to be added to all files containing this interface.
205
206
        The items of the keys are again dictionaries which have the generic
207
        dictionary form:
208
            {'min': <value>,
209
             'max': <value>,
210
             'step': <value>,
211
             'unit': '<value>'}
212
213
        Only the key 'hardware_binwidth_list' differs, since they
214
        contain the list of possible binwidths.
215
216
        If the constraints cannot be set in the fast counting hardware then
217
        write just zero to each key of the generic dicts.
218
        Note that there is a difference between float input (0.0) and
219
        integer input (0), because some logic modules might rely on that
220
        distinction.
221
222
        ALL THE PRESENT KEYS OF THE CONSTRAINTS DICT MUST BE ASSIGNED!
223
        """
224
225
        constraints = dict()
226
227
        # the unit of those entries are seconds per bin. In order to get the
228
        # current binwidth in seconds use the get_binwidth method.
229
        constraints['hardware_binwidth_list'] = list(self.minimal_binwidth * (2 ** np.array(
230
                                                     np.linspace(0,24,25))))
231
        constraints['max_sweep_len'] = 6.8
232
        return constraints
233
234
    def configure(self, bin_width_s, record_length_s, number_of_gates=0, filename=None):
235
        """ Configuration of the fast counter.
236
237
        @param float bin_width_s: Length of a single time bin in the time trace
238
                                  histogram in seconds.
239
        @param float record_length_s: Total length of the timetrace/each single
240
                                      gate in seconds.
241
        @param int number_of_gates: optional, number of gates in the pulse
242
                                    sequence. Ignore for not gated counter.
243
244
        @return tuple(binwidth_s, record_length_s, number_of_gates):
245
                    binwidth_s: float the actual set binwidth in seconds
246
                    gate_length_s: the actual record length in seconds
247
                    number_of_gates: the number of gated, which are accepted,
248
                    None if not-gated
249
        """
250
251
        # when not gated, record length = total sequence length, when gated, record length = laser length.
252
        # subtract 200 ns to make sure no sequence trigger is missed
253
        record_length_FastComTech_s = record_length_s
254
        if self.gated:
255
            # add time to account for AOM delay
256
            no_of_bins = int((record_length_FastComTech_s + self.aom_delay) / self.set_binwidth(bin_width_s))
257
        else:
258
            # subtract time to make sure no sequence trigger is missed
259
            no_of_bins = int((record_length_FastComTech_s - self.trigger_safety) / self.set_binwidth(bin_width_s))
260
261
        self.set_length(no_of_bins, preset=1, cycles=number_of_gates)
262
263
        if filename is not None:
264
            self._change_filename(filename)
265
266
        return (self.get_binwidth(), record_length_FastComTech_s, number_of_gates)
267
268
269
270
    def get_status(self):
271
        """
272
        Receives the current status of the Fast Counter and outputs it as return value.
273
        0 = unconfigured
274
        1 = idle
275
        2 = running
276
        3 = paused
277
        -1 = error state
278
        """
279
        status = AcqStatus()
280
        self.dll.GetStatusData(ctypes.byref(status), 0)
281
        if status.started == 1:
282
            return 2
283
        elif status.started == 0:
284
            if self.stopped_or_halt == "stopped":
285
                return 1
286
            elif self.stopped_or_halt == "halt":
287
                return 3
288
            else:
289
                self.log.error('FastComTec neither stopped nor halt')
290
291
                return -1
292
        else:
293
            self.log.error(
294
                'There is an unknown status from FastComtec. The status message was %s' % (str(status.started)))
295
            return -1
296
297
    def get_current_runtime(self):
298
        """
299
        Returns the current runtime.
300
        @return float runtime: in s
301
        """
302
        status = AcqStatus()
303
        self.dll.GetStatusData(ctypes.byref(status), 0)
304
        return status.runtime
305
306
    def get_current_sweeps(self):
307
        """
308
        Returns the current runtime.
309
        @return int sweeps: in sweeps
310
        """
311
        status = AcqStatus()
312
        self.dll.GetStatusData(ctypes.byref(status), 0)
313
        return status.sweeps
314
315
    def start_measure(self):
316
        """Start the measurement. """
317
        status = self.dll.Start(0)
318
        while self.get_status() != 2:
319
            time.sleep(0.05)
320
        return status
321
322
    def pause_measure(self):
323
        """Make a pause in the measurement, which can be continued. """
324
        self.stopped_or_halt = "halt"
325
        status = self.dll.Halt(0)
326
        while self.get_status() != 3:
327
            time.sleep(0.05)
328
329
        if self.gated:
330
            self.timetrace_tmp = self.get_data_trace()
331
        return status
332
333
    def stop_measure(self):
334
        """Stop the measurement. """
335
        self.stopped_or_halt = "stopped"
336
        status = self.dll.Halt(0)
337
        while self.get_status() != 1:
338
            time.sleep(0.05)
339
340
        if self.gated:
341
            self.timetrace_tmp = []
342
        return status
343
344
    def continue_measure(self):
345
        """Continue a paused measurement. """
346
        if self.gated:
347
            status = self.start_measure()
348
        else:
349
            status = self.dll.Continue(0)
350
            while self.get_status() != 2:
351
                time.sleep(0.05)
352
        return status
353
354
    def get_binwidth(self):
355
        """ Returns the width of a single timebin in the timetrace in seconds.
356
357
        @return float: current length of a single bin in seconds (seconds/bin)
358
359
        The red out bitshift will be converted to binwidth. The binwidth is
360
        defined as 2**bitshift*minimal_binwidth.
361
        """
362
        return self.minimal_binwidth*(2**int(self.get_bitshift()))
363
364
365
    def is_gated(self):
366
        """ Check the gated counting possibility.
367
368
        @return bool: Boolean value indicates if the fast counter is a gated
369
                      counter (TRUE) or not (FALSE).
370
        """
371
        return self.gated
372
373
    def get_data_trace(self):
374
        """
375
        Polls the current timetrace data from the fast counter and returns it as a numpy array (dtype = int64).
376
        The binning specified by calling configure() must be taken care of in this hardware class.
377
        A possible overflow of the histogram bins must be caught here and taken care of.
378
        If the counter is UNgated it will return a 1D-numpy-array with returnarray[timebin_index]
379
        If the counter is gated it will return a 2D-numpy-array with returnarray[gate_index, timebin_index]
380
381
          @return arrray: Time trace.
382
        """
383
        setting = AcqSettings()
384
        self.dll.GetSettingData(ctypes.byref(setting), 0)
385
        N = setting.range
386
387
        if self.gated:
388
            bsetting=AcqSettings()
389
            self.dll.GetSettingData(ctypes.byref(bsetting), 0)
390
            H = bsetting.cycles
391
            data = np.empty((H, int(N / H)), dtype=np.uint32)
392
393
        else:
394
            data = np.empty((N,), dtype=np.uint32)
395
396
        p_type_ulong = ctypes.POINTER(ctypes.c_uint32)
397
        ptr = data.ctypes.data_as(p_type_ulong)
398
        self.dll.LVGetDat(ptr, 0)
399
        time_trace = np.int64(data)
400
401
        if self.gated and self.timetrace_tmp != []:
402
            time_trace = time_trace + self.timetrace_tmp
403
404
        return time_trace
405
406
407
    def get_data_testfile(self):
408
        ''' Load data test file '''
409
        data = np.loadtxt(os.path.join(get_main_dir(), 'tools', 'FastComTec_demo_timetrace.asc'))
410
        time.sleep(0.5)
411
        return data
412
413
414
    # =========================================================================
415
    #                           Non Interface methods
416
    # =========================================================================
417
418
    def get_bitshift(self):
419
        """Get bitshift from Fastcomtec.
420
421
        @return int settings.bitshift: the red out bitshift
422
        """
423
424
        settings = AcqSettings()
425
        self.dll.GetSettingData(ctypes.byref(settings), 0)
426
        return int(settings.bitshift)
427
428
    def set_bitshift(self, bitshift):
429
        """ Sets the bitshift properly for this card.
430
431
        @param int bitshift:
432
433
        @return int: asks the actual bitshift and returns the red out value
434
        """
435
436
        cmd = 'BITSHIFT={0}'.format(bitshift)
437
        self.dll.RunCmd(0, bytes(cmd, 'ascii'))
438
        return self.get_bitshift()
439
440
    def set_binwidth(self, binwidth):
441
        """ Set defined binwidth in Card.
442
443
        @param float binwidth: the current binwidth in seconds
444
445
        @return float: Red out bitshift converted to binwidth
446
447
        The binwidth is converted into to an appropiate bitshift defined as
448
        2**bitshift*minimal_binwidth.
449
        """
450
        bitshift = int(np.log2(binwidth/self.minimal_binwidth))
451
        new_bitshift=self.set_bitshift(bitshift)
452
453
        return self.minimal_binwidth*(2**new_bitshift)
454
455
456
    #TODO: Check such that only possible lengths are set.
457
    def set_length(self, length_bins, preset=None, cycles=None, sequences=None):
458
        """ Sets the length of the length of the actual measurement.
459
460
        @param int length_bins: Length of the measurement in bins
461
462
        @return float: Red out length of measurement
463
        """
464
        constraints = self.get_constraints()
465
        if length_bins * self.get_binwidth() < constraints['max_sweep_len']:
466
            cmd = 'RANGE={0}'.format(int(length_bins))
467
            self.dll.RunCmd(0, bytes(cmd, 'ascii'))
468
            cmd = 'roimax={0}'.format(int(length_bins))
469
            self.dll.RunCmd(0, bytes(cmd, 'ascii'))
470
            if preset != None:
471
                cmd = 'swpreset={0}'.format(preset)
472
                self.dll.RunCmd(0, bytes(cmd, 'ascii'))
473
            if cycles != None and cycles != 0:
474
                cmd = 'cycles={0}'.format(cycles)
475
                self.dll.RunCmd(0, bytes(cmd, 'ascii'))
476
            if sequences != None and sequences != 0:
477
                cmd = 'sequences={0}'.format(sequences)
478
                self.dll.RunCmd(0, bytes(cmd, 'ascii'))
479
            return self.get_length()
480
        else:
481
            self.log.error(
482
                'Length of sequence is too high: %s' % (str(length_bins * self.get_binwidth())))
483
            return -1
484
485
    def set_preset(self, preset):
486
        cmd = 'swpreset={0}'.format(preset)
487
        self.dll.RunCmd(0, bytes(cmd, 'ascii'))
488
        return preset
489
490
    def set_cycles(self, cycles):
491
        cmd = 'cycles={0}'.format(cycles)
492
        self.dll.RunCmd(0, bytes(cmd, 'ascii'))
493
        return cycles
494
495
    def get_length(self):
496
        """ Get the length of the current measurement.
497
498
          @return int: length of the current measurement
499
        """
500
        setting = AcqSettings()
501
        self.dll.GetSettingData(ctypes.byref(setting), 0)
502
        return int(setting.range)
503
504
    def _change_filename(self,name):
505
        """ Changed the name in FCT"""
506
        cmd = 'datname=%s'%name
507
        self.dll.RunCmd(0, bytes(cmd, 'ascii'))
508
        return name
509
510
    def change_sweep_mode(self, gated):
511
        if gated:
512
            cmd = 'sweepmode={0}'.format(hex(1978500))
513
            self.dll.RunCmd(0, bytes(cmd, 'ascii'))
514
            cmd = 'prena={0}'.format(hex(16)) #To select starts preset
515
            # cmd = 'prena={0}'.format(hex(4)) #To select sweeps preset
516
            self.dll.RunCmd(0, bytes(cmd, 'ascii'))
517
            self.gated = True
518
        else:
519 View Code Duplication
            # fastcomtch standard settings for ungated acquisition (check manual)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
520
            cmd = 'sweepmode={0}'.format(hex(1978496))
521
            self.dll.RunCmd(0, bytes(cmd, 'ascii'))
522
            cmd = 'prena={0}'.format(hex(0))
523
            self.dll.RunCmd(0, bytes(cmd, 'ascii'))
524
            self.gated = False
525
        return gated
526
527
528
    def change_save_mode(self, mode):
529
        """ Changes the save mode of p7887
530
531
        @param int mode: Specifies the save mode (0: No Save at Halt, 1: Save at Halt,
532
                        2: Write list file, No Save at Halt, 3: Write list file, Save at Halt
533
534
        @return int mode: specified save mode
535
        """
536
        cmd = 'savedata={0}'.format(mode)
537
        self.dll.RunCmd(0, bytes(cmd, 'ascii'))
538
        return mode
539
540
    def set_delay_start(self, delay_s):
541
        """ Sets the record delay length
542
543
        @param int delay_s: Record delay after receiving a start trigger
544
545
        @return int mode: specified save mode
546
        """
547
548
        # A delay can only be adjusted in steps of 6.4ns
549
        delay_bins = np.rint(delay_s / 6.4e-9 /2.5)
550
        cmd = 'fstchan={0}'.format(int(delay_bins))
551
        self.dll.RunCmd(0, bytes(cmd, 'ascii'))
552
        return delay_bins
553
554
    def get_delay_start(self):
555
        """ Returns the current record delay length
556
557
        @return float delay_s: current record delay length in seconds
558
        """
559
        bsetting = AcqSettings()
560
        self.dll.GetSettingData(ctypes.byref(bsetting), 0)
561
        delay_s = bsetting.fstchan * 6.4e-9 *2.5
562
        #prena = bsetting.prena
563
        return delay_s
564
565
    # =========================================================================
566
    #   The following methods have to be carefully reviewed and integrated as
567
    #   internal methods/function, because they might be important one day.
568
    # =========================================================================
569
570
    def SetDelay(self, t):
571
        #~ setting = AcqSettings()
572
        #~ self.dll.GetSettingData(ctypes.byref(setting), 0)
573
        #~ setting.fstchan = t/6.4
574
        #~ self.dll.StoreSettingData(ctypes.byref(setting), 0)
575
        #~ self.dll.NewSetting(0)
576
        self.dll.RunCmd(0, 'DELAY={0:f}'.format(t))
577
        return self.GetDelay()
578
579
    def GetDelay(self):
580
        setting = AcqSettings()
581
        self.dll.GetSettingData(ctypes.byref(setting), 0)
582
        return setting.fstchan * 6.4
583
584
585
    #former SaveData_fast
586
    def SaveData_locally(self, filename, laser_index):
587
        # os.chdir(r'D:\data\FastComTec')
588
        data = self.get_data()
589
        fil = open(filename + '.asc', 'w')
590
        for i in laser_index:
591
            for n in data[i:i+int(round(3000/(self.minimal_binwidth*2**self.GetBitshift())))
592
                    +int(round(1000/(self.minimal_binwidth*2**self.GetBitshift())))]:
593
                fil.write('{0!s}\n'.format(n))
594
        fil.close()
595
596
    def SetLevel(self, start, stop):
597
        setting = AcqSettings()
598
        self.dll.GetSettingData(ctypes.byref(setting), 0)
599
        def FloatToWord(r):
600
            return int((r+2.048)/4.096*int('ffff',16))
601
        setting.dac0 = ( setting.dac0 & int('ffff0000',16) ) | FloatToWord(start)
602
        setting.dac1 = ( setting.dac1 & int('ffff0000',16) ) | FloatToWord(stop)
603
        self.dll.StoreSettingData(ctypes.byref(setting), 0)
604
        self.dll.NewSetting(0)
605
        return self.GetLevel()
606
607
    def GetLevel(self):
608
        setting = AcqSettings()
609
        self.dll.GetSettingData(ctypes.byref(setting), 0)
610
        def WordToFloat(word):
611
            return (word & int('ffff',16)) * 4.096 / int('ffff',16) - 2.048
612
        return WordToFloat(setting.dac0), WordToFloat(setting.dac1)
613
614
    #used in one script for SSR
615
    #Todo: Remove
616
    def Running(self):
617
        s = self.GetStatus()
618
        return s.started
619
620
    def GetStatus(self):
621
        status = AcqStatus()
622
        self.dll.GetStatusData(ctypes.byref(status), 0)
623
        return status
624
625
626
    def load_setup(self, configname):
627
        cmd = 'loadcnf={0}'.format(configname)
628
        self.dll.RunCmd(0, bytes(cmd, 'ascii'))