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

PicoHarp300.set_phr800_input()   B

Complexity

Conditions 5

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
dl 0
loc 38
rs 8.0894
c 1
b 0
f 0
1
# -*- coding: utf-8 -*-
2
"""
3
This file contains the Qudi hardware module for the PicoHarp300.
4
5
Qudi is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Qudi is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with Qudi. If not, see <http://www.gnu.org/licenses/>.
17
18
Copyright (c) the Qudi Developers. See the COPYRIGHT.txt file at the
19
top-level directory of this distribution and at <https://github.com/Ulm-IQO/qudi/>
20
"""
21
22
import ctypes
23
import numpy as np
24
import time
25
from qtpy import QtCore
26
27
from core.base import Base
28
from core.util.mutex import Mutex
29
from interface.slow_counter_interface import SlowCounterInterface
30
from interface.fast_counter_interface import FastCounterInterface
31
32
# =============================================================================
33
# Wrapper around the PHLib.DLL. The current file is based on the header files
34
# 'phdefin.h', 'phlib.h' and 'errorcodes.h'. The 'phdefin.h' contains all the
35
# constants and 'phlib.h' contains all the functions exported within the dll
36
# file. 'errorcodes.h' contains the possible error messages of the device.
37
#
38
# The wrappered commands are based on the PHLib Version 3.0. For further
39
# information read the manual
40
#       'PHLib - Programming Library for Custom Software Development'
41
# which can be downloaded from the PicoQuant homepage.
42
# =============================================================================
43
"""
44
The PicoHarp programming library PHLib.DLL is written in C and its data types
45
correspond to standard C/C++ data types as follows:
46
47
    char                    8 bit, byte (or characters in ASCII)
48
    short int               16 bit signed integer
49
    unsigned short int      16 bit unsigned integer
50
    int                     32 bit signed integer
51
    long int                32 bit signed integer
52
    unsigned int            32 bit unsigned integer
53
    unsigned long int       32 bit unsigned integer
54
    float                   32 bit floating point number
55
    double                  64 bit floating point number
56
"""
57
58
# Bitmask in hex.
59
# the comments behind each bitmask contain the integer value for the bitmask.
60
# You can check that by typing 'int(0x0001)' into the console to get the int.
61
62
#FEATURE_DLL     = 0x0001    #
63
#FEATURE_TTTR    = 0x0002    # 2
64
#FEATURE_MARKERS = 0x0004    # 4
65
#FEATURE_LOWRES  = 0x0008    # 8
66
#FEATURE_TRIGOUT = 0x0010    # 16
67
#
68
#FLAG_FIFOFULL   = 0x0003  # T-modes             # 3
69
#FLAG_OVERFLOW   = 0x0040  # Histomode           # 64
70
#FLAG_SYSERROR   = 0x0100  # Hardware problem    # 256
71
72
# The following are bitmasks for return values from GetWarnings()
73
#WARNING_INP0_RATE_ZERO         = 0x0001    # 1
74
#WARNING_INP0_RATE_TOO_LOW      = 0x0002    # 2
75
#WARNING_INP0_RATE_TOO_HIGH     = 0x0004    # 4
76
#
77
#WARNING_INP1_RATE_ZERO         = 0x0010    # 16
78
#WARNING_INP1_RATE_TOO_HIGH     = 0x0040    # 64
79
#
80
#WARNING_INP_RATE_RATIO         = 0x0100    # 256
81
#WARNING_DIVIDER_GREATER_ONE    = 0x0200    # 512
82
#WARNING_TIME_SPAN_TOO_SMALL    = 0x0400    # 1024
83
#WARNING_OFFSET_UNNECESSARY     = 0x0800    # 2048
84
85
86
class PicoHarp300(Base, SlowCounterInterface, FastCounterInterface):
87
    """Hardware class to control the Picoharp 300 from PicoQuant.
88
89
    This class is written according to the Programming Library Version 3.0
90
    STABLE AND TESTED VERSION: Alex S.
91
    """
92
    _modclass = 'PicoHarp300'
93
    _modtype = 'hardware'
94
95
    # declare connectors
96
    _out = {'picocounter': 'PicoHarp300',
97
            'counter': 'SlowCounterInterface'
98
            }
99
100
    sigReadoutPicoharp = QtCore.Signal()
101
    sigAnalyzeData = QtCore.Signal(object, object)
102
    sigStart = QtCore.Signal()
103
104
    def __init__(self, config, **kwargs):
105
        super().__init__(config=config, **kwargs)
106
107
        if 'deviceID' in config.keys():
108
            self._deviceID = config['deviceID']
109
        else:
110
            self.log.warning('Picoharp: No deviceID specified in the '
111
                    'config!\n'
112
                    'Devide ID = 0 will be taken, but without any '
113
                    'warranty to be able to connect now correctly to the '
114
                    'device.')
115
116
            self._deviceID = 0
117
118
        if 'mode' in config.keys():
119
            self._mode = config['mode']
120
        else:
121
            self.log.warning('Picoharp: No mode specified in the config!\n'
122
                        'Mode will be set to 0 (= Histogram Mode) as a '
123
                        'default.')
124
            self._mode = 0
125
126
        self.errorcode = self._create_errorcode()
127
        self._set_constants()
128
129
        # the library can communicate with 8 devices:
130
        self.connected_to_device = False
131
132
        #FIXME: Check which architecture the host PC is and choose the dll
133
        # according to that!
134
135
        # Load the picoharp library file phlib64.dll from the folder
136
        # <Windows>/System32/
137
        self._dll = ctypes.cdll.LoadLibrary('phlib64')
138
139
        # Just some default values:
140
        self._bin_width_ns = 3000
141
        self._record_length_ns = 100 *1e9
142
143
        self._photon_source2 = None #for compatibility reasons with second APD
144
        self._count_channel = 1
145
146
        #locking for thread safety
147
        self.threadlock = Mutex()
148
149
150
    def on_activate(self, fysom_e=None):
151
        """ Activate and establish the connection to Picohard and initialize.
152
153
        @param object e: Event class object from Fysom.
154
                         An object created by the state machine module Fysom,
155
                         which is connected to a specific event (have a look in
156
                         the Base Class). This object contains the passed event
157
                         the state before the event happens and the destination
158
                         of the state which should be reached after the event
159
                         has happen.
160
        """
161
        self.open_connection()
162
        self.initialize(self._mode)
163
        self.calibrate()
164
165
        #FIXME: These are default values determined from the measurement
166
        # One need still to include this in the config.
167
        self.set_input_CFD(1,10,7)
168
169
        # the signal has one argument of type object, which should allow
170
        # anything to pass through:
171
172
        self.sigStart.connect(self.start_measure)
173
        self.sigReadoutPicoharp.connect(self.get_fresh_data_loop, QtCore.Qt.QueuedConnection) # ,QtCore.Qt.QueuedConnection
174
        self.sigAnalyzeData.connect(self.analyze_received_data, QtCore.Qt.QueuedConnection)
175
        self.result = []
176
177
178
    def on_deactivate(self, fysom_e=None):
179
        """ Deactivates and disconnects the device.
180
181
        @param object e: Event class object from Fysom. Detailed explanation
182
                         see in method 'activation'.
183
        """
184
185
        self.close_connection()
186
        self.sigReadoutPicoharp.disconnect()
187
        self.sigAnalyzeData.disconnect()
188
189
    def _create_errorcode(self):
190
        """ Create a dictionary with the errorcode for the device.
191
192
        @return dict: errorcode in a dictionary
193
194
        The errorcode is extracted of PHLib  Ver. 3.0, December 2013. The
195
        errorcode can be also extracted by calling the get_error_string method
196
        with the appropriate integer value.
197
        """
198
199
        maindir = self.get_main_dir()
200
201
        filename = os.path.join(maindir, 'hardware', 'PicoQuant', 'errorcodes.h')
202
        try:
203
            with open(filename) as f:
204
                content = f.readlines()
205
        except:
206
            self.log.error('No file "errorcodes.h" could be found in the '
207
                        'PicoHarp hardware directory!')
208
209
        errorcode = {}
210
        for line in content:
211
            if '#define ERROR' in line:
212
                errorstring, errorvalue = line.split()[-2:]
213
                errorcode[int(errorvalue)] = errorstring
214
215
        return errorcode
216
217
    def _set_constants(self):
218
        """Set the constants (max and min values) for the Picoharp300 device.
219
        These setting are taken from phdefin.h"""
220
221
        self.MODE_HIST = 0
222
        self.MODE_T2 = 2
223
        self.MODE_T3 = 3
224
225
        # in mV:
226
        self.ZCMIN = 0
227
        self.ZCMAX = 20
228
        self.DISCRMIN = 0
229
        self.DISCRMAX = 800
230
        self.PHR800LVMIN = -1600
231
        self.PHR800LVMAX = 2400
232
233
        # in ps:
234
        self.OFFSETMIN = 0
235
        self.OFFSETMAX = 1000000000
236
        self.SYNCOFFSMIN = -99999
237
        self.SYNCOFFSMAX	= 99999
238
239
        # in ms:
240
        self.ACQTMIN = 1
241
        self.ACQTMAX = 10*60*60*1000
242
        self.TIMEOUT = 80   # the maximal device timeout for a readout request
243
244
        # in ns:
245
        self.HOLDOFFMAX = 210480
246
247
        self.BINSTEPSMAX = 8
248
        self.HISTCHAN = 65536    # number of histogram channels 2^16
249
        self.TTREADMAX = 131072  # 128K event records (2^17)
250
251
        # in Hz:
252
        self.COUNTFREQ = 10
253
254
    def check(self, func_val):
255
        """ Check routine for the received error codes.
256
257
        @param func_val int: return error code of the called function.
258
259
        @return int: pass the error code further so that other functions have
260
                     the possibility to use it.
261
262
        Each called function in the dll has an 32-bit return integer, which
263
        indicates, whether the function was called and finished successfully
264
        (then func_val = 0) or if any error has occured (func_val < 0). The
265
        errorcode, which corresponds to the return value can be looked up in
266
        the file 'errorcodes.h'.
267
        """
268
269
        if not func_val == 0:
270
            self.log.error('Error in PicoHarp300 with errorcode {0}:\n'
271
                        '{1}'.format(func_val, self.errorcode[func_val]))
272
        return func_val
273
274
    # =========================================================================
275
    # These two function below can be accessed without connection to device.
276
    # =========================================================================
277
278
    def get_version(self):
279
        """ Get the software/library version of the device.
280
281
        @return string: string representation of the
282
                        Version number of the current library."""
283
        buf = ctypes.create_string_buffer(16)   # at least 8 byte
284
        self.check(self._dll.PH_GetLibraryVersion(ctypes.byref(buf)))
285
        return buf.value # .decode() converts byte to string
286
287
    def get_error_string(self, errcode):
288
        """ Get the string error code from the Picoharp Device.
289
290
        @param int errcode: errorcode from 0 and below.
291
292
        @return byte: byte representation of the string error code.
293
294
        The stringcode for the error is the same as it is extracted from the
295
        errorcodes.h header file. Note that errcode should have the value 0
296
        or lower, since interger bigger 0 are not defined as error.
297
        """
298
299
        buf = ctypes.create_string_buffer(80)   # at least 40 byte
300
        self.check(self._dll.PH_GetErrorString(ctypes.byref(buf), errcode))
301
        return buf.value.decode() # .decode() converts byte to string
302
303
    # =========================================================================
304
    # Establish the connection and initialize the device or disconnect it.
305
    # =========================================================================
306
307
    def open_connection(self):
308
        """ Open a connection to this device. """
309
310
311
        buf = ctypes.create_string_buffer(16)   # at least 8 byte
312
        ret = self.check(self._dll.PH_OpenDevice(self._deviceID, ctypes.byref(buf)))
313
        self._serial = buf.value.decode()   # .decode() converts byte to string
314
        if ret >= 0:
315
            self.connected_to_device = True
316
            self.log.info('Connection to the Picoharp 300 established')
317
318
    def initialize(self, mode):
319
        """ Initialize the device with one of the three possible modes.
320
321
        @param int mode:    0: histogramming
322
                            2: T2
323
                            3: T3
324
        """
325
        mode = int(mode)    # for safety reasons, convert to integer
326
        self._mode = mode
327
328
        if not ((mode != self.MODE_HIST) or (mode != self.MODE_T2) or \
329
                (mode != self.MODE_T3)):
330
            self.log.error('Picoharp: Mode for the device could not be set. '
331
                    'It must be {0}=Histogram-Mode, {1}=T2-Mode or '
332
                    '{2}=T3-Mode, but a parameter {3} was '
333
                    'passed.'.format(
334
                        self.MODE_HIST,
335
                        self.MODE_T2,
336
                        self.MODE_T3,
337
                        mode))
338
        else:
339
            self.check(self._dll.PH_Initialize(self._deviceID, mode))
340
341
    def close_connection(self):
342
        """Close the connection to the device.
343
344
        @param int deviceID: a divice index from 0 to 7.
345
        """
346
        self.connected_to_device = False
347
        self.check(self._dll.PH_CloseDevice(self._deviceID))
348
        self.log.info('Connection to the Picoharp 300 closed.')
349
350
#    def __del__(self):
351
#        """ Delete the object PicoHarp300."""
352
#        self.close()
353
354
    # =========================================================================
355
    # All functions below can be used if the device was successfully called.
356
    # =========================================================================
357
358
    def get_hardware_info(self):
359
        """ Retrieve the device hardware information.
360
361
        @return string tuple(3): (Model, Partnum, Version)
362
        """
363
364
        model = ctypes.create_string_buffer(32)     # at least 16 byte
365
        version = ctypes.create_string_buffer(16)   # at least 8 byte
366
        partnum = ctypes.create_string_buffer(16)   # at least 8 byte
367
        self.check(self._dll.PH_GetHardwareInfo(self._deviceID, ctypes.byref(model),
368
                                                    ctypes.byref(partnum), ctypes.byref(version)))
369
370
        # the .decode() function converts byte objects to string objects
371
        return (model.value.decode(), partnum.value.decode(), version.value.decode())
372
373
    def get_serial_number(self):
374
        """ Retrieve the serial number of the device.
375
376
        @return string: serial number of the device
377
        """
378
379
        serialnum = ctypes.create_string_buffer(16)   # at least 8 byte
380
        self.check(self._dll.PH_GetSerialNumber(self._deviceID, ctypes.byref(serialnum)))
381
        return serialnum.value.decode() # .decode() converts byte to string
382
383
    def get_base_resolution(self):
384
        """ Retrieve the base resolution of the device.
385
386
        @return double: the base resolution of the device
387
        """
388
389
        res = ctypes.c_double()
390
        self.check(self._dll.PH_GetBaseResolution(self._deviceID, ctypes.byref(res)))
391
        return res.value
392
393
    def calibrate(self):
394
        """ Calibrate the device."""
395
        self.check(self._dll.PH_Calibrate(self._deviceID))
396
397
    def get_features(self):
398
        """ Retrieve the possible features of the device.
399
400
        @return int: a bit pattern indicating the feature.
401
        """
402
        features = ctypes.c_int32()
403
        self.check(self._dll.PH_GetFeatures(self._deviceID, ctypes.byref(features)))
404
        return features.value
405
406 View Code Duplication
    def set_input_CFD(self, channel, level, zerocross):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
407
        """ Set the Constant Fraction Discriminators for the Picoharp300.
408
409
        @param int channel: number (0 or 1) of the input channel
410
        @param int level: CFD discriminator level in millivolts
411
        @param int zerocross: CFD zero cross in millivolts
412
        """
413
        channel = int(channel)
414
        level = int(level)
415
        zerocross = int(zerocross)
416
        if channel not in (0, 1):
417
            self.log.error('PicoHarp: Channal does not exist.\nChannel has '
418
                    'to be 0 or 1 but {0} was passed.'.format(channel))
419
            return
420
        if not(self.DISCRMIN <= level <= self.DISCRMAX):
421
            self.log.error('PicoHarp: Invalid CFD level.\nValue must be '
422
                        'within the range [{0},{1}] millivolts but a value of '
423
                        '{2} has been '
424
                        'passed.'.format(self.DISCRMIN, self.DISCRMAX, level))
425
            return
426
        if not(self.ZCMIN <= zerocross <= self.ZCMAX):
427
            self.log.error('PicoHarp: Invalid CFD zero cross.\nValue must be '
428
                        'within the range [{0},{1}] millivolts but a value of '
429
                        '{2} has been '
430
                        'passed.'.format(self.ZCMIN, self.ZCMAX, zerocross))
431
            return
432
433
        self.check(self._dll.PH_SetInputCFD(self._deviceID, channel, level, zerocross))
434
435
436
    def set_sync_div(self, div):
437
        """ Synchronize the devider of the device.
438
439
        @param int div: input rate devider applied at channel 0 (1,2,4, or 8)
440
441
        The sync devider must be used to keep the  effective sync rate at
442
        values <= 10MHz. It should only be used with sync sources of stable
443
        period. The readins obtained with PH_GetCountRate are corrected for the
444
        devider settin and deliver the external (undivided) rate.
445
        """
446
        if not ( (div !=1) or (div !=2) or (div !=4) or (div !=8) ):
447
            self.log.error('PicoHarp: Invalid sync devider.\n'
448
                        'Value must be 1, 2, 4 or 8 but a value of {0} was '
449
                        'passed.'.format(div))
450
            return
451
        else:
452
            self.check(self._dll.PH_SetSyncDiv(self._deviceID, div))
453
454
    def set_sync_offset(self, offset):
455
        """ Set the offset of the synchronization.
456
457
        @param int offset: offset (time shift) in ps for that channel. That
458
                           value must lie within the range of SYNCOFFSMIN and
459
                           SYNCOFFSMAX.
460
        """
461
        offset = int(offset)
462
        if not(self.SYNCOFFSMIN <= offset <= self.SYNCOFFSMAX):
463
            self.log.error('PicoHarp: Invalid Synchronization offset.\nValue '
464
                    'must be within the range [{0},{1}] ps but a value of '
465
                    '{2} has been passed.'.format(
466
                        self.SYNCOFFSMIN, self.SYNCOFFSMAX, offset))
467
        else:
468
            self.check(self._dll.PH_SetSyncOffset(self._deviceID, offset))
469
470
471
    def set_stop_overflow(self, stop_ovfl, stopcount):
472
        """ Stop the measurement if maximal amount of counts is reached.
473
474
        @param int stop_ovfl:  0 = do not stop,
475
                               1 = do stop on overflow
476
        @param int stopcount: count level at which should be stopped
477
                              (maximal 65535).
478
479
        This setting determines if a measurement run will stop if any channel
480
        reaches the maximum set by stopcount. If stop_ofl is 0 the measurement
481
        will continue but counts above 65535 in any bin will be clipped.
482
        """
483
        if stop_ovfl not in (0, 1):
484
            self.log.error('PicoHarp: Invalid overflow parameter.\n'
485
                        'The overflow parameter must be either 0 or 1 but a '
486
                        'value of {0} was passed.'.format(stop_ovfl))
487
            return
488
489
        if not(0 <= stopcount <= self.HISTCHAN):
490
            self.log.error('PicoHarp: Invalid stopcount parameter.\n'
491
                        'stopcount must be within the range [0,{0}] but a '
492
                        'value of {1} was passed.'.format(self.HISTCHAN, stopcount))
493
            return
494
495
        return self.check(self._dll.PH_SetStopOverflow(self._deviceID, stop_ovfl, stopcount))
496
497
    def set_binning(self, binning):
498
        """ Set the base resolution of the measurement.
499
500
        @param int binning: binning code
501
                                minimum = 0 (smallest, i.e. base resolution)
502
                                maximum = (BINSTEPSMAX-1) (largest)
503
504
        The binning code corresponds to a power of 2, i.e.
505
            0 = base resolution,        => 4*2^0 =    4ps
506
            1 =   2x base resolution,     => 4*2^1 =    8ps
507
            2 =   4x base resolution,     => 4*2^2 =   16ps
508
            3 =   8x base resolution      => 4*2^3 =   32ps
509
            4 =  16x base resolution      => 4*2^4 =   64ps
510
            5 =  32x base resolution      => 4*2^5 =  128ps
511
            6 =  64x base resolution      => 4*2^6 =  256ps
512
            7 = 128x base resolution      => 4*2^7 =  512ps
513
514
        These are all the possible values. In histogram mode the internal
515
        buffer can store 65535 points (each a 32bit word). For largest
516
        resolution you can count  33.55392 ms in total
517
518
        """
519
        if not(0 <= binning < self.BINSTEPSMAX):
520
            self.log.error('PicoHarp: Invalid binning.\nValue must be within '
521
                    'the range [{0},{1}] bins, but a value of {2} has been '
522
                    'passed.'.format(0, self.BINSTEPSMAX, binning))
523
        else:
524
            self.check(self._dll.PH_SetBinning(self._deviceID, binning))
525
526
    def set_multistop_enable(self, enable=True):
527
        """ Set whether multistops are possible within a measurement.
528
529
        @param bool enable: optional, Enable or disable the mutlistops.
530
531
        This is only for special applications where the multistop feature of
532
        the Picoharp is causing complications in statistical analysis. Usually
533
        it is not required to call this function. By default, multistop is
534
        enabled after PH_Initialize.
535
        """
536
        if enable:
537
            self.check(self._dll.PH_SetMultistopEnable(self._deviceID, 1))
538
        else:
539
            self.check(self._dll.PH_SetMultistopEnable(self._deviceID, 0))
540
541
    def set_offset(self, offset):
542
        """ Set an offset time.
543
544
        @param int offset: offset in ps (only possible for histogramming and T3
545
                           mode!). Value must be within [OFFSETMIN,OFFSETMAX].
546
547
        The true offset is an approximation fo the desired offset by the
548
        nearest multiple of the base resolution. This offset only acts on the
549
        difference between ch1 and ch0 in hitogramming and T3 mode. Do not
550
        confuse it with the input offsets!
551
        """
552
        if not(self.OFFSETMIN <= offset <= self.OFFSETMAX):
553
            self.log.error('PicoHarp: Invalid offset.\nValue must be within '
554
                    'the range [{0},{1}] ps, but a value of {2} has been '
555
                    'passed.'.format(self.OFFSETMIN, self.OFFSETMAX, offset))
556
        else:
557
            self.check(self._dll.PH_SetOffset(self._deviceID, offset))
558
559
    def clear_hist_memory(self, block=0):
560
        """ Clear the histogram memory.
561
562
        @param int block: set which block number to clear.
563
        """
564
        self.check(self._dll.PH_ClearHistMem(self._deviceID, block))
565
566
    def start(self, acq_time):
567
        """ Start acquisition for 'acq_time' ms.
568
569
        @param int acq_time: acquisition time in miliseconds. The value must be
570
                             be within the range [ACQTMIN,ACQTMAX].
571
        """
572
        if not(self.ACQTMIN <= acq_time <= self.ACQTMAX):
573
            self.log.error('PicoHarp: No measurement could be started.\n'
574
                'The acquisition time must be within the range [{0},{1}] '
575
                'ms, but a value of {2} has been passed.'
576
                ''.format(self.ACQTMIN, self.ACQTMAX, acq_time))
577
        else:
578
            self.check(self._dll.PH_StartMeas(self._deviceID, int(acq_time)))
579
580
    def stop_device(self):
581
        """ Stop the measurement."""
582
        self.check(self._dll.PH_StopMeas(self._deviceID))
583
        self.meas_run = False
584
585
    def _get_status(self):
586
        """ Check the status of the device.
587
588
        @return int:  = 0: acquisition time still running
589
                      > 0: acquisition time has ended, measurement finished.
590
        """
591
        ctcstatus = ctypes.c_int32()
592
        self.check(self._dll.PH_CTCStatus(self._deviceID, ctypes.byref(ctcstatus)))
593
        return ctcstatus.value
594
595
    def get_histogram(self, block=0, xdata=True):
596
        """ Retrieve the measured histogram.
597
598
        @param int block: the block number to fetch (block >0 is only
599
                          meaningful with routing)
600
        @param bool xdata: if true, the x values in ns corresponding to the
601
                           read array will be returned.
602
603
        @return numpy.array[65536] or  numpy.array[65536], numpy.array[65536]:
604
                        depending if xdata = True, also the xdata are passed in
605
                        ns.
606
607
        """
608
        chcount = np.zeros((self.HISTCHAN,), dtype=np.uint32)
609
        # buf.ctypes.data is the reference to the array in the memory.
610
        self.check(self._dll.PH_GetHistogram(self._deviceID, chcount.ctypes.data, block))
611
        if xdata:
612
            xbuf = np.arange(self.HISTCHAN) * self.get_resolution() / 1000
613
            return xbuf, chcount
614
        return chcount
615
616
    def get_resolution(self):
617
        """ Retrieve the current resolution of the picohard.
618
619
        @return double: resolution at current binning.
620
        """
621
622
        resolution = ctypes.c_double()
623
        self.check(self._dll.PH_GetResolution(self._deviceID, ctypes.byref(resolution)))
624
        return resolution.value
625
626
    def get_count_rate(self, channel):
627
        """ Get the current count rate for the
628
629
        @param int channel: which input channel to read (0 or 1):
630
631
        @return int: count rate in ps.
632
633
        The hardware rate meters emply a gate time of 100ms. You must allow at
634
        least 100ms after PH_Initialize or PH_SetDyncDivider to get a valid
635
        rate meter reading. Similarly, wait at least 100ms to get a new
636
        reading. The readings are corrected for the snyc devider setting and
637
        deliver the external (undivided) rate. The gate time cannot be changed.
638
        The readings may therefore be inaccurate of fluctuating when the rate
639
        are very low. If accurate rates are needed you must perform a full
640
        blown measurement and sum up the recorded events.
641
        """
642
        if not ((channel !=0) or (channel != 1)):
643
            self.log.error('PicoHarp: Count Rate could not be read out, '
644
                    'Channel does not exist.\nChannel has to be 0 or 1 '
645
                    'but {0} was passed.'.format(channel))
646
            return -1
647
        else:
648
            rate = ctypes.c_int32()
649
            self.check(self._dll.PH_GetCountRate(self._deviceID, channel, ctypes.byref(rate)))
650
            return rate.value
651
652
    def get_flags(self):
653
        """ Get the current status flag as a bit pattern.
654
655
        @return int: the current status flags (a bit pattern)
656
657
        Use the predefined bit mask values in phdefin.h (e.g. FLAG_OVERFLOW) to
658
        extract indiviual bits though a bitwise AND. It is also recommended to
659
        check for FLAG_SYSERROR to detect possible hardware failures. In that
660
        case you may want to call PH_GetHardwareDebugInfo and submit the
661
        results to support.
662
        """
663
664
        flags = ctypes.c_int32()
665
        self.check(self._dll.PH_GetFlags(self._deviceID, ctypes.byref(flags)))
666
        return flags.value
667
668
    def get_elepased_meas_time(self):
669
        """ Retrieve the elapsed measurement time in ms.
670
671
        @return double: the elapsed measurement time in ms.
672
        """
673
        elapsed = ctypes.c_double()
674
        self.check(self._dll.PH_GetElapsedMeasTime(self._deviceID, ctypes.byref(elapsed)))
675
        return elapsed.value
676
677
    def get_warnings(self):
678
        """Retrieve any warnings about the device or the current measurement.
679
680
        @return int: a bitmask for the warnings, as defined in phdefin.h
681
682
        NOTE: you have to call PH_GetCountRates for all channels prior to this
683
              call!
684
        """
685
        warnings = ctypes.c_int32()
686
        self.check(self._dll.PH_GetWarnings(self._deviceID, ctypes.byref(warnings)))
687
        return warnings.value
688
689
    def get_warnings_text(self, warning_num):
690
        """Retrieve the warningtext for the corresponding warning bitmask.
691
692
        @param int warning_num: the number for which you want to have the
693
                                warning text.
694
        @return char[32568]: the actual text of the warning.
695
696
        """
697
        text = ctypes.create_string_buffer(32568) # buffer at least 16284 byte
698
        self.check(self._dll.PH_GetWarningsText(self._deviceID, warning_num, text))
699
        return text.value
700
701
    def get_hardware_debug_info(self):
702
        """ Retrieve the debug information for the current hardware.
703
704
        @return char[32568]: the information for debugging.
705
        """
706
        debuginfo = ctypes.create_string_buffer(32568) # buffer at least 16284 byte
707
        self.check(self._dll.PH_GetHardwareDebugInfo(self._deviceID, debuginfo))
708
        return debuginfo.value
709
710
    # =========================================================================
711
    #  Special functions for Time-Tagged Time Resolved mode
712
    # =========================================================================
713
    # To check whether you can use the TTTR mode (must be purchased in
714
    # addition) you can call PH_GetFeatures to check.
715
716
    def tttr_read_fifo(self):#, num_counts):
717
        """ Read out the buffer of the FIFO.
718
719
        @param int num_counts: number of TTTR records to be fetched. Maximal
720
                               TTREADMAX
721
722
        @return tuple (buffer, actual_num_counts):
723
                    buffer = data array where the TTTR data are stored.
724
                    actual_num_counts = how many numbers of TTTR could be
725
                                        actually be read out. THIS NUMBER IS
726
                                        NOT CHECKED FOR PERFORMANCE REASONS, SO
727
                                        BE  CAREFUL! Maximum is TTREADMAX.
728
729
        THIS FUNCTION SHOULD BE CALLED IN A SEPARATE THREAD!
730
731
        Must not be called with count larger than buffer size permits. CPU time
732
        during wait for completion will be yielded to other processes/threads.
733
        Function will return after a timeout period of 80 ms even if not all
734
        data could be fetched. Return value indicates how many records were
735
        fetched. Buffer must not be accessed until the function returns!
736
        """
737
738
        # if type(num_counts) is not int:
739
        #     num_counts = self.TTREADMAX
740
        # elif (num_counts<0) or (num_counts>self.TTREADMAX):
741
        #     self.log.error('PicoHarp: num_counts were expected to within the '
742
        #                 'interval [0,{0}], but a value of {1} was '
743
        #                 'passed'.format(self.TTREADMAX, num_counts))
744
        #     num_counts = self.TTREADMAX
745
746
        # PicoHarp T3 Format (for analysis and interpretation):
747
        # The bit allocation in the record for the 32bit event is, starting
748
        # from the MSB:
749
        #       channel:     4 bit
750
        #       dtime:      12 bit
751
        #       nsync:      16 bit
752
        # The channel code 15 (all bits ones) marks a special record.
753
        # Special records can be overflows or external markers. To
754
        # differentiate this, dtime must be checked:
755
        #
756
        #     If it is zero, the record marks an overflow.
757
        #     If it is >=1 the individual bits are external markers.
758
759
        num_counts = self.TTREADMAX
760
761
        buffer = np.zeros((num_counts,), dtype=np.uint32)
762
763
        actual_num_counts = ctypes.c_int32()
764
765
        self.check(self._dll.PH_ReadFiFo(self._deviceID, buffer.ctypes.data,
766
                                         num_counts, ctypes.byref(actual_num_counts)))
767
768
769
        return (buffer, actual_num_counts.value)
770
771
    def tttr_set_marker_edges(self, me0, me1, me2, me3):
772
        """ Set the marker edges
773
774
        @param int me<n>:   active edge of marker signal <n>,
775
                                0 = falling
776
                                1 = rising
777
778
        PicoHarp devices prior to hardware version 2.0 support only the first
779
        three markers. Default after Initialize is all rising, i.e. set to 1.
780
        """
781
782
        if (me0 != 0) or (me0 != 1) or (me1 != 0) or (me1 != 1) or \
783
           (me2 != 0) or (me2 != 1) or (me3 != 0) or (me3 != 1):
784
785
            self.log.error('PicoHarp: All the marker edges must be either 0 '
786
                    'or 1, but the current marker settings were passed:\n'
787
                    'me0={0}, me1={1}, '
788
                    'me2={2}, me3={3},'.format(me0, me1, me2, me3))
789
            return
790
        else:
791
            self.check(self._dll.PH_TTSetMarkerEdges(self._deviceID, me0, me1,
792
                                                      me2, me3))
793
794
    def tttr_set_marker_enable(self, me0, me1, me2, me3):
795
        """ Set the marker enable or not.
796
797
        @param int me<n>:   enabling of marker signal <n>,
798
                                0 = disabled
799
                                1 = enabled
800
801
        PicoHarp devices prior to hardware version 2.0 support only the first
802
        three markers. Default after Initialize is all rising, i.e. set to 1.
803
        """
804
805
#        if ((me0 != 0) or (me0 != 1)) or ((me1 != 0) or (me1 != 1)) or \
806
#           ((me2 != 0) or (me2 != 1)) or ((me3 != 0) or (me3 != 1)):
807
#
808
#            self.log.error('PicoHarp: Could not set marker enable.\n'
809
#                        'All the marker options must be either 0 or 1, but '
810
#                        'the current marker settings were passed:\n'
811
#                        'me0={0}, me1={1}, '
812
#                        'me2={2}, me3={3},'.format(me0, me1, me2, me3))
813
#            return
814
#        else:
815
        self.check(self._dll.PH_SetMarkerEnable(self._deviceID, me0,
816
                                                       me1, me2, me3))
817
818
    def tttr_set_marker_holdofftime(self, holfofftime):
819
        """ Set the holdofftime for the markers.
820
821
        @param int holdofftime: holdofftime in ns. Maximal value is HOLDOFFMAX.
822
823
        This setting can be used to clean up glitches on the marker signals.
824
        When set to X ns then after detecting a first marker edge the next
825
        marker will not be accepted before x ns. Observe that the internal
826
        granularity of this time is only about 50ns. The holdoff time is set
827
        equally for all marker inputs but the holdoff logic acts on each
828
        marker independently.
829
        """
830
831
        if not(0 <= holdofftime <= self.HOLDOFFMAX):
832
            self.log.error('PicoHarp: Holdofftime could not be set.\n'
833
                'Value of holdofftime must be within the range '
834
                '[0,{0}], but a value of {1} was passed.'
835
                ''.format(self.HOLDOFFMAX, holfofftime))
836
        else:
837
            self.check(self._dll.PH_SetMarkerHoldofftime(self._deviceID, holfofftime))
838
839
    # =========================================================================
840
    #  Special functions for Routing Devices
841
    # =========================================================================
842
    # If this functions wanted to be used, then you have to use the current
843
    # PicoHarp300 with a router device like PHR 402, PHR 403 or PHR 800.
844
845
    def get_routing_channels(self):
846
        """  Retrieve the number of routing channels.
847
848
        @param return int: The number of possible routing_channels.
849
        """
850
        routing_channels = ctypes.c_int32()
851
        self.check(self._dll.PH_GetRoutingChannels(
852
            self._deviceID, ctypes.byref(routing_channels)))
853
        return routing_channels.value
854
855
    def set_enable_routing(self, use_router):
856
        """ Configure whether the connected router is used or not.
857
858
        @param int use_router: 0 = enable routing
859
                               1 = disable routing
860
861
        Note: This function can also be used to detect the presence of a router!
862
        """
863
864
        return self.check(self._dll.PH_EnableRouting(self._deviceID, use_router))
865
866
    def get_router_version(self):
867
        """ Retrieve the model number and the router version.
868
869
        @return string list[2]: first entry will be the model number and second
870
                                entry the router version.
871
        """
872
        # pointer to a buffer for at least 8 characters:
873
        model_number = ctypes.create_string_buffer(16)
874
        version_number =  ctypes.create_string_buffer(16)
875
876
        self.check(self._dll.PH_GetRouterVersion(self._deviceID,
877
                                                 ctypes.byref(model_number),
878
                                                 ctypes.byref(version_number)))
879
880
        return [model_number.value.decode(), version_number.value.decode()]
881
882
    def set_routing_channel_offset(self, offset_time):
883
        """ Set the offset for the routed channels to compensate cable delay.
884
885
        @param int offset_time: offset (time shift) in ps for that channel.
886
                                Value must be within [OFFSETMIN,OFFSETMAX]
887
888
        Note: This function can be used to compensate small timing delays
889
              between the individual routing channels. It is similar to
890
              PH_SetSyncOffset and can replace cumbersome cable length
891
              adjustments but compared to PH_SetSyncOffset the adjustment range
892
              is relatively small. A positive number corresponds to inserting
893
              cable in that channel.
894
        """
895
896
        if not(self.OFFSETMIN <= offset_time <= self.OFFSETMAX):
897
            self.log.error('PicoHarp: Invalid offset time for routing.\nThe '
898
                        'offset time was expected to be within the interval '
899
                        '[{0},{1}] ps, but a value of {2} was passed.'
900
                        ''.format(self.OFFSETMIN, self.OFFSETMAX, offset_time))
901
            return
902
        else:
903
            self.check(self._dll.PH_SetRoutingChannelOffset(self._deviceID, offset_time))
904
905
    def set_phr800_input(self, channel, level, edge):
906
        """ Configure the input channels of the PHR800 device.
907
908
        @param int channel: which router channel is going to be programmed.
909
                            This number but be within the range [0,3].
910
        @param int level: set the trigger voltage level in mV. The entered
911
                          value must be within [PHR800LVMIN,PHR800LVMAX].
912
        @param int edge: Specify whether the trigger should be detected on
913
                            0 = falling edge or
914
                            1 = rising edge.
915
916
        Note: Not all channels my be present!
917
        Note: INVALID COMBINATIONS OF LEVEL AND EDGES MAY LOOK UP ALL CHANNELS!
918
        """
919
920
        channel = int(channel)
921
        level =  int(level)
922
        edge = int(edge)
923
924
        if channel not in range(0, 4):
925
            self.log.error('PicoHarp: Invalid channel for routing.\n'
926
                    'The channel must be within the interval [0,3], but a value '
927
                    'of {2} was passed.'.format(channel))
928
            return
929
        if not(self.PHR800LVMIN <= level <= self.PHR800LVMAX):
930
            self.log.error('PicoHarp: Invalid level for routing.\n'
931
                'The level used for channel {0} must be within the interval '
932
                '[{1},{2}] mV, but a value of {3} was passed.'
933
                ''.format(channel, self.PHR800LVMIN, self.PHR800LVMAX, level))
934
            return
935
        if (edge != 0) or (edge != 1):
936
            self.log.error('PicoHarp: Could not set edge.\n'
937
                        'The edge setting must be either 0 or 1, but the '
938
                        'current edge value {0} was '
939
                        'passed'.format(edge))
940
            return
941
942
        self.check(self._dll.PH_SetPHR800Input(self._deviceID, channel, level, edge))
943
944 View Code Duplication
    def set_phr800_cfd(self, channel, dscrlevel, zerocross):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
945
        """ Set the Constant Fraction Discriminators (CFD) for the PHR800 device.
946
947
        @param int channel: which router channel is going to be programmed.
948
                            This number but be within the range [0,3].
949
        @param dscrlevel: the discriminator level in mV, which must be within a
950
                          range of [DISCRMIN,DISCRMAX]
951
        """
952
953
        channel = int(channel)
954
        dscrlevel = int(dscrlevel)
955
        zerocross = int(zerocross)
956
957
        if channel not in range(0, 4):
958
            self.log.error('PicoHarp: Invalid channel for routing.\nThe '
959
                    'channel must be within the interval [0,3], but a value '
960
                    'of {2} has been passed.'.format(channel))
961
            return
962
        if not(self.DISCRMIN <= dscrlevel <= self.DISCRMAX):
963
            self.log.error('PicoHarp: Invalid Constant Fraction Discriminators '
964
                        'level.\nValue must be within the range [{0},{1}] '
965
                        ' millivolts but a value of {2} has been '
966
                        'passed.'.format(self.DISCRMIN, self.DISCRMAX, dscrlevel))
967
            return
968
        if not(self.ZCMIN <= zerocross <= self.ZCMAX):
969
            self.log.error('PicoHarp: Invalid CFD zero cross.\nValue must be '
970
                        'within the range [{0},{1}] millivolts but a value of '
971
                        '{2} has been '
972
                        'passed.'.format(self.ZCMIN, self.ZCMAX, zerocross))
973
            return
974
975
        self.check(self._dll.PH_SetPHR800CFD(self._deviceID, channel, dscrlevel, zerocross))
976
977
    # =========================================================================
978
    #  Higher Level function, which should be called directly from Logic
979
    # =========================================================================
980
981
982
    # =========================================================================
983
    #  Functions for the SlowCounter Interface
984
    # =========================================================================
985
986
    def set_up_clock(self, clock_frequency = None, clock_channel = None):
987
        """ Set here which channel you want to access of the Picoharp.
988
989
        @param float clock_frequency: Sets the frequency of the clock. That
990
                                      frequency will not be taken. It is not
991
                                      needed, and argument will be omitted.
992
        @param string clock_channel: This is the physical channel
993
                                     of the clock. It is not needed, and
994
                                     argument will be omitted.
995
996
        The Hardware clock for the Picoharp is not programmable. It is a gated
997
        counter every 100ms. That you cannot change. You can retrieve from both
998
        channels simultaneously the count rates.
999
1000
        @return int: error code (0:OK, -1:error)
1001
        """
1002
        self.log.info('Picoharp: The Hardware clock for the Picoharp is not '
1003
                    'programmable!\n'
1004
                    'It is a gated counter every 100ms. That you cannot change. '
1005
                    'You can retrieve from both channels simultaneously the '
1006
                    'count rates.')
1007
1008
        return 0
1009
1010
    def set_up_counter(self, counter_channel = 1, photon_source = None,
1011
                       clock_channel = None):
1012
        """ Ensure Interface compatibility. The counter allows no set up.
1013
1014
        @param string counter_channel: Set the actual channel which you want to
1015
                                       read out. Default it is 0. It can
1016
                                       also be 1.
1017
        @param string photon_source: is not needed, arg will be omitted.
1018
        @param string clock_channel: is not needed, arg will be omitted.
1019
1020
        @return int: error code (0:OK, -1:error)
1021
        """
1022
        self._count_channel = counter_channel
1023
        self.log.info('Picoharp: The counter allows no set up!\n'
1024
                    'The implementation of this command ensures Interface '
1025
                    'compatibility.')
1026
1027
        #FIXME: make the counter channel chooseable in config
1028
        #FIXME: add second photon source either to config or in a better way to file
1029
        return 0
1030
1031
    def get_counter(self, samples=None):
1032
        """ Returns the current counts per second of the counter.
1033
1034
        @param int samples: if defined, number of samples to read in one go
1035
1036
        @return float: the photon counts per second
1037
        """
1038
        time.sleep(0.05)
1039
        return [self.get_count_rate(self._count_channel)]
1040
1041
    def close_counter(self):
1042
        """ Closes the counter and cleans up afterwards. Actually, you do not
1043
        have to do anything with the picoharp. Therefore this command will do
1044
        nothing and is only here for SlowCounterInterface compatibility.
1045
1046
        @return int: error code (0:OK, -1:error)
1047
        """
1048
        return 0
1049
1050
    def close_clock(self):
1051
        """Closes the clock and cleans up afterwards.. Actually, you do not
1052
        have to do anything with the picoharp. Therefore this command will do
1053
        nothing and is only here for SlowCounterInterface compatibility.
1054
1055
        @return int: error code (0:OK, -1:error)
1056
        """
1057
        return 0
1058
1059
    # =========================================================================
1060
    #  Functions for the FastCounter Interface
1061
    # =========================================================================
1062
1063
    #FIXME: The interface connection to the fast counter must be established!
1064
1065
    def configure(self, bin_width_ns, record_length_ns, number_of_gates = 0):
1066
        """
1067
        Configuration of the fast counter.
1068
        bin_width_ns: Length of a single time bin in the time trace histogram
1069
                      in nanoseconds.
1070
        record_length_ns: Total length of the timetrace/each single gate in
1071
                          nanoseconds.
1072
        number_of_gates: Number of gates in the pulse sequence. Ignore for
1073
                         ungated counter.
1074
        """
1075
#        self.initialize(mode=3)
1076
        self._bin_width_ns = bin_width_ns
1077
        self._record_length_ns = record_length_ns
1078
        self._number_of_gates = number_of_gates
1079
1080
        #FIXME: actualle only an unsigned array will be needed. Change that later.
1081
#        self.data_trace = np.zeros(number_of_gates, dtype=np.int64 )
1082
        self.data_trace = [0]*number_of_gates
1083
        self.count = 0
1084
1085
        self.result = []
1086
        self.initialize(2)
1087
        return
1088
1089
    def get_status(self):
1090
        """
1091
        Receives the current status of the Fast Counter and outputs it as
1092
        return value.
1093
        0 = unconfigured
1094
        1 = idle
1095
        2 = running
1096
        3 = paused
1097
        -1 = error state
1098
        """
1099
        if not self.connected_to_device:
1100
            return -1
1101
        else:
1102
            returnvalue = self._get_status()
1103
            if returnvalue == 0:
1104
                return 2
1105
            else:
1106
                return 1
1107
1108
1109
    def pause_measure(self):
1110
        """
1111
        Pauses the current measurement if the fast counter is in running state.
1112
        """
1113
1114
        self.stop_measure()
1115
        self.meas_run = False
1116
1117
    def continue_measure(self):
1118
        """
1119
        Continues the current measurement if the fast counter is in pause state.
1120
        """
1121
        self.meas_run = True
1122
        self.start(self._record_length_ns/1e6)
1123
1124
    def is_gated(self):
1125
        """
1126
        Boolean return value indicates if the fast counter is a gated counter
1127
        (TRUE) or not (FALSE).
1128
        """
1129
        return False
1130
1131
    def get_binwidth(self):
1132
        """
1133
        returns the width of a single timebin in the timetrace in seconds
1134
        """
1135
        #FIXME: Must be implemented
1136
        return 2e-9
1137
1138
    def get_data_trace(self):
1139
        """
1140
        Polls the current timetrace data from the fast counter and returns it
1141
        as a numpy array (dtype = int64). The binning specified by calling
1142
        configure() must be taken care of in this hardware class. A possible
1143
        overflow of the histogram bins must be caught here and taken care of.
1144
          - If the counter is NOT gated it will return a 1D-numpy-array with
1145
            returnarray[timebin_index].
1146
          - If the counter is gated it will return a 2D-numpy-array with
1147
            returnarray[gate_index, timebin_index]
1148
        """
1149
1150
1151
        return self.data_trace
1152
1153
1154
1155
    # =========================================================================
1156
    #  Test routine for continuous readout
1157
    # =========================================================================
1158
1159
1160
    def start_measure(self):
1161
        """
1162
        Starts the fast counter.
1163
        """
1164
        self.lock()
1165
1166
        self.meas_run = True
1167
1168
        # start the device:
1169
        self.start(int(self._record_length_ns/1e6))
1170
1171
        self.sigReadoutPicoharp.emit()
1172
1173
    def stop_measure(self):
1174
        """ By setting the Flag, the measurement should stop.  """
1175
        self.meas_run = False
1176
1177
1178
    def get_fresh_data_loop(self):
1179
        """ This method will be run infinitely until the measurement stops. """
1180
1181
        # for testing one can also take another array:
1182
        buffer, actual_counts = self.tttr_read_fifo()
1183
#        buffer, actual_counts = [1,2,3,4,5,6,7,8,9], 9
1184
1185
        # This analysis signel should be analyzed in a queued thread:
1186
        self.sigAnalyzeData.emit(buffer[0:actual_counts-1], actual_counts)
1187
1188
        if not self.meas_run:
1189
            with self.threadlock:
1190
                self.unlock()
1191
                self.stop_device
1192
                return
1193
1194
        print('get new data.')
1195
        # get the next data:
1196
        self.sigReadoutPicoharp.emit()
1197
1198
1199
1200
    def analyze_received_data(self, arr_data, actual_counts):
1201
        """ Analyze the actual data obtained from the TTTR mode of the device.
1202
1203
        @param arr_data: numpy uint32 array with length 'actual_counts'.
1204
        @param actual_counts: int, number of read out events from the buffer.
1205
1206
        Write the obtained arr_data to the predefined array data_trace,
1207
        initialized in the configure method.
1208
1209
        The received array contains 32bit words. The bit assignment starts from
1210
        the MSB (most significant bit), which is here displayed as the most
1211
        left bit.
1212
1213
        For T2 (initialized device with mode=2):
1214
        ----------------------------------------
1215
1216
        [ 4 bit for channel-number |28 bit for time-tag] = [32 bit word]
1217
1218
        channel-number: 4 marker, which serve for the different channels.
1219
                            0001 = marker 1
1220
                            0010 = marker 2
1221
                            0011 = marker 3
1222
                            0100 = marker 4
1223
1224
                        The channel code 15 (all bits ones, 1111) marks a
1225
                        special record. Special records can be overflows or
1226
                        external markers. To differentiate this, the lower 4
1227
                        bits of timetag must be checked:
1228
                            - If they are all zero, the record marks an
1229
                              overflow.
1230
                            - If they are >=1 the individual bits are external
1231
                              markers.
1232
1233
                        Overflow period: 210698240
1234
1235
                        the first bit is the overflow bit. It will be set if
1236
                        the time-tag reached 2^28:
1237
1238
                            0000 = overflow
1239
1240
                        Afterwards both overflow marker and time-tag
1241
                        will be reseted. This overflow should be detected and
1242
                        the time axis should be adjusted accordingly.
1243
1244
        time-tag: The resolution is fixed to 4ps. Within the time of
1245
                  4ps*2^28 = 1.073741824 ms
1246
                  another photon event should occur so that the time axis can
1247
                  be computed properly.
1248
1249
        For T3 (initialized device with mode=3):
1250
        ----------------------------------------
1251
1252
        [ 4 bit for channel-number | 12 bit for start-stop-time | 16 bit for sync counter] = [32 bit word]
1253
1254
        channel-number: 4 marker, which serve for the different channels.
1255
                            0001 = marker 1
1256
                            0010 = marker 2
1257
                            0011 = marker 3
1258
                            0100 = marker 4
1259
1260
                        the first bit is the overflow bit. It will be set if
1261
                        the sync-counter reached 65536 events:
1262
1263
                            1000 = overflow
1264
1265
                        Afterwards both, overflow marker and sync-counter
1266
                        will be reseted. This overflow should be detected and
1267
                        the time axis should be adjusted accordingly.
1268
1269
        start-stop-time: time between to consecutive sync pulses. Maximal time
1270
                         between two sync pulses is therefore limited to
1271
                             2^12 * Res
1272
                         where Res is the Resolution
1273
                             Res = {4,8,16,32,54,128,256,512} (in ps)
1274
                         For largest Resolution of 512ps you have 2097.152 ns.
1275
        sync-counter: can hold up to 2^16 = 65536 events. It that number is
1276
                      reached overflow will be set. That means all 4 bits in
1277
                      the channel-number are set to high (i.e. 1).
1278
        """
1279
1280
        # at first just a simple test
1281
        time.sleep(0.2)
1282
1283
        self.data_trace[self.count] = actual_counts
1284
        self.count += 1
1285
1286
        if self.count > self._number_of_gates-1:
1287
            self.count = 0
1288
1289
        if actual_counts == self.TTREADMAX:
1290
            self.log.warning('Overflow!')
1291
1292
        print('Data analyzed.')
1293
1294
#        self.result = []
1295
#        for entry in arr_data[0:actual_counts-1]:
1296
#
1297
#            # apply three bitmasks to extract the relavent numbers:
1298
#            overflow = entry & (2**(32-1) )
1299
#            marker_ch = entry & (2**(32-2)  + 2**(32-3) + 2**(32-4))
1300
#            time_tag = entry & (2**32 -1 - 2**(32-1) + 2**(32-2) + 2**(32-3) + 2**(32-4))
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313