interface.py 13.9 KB
Newer Older
1
2
3
"""Python interface to handel API."""

from __future__ import absolute_import
Vincent Michel's avatar
Vincent Michel committed
4

Vincent Michel's avatar
Vincent Michel committed
5
import os
Vincent Michel's avatar
Vincent Michel committed
6
from collections import namedtuple
Vincent Michel's avatar
Vincent Michel committed
7

Vincent Michel's avatar
Vincent Michel committed
8
9
import numpy

10
from .error import check_error
11
from ._cffi import handel, ffi
Vincent Michel's avatar
Vincent Michel committed
12
from .parser import parse_xia_ini_file
13

14
15
__all__ = ['init', 'init_handel', 'exit',
           'new_detector', 'get_num_detectors', 'get_detectors',
16
           'get_detector_from_channel',
17
18
19
20
           'start_run', 'stop_run',
           'get_spectrum_length', 'get_spectrum', 'get_spectrums',
           'is_channel_running', 'is_running',
           'get_module_statistics', 'get_statistics',
21
           'get_buffer_length', 'get_buffer_full', 'get_buffer', 'buffer_done',
22
23
24
           'load_system', 'save_system', 'start_system',
           'enable_log_output', 'disable_log_output',
           'set_log_output', 'set_log_level', 'close_log',
25
           'get_num_modules', 'get_modules', 'get_module_from_channel',
Vincent Michel's avatar
Vincent Michel committed
26
           'get_module_type', 'get_module_interface',
Vincent Michel's avatar
Vincent Michel committed
27
           'get_module_channels', 'get_grouped_channels', 'get_channels',
28
           'set_acquisition_value', 'get_acquisition_value',
29
           'remove_acquisition_value', 'apply_acquisition_values',
30
31
           'get_handel_version']

32
33
MAX_STRING_LENGTH = 80

34
35
36

# Helpers

Vincent Michel's avatar
Vincent Michel committed
37
38
Stats = namedtuple(
    'Stats', 'realtime livetime triggers events icr ocr deadtime')
Vincent Michel's avatar
Vincent Michel committed
39
40


41
42
43
44
45
def to_bytes(arg):
    if isinstance(arg, bytes):
        return arg
    return arg.encode()

46

47
48
49
50
51
52
53
54
def to_buffer_id(bid):
    bid = to_bytes(bid.lower())
    if bid in (b'a', b'b'):
        return bid
    msg = '{!r} is not a valid buffer id'
    raise ValueError(msg.format(bid))


Vincent Michel's avatar
Vincent Michel committed
55
56
# Initializing handel

Vincent Michel's avatar
Vincent Michel committed
57
58
def init(*path):
    filename = to_bytes(os.path.join(*path))
59
60
    code = handel.xiaInit(filename)
    check_error(code)
61
62
63


def init_handel():
64
65
    code = handel.xiaInitHandel()
    check_error(code)
66
67


Vincent Michel's avatar
Vincent Michel committed
68
def exit():
69
70
    code = handel.xiaExit()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
71
72
73
74


# Detectors

75
def new_detector(alias):
76
    alias = to_bytes(alias)
77
78
    code = handel.xiaNewDetector(alias)
    check_error(code)
79
80


81
82
def get_num_detectors():
    num = ffi.new('unsigned int *')
83
84
    code = handel.xiaGetNumDetectors(num)
    check_error(code)
85
86
87
88
89
    return num[0]


def get_detectors():
    n = get_num_detectors()
90
    arg = [ffi.new('char []', MAX_STRING_LENGTH) for _ in range(n)]
91
92
    code = handel.xiaGetDetectors(arg)
    check_error(code)
93
    return tuple(ffi.string(x).decode() for x in arg)
94

Vincent Michel's avatar
Vincent Michel committed
95

96
97
98
99
100
101
102
103
104
def get_detector_from_channel(channel):
    alias = ffi.new('char []', MAX_STRING_LENGTH)
    code = handel.xiaDetectorFromDetChan(channel, alias)
    check_error(code)
    return ffi.string(alias).decode()


# Not exposed

Vincent Michel's avatar
Vincent Michel committed
105
106
107
# int xiaAddDetectorItem(char *alias, char *name, void *value);
# int xiaModifyDetectorItem(char *alias, char *name, void *value);
# int xiaGetDetectorItem(char *alias, char *name, void *value);
108
109
# int xiaGetDetectors_VB(unsigned int index, char *alias);
# int xiaRemoveDetector(char *alias);
Vincent Michel's avatar
Vincent Michel committed
110
111
112
113


# Run control

114
115
116
def start_run(channel=None, resume=False):
    if channel is None:
        channel = -1  # All channels
117
118
    code = handel.xiaStartRun(channel, resume)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
119
120


121
122
123
def stop_run(channel=None):
    if channel is None:
        channel = -1  # All channels
124
125
    code = handel.xiaStopRun(channel)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
126
127


128
def get_spectrum_length(channel):
Vincent Michel's avatar
Vincent Michel committed
129
    length = ffi.new('unsigned long *')
Vincent Michel's avatar
Vincent Michel committed
130
    code = handel.xiaGetRunData(channel, b'mca_length', length)
131
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
132
133
134
    return length[0]


135
136
def get_spectrum(channel):
    length = get_spectrum_length(channel)
Vincent Michel's avatar
Vincent Michel committed
137
138
    array = numpy.zeros(length, dtype='uint32')
    data = ffi.cast('uint32_t *', array.ctypes.data)
Vincent Michel's avatar
Vincent Michel committed
139
    code = handel.xiaGetRunData(channel, b'mca', data)
140
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
141
142
143
    return array


144
145
146
147
148
149
150
151
152
def get_spectrums():
    """Return the spectrums for all enabled channels as a dictionary."""
    return {channel: get_spectrum(channel) for channel in get_channels()}


def is_channel_running(channel):
    running = ffi.new('short *')
    code = handel.xiaGetRunData(channel, b'run_active', running)
    check_error(code)
153
154
155
156
157
    # It turns out running contains 2 bits of information
    # - bit 0: whether the channel is acquiring
    # - bit 1: whether the channel is running (in the start_run/stop_run sense)
    # We're interested in the first bit of information here
    return bool(running[0] & 0x1)
158
159
160
161


def is_running():
    """Return True if any channel is running, False otherwise."""
Vincent Michel's avatar
Vincent Michel committed
162
    return any(is_channel_running(channel) for channel in get_channels())
163
164
165
166
167


# Statistics

def get_module_statistics(module):
Vincent Michel's avatar
Vincent Michel committed
168
    # Get raw data
169
170
171
172
173
174
175
    channels = get_module_channels(module)
    data_size = 7 * len(channels)
    master = next(c for c in channels if c != -1)
    array = numpy.zeros(data_size, dtype='double')
    data = ffi.cast('double *', array.ctypes.data)
    code = handel.xiaGetRunData(master, b'module_statistics', data)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
176
177
178
179
    # Parse raw data
    result = {}
    for index, channel in enumerate(channels):
        if channel != -1:
Vincent Michel's avatar
Vincent Michel committed
180
181
182
183
184
185
186
187
188
            realtime = array[index * 7 + 0]
            livetime = array[index * 7 + 1]
            triggers = int(array[index * 7 + 3])
            events = int(array[index * 7 + 4])
            icr = int(array[index * 7 + 5])
            ocr = int(array[index * 7 + 6])
            deadtime = 1 - float(ocr) / icr if icr != 0 else 1.
            result[channel] = Stats(
                realtime, livetime, triggers, events, icr, ocr, deadtime)
Vincent Michel's avatar
Vincent Michel committed
189
    return result
190
191
192
193
194
195
196
197
198
199


def get_statistics():
    """Return the statistics for all enabled channels as a dictionary."""
    result = {}
    for module in get_modules():
        result.update(get_module_statistics(module))
    return result


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# Buffer

def get_buffer_length(channel):
    length = ffi.new('unsigned long *')
    code = handel.xiaGetRunData(channel, b'buffer_len', length)
    check_error(code)
    return length[0]


def get_buffer_full(channel, buffer_id):
    bid = to_buffer_id(buffer_id)
    command = b'buffer_full_%c' % bid
    result = ffi.new('unsigned short *')
    code = handel.xiaGetRunData(channel, command, result)
    check_error(code)
    return bool(result[0])


def get_buffer(channel, buffer_id):
    bid = to_buffer_id(buffer_id)
    command = b'buffer_%c' % bid
    length = get_buffer_length(channel)
Vincent Michel's avatar
Vincent Michel committed
222
223
    array = numpy.zeros(length, dtype='uint32')
    data = ffi.cast('uint32_t *', array.ctypes.data)
224
225
226
227
228
229
230
231
232
233
234
235
236
    code = handel.xiaGetRunData(channel, command, data)
    check_error(code)
    return array


def buffer_done(channel, buffer_id):
    bid = to_buffer_id(buffer_id)
    code = handel.xiaBoardOperation(channel, b'buffer_done', bid)
    check_error(code)


# Not exposed

Vincent Michel's avatar
Vincent Michel committed
237
238
239
240
241
242
# int xiaDoSpecialRun(int detChan, char *name, void *info);
# int xiaGetSpecialRunData(int detChan, char *name, void *value);


# System

Vincent Michel's avatar
Vincent Michel committed
243
244
def load_system(*path):
    filename = to_bytes(os.path.join(*path))
245
246
    code = handel.xiaLoadSystem(b'handel_ini', filename)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
247
248


Vincent Michel's avatar
Vincent Michel committed
249
250
def save_system(*path):
    filename = to_bytes(os.path.join(*path))
251
252
    code = handel.xiaSaveSystem(b'handel_ini', filename)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
253
254
255


def start_system():
256
257
    code = handel.xiaStartSystem()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
258
259
260
261
262


# Logging

def enable_log_output():
263
264
    code = handel.xiaEnableLogOutput()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
265
266
267


def disable_log_output():
268
269
    code = handel.xiaSuppressLogOutput()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
270
271
272


def set_log_level(level):
273
274
    code = handel.xiaSetLogLevel(level)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
275
276
277


def set_log_output(filename):
278
    filename = to_bytes(filename)
279
280
    code = handel.xiaSetLogOutput(filename)
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
281
282
283


def close_log():
284
285
    code = handel.xiaCloseLog()
    check_error(code)
Vincent Michel's avatar
Vincent Michel committed
286
287
288

# Firmware

289
290
# Not exposed

291
292
293
294
295
296
297
298
299
# int xiaNewFirmware(char *alias);
# int xiaAddFirmwareItem(char *alias, char *name, void *value);
# int xiaModifyFirmwareItem(char *alias, unsigned short decimation, char *name, void *value);
# int xiaGetFirmwareItem(char *alias, unsigned short decimation, char *name, void *value);
# int xiaGetNumFirmwareSets(unsigned int *numFirmware);
# int xiaGetFirmwareSets(char *firmware[]);
# int xiaGetFirmwareSets_VB(unsigned int index, char *alias);
# int xiaGetNumPTRRs(char *alias, unsigned int *numPTRR);
# int xiaRemoveFirmware(char *alias);
Vincent Michel's avatar
Vincent Michel committed
300
301
302
303
304
# int xiaDownloadFirmware(int detChan, char *type);


# Module

305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
def get_num_modules():
    num_modules = ffi.new('unsigned int *')
    code = handel.xiaGetNumModules(num_modules)
    check_error(code)
    return num_modules[0]


def get_modules():
    n = get_num_modules()
    arg = [ffi.new('char []', MAX_STRING_LENGTH) for _ in range(n)]
    code = handel.xiaGetModules(arg)
    check_error(code)
    return tuple(ffi.string(x).decode() for x in arg)


def get_module_from_channel(channel):
    alias = ffi.new('char []', MAX_STRING_LENGTH)
    code = handel.xiaModuleFromDetChan(channel, alias)
    check_error(code)
    return ffi.string(alias).decode()


327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
def get_module_type(alias):
    alias = to_bytes(alias)
    value = ffi.new('char []', MAX_STRING_LENGTH)
    code = handel.xiaGetModuleItem(alias, b'module_type', value)
    check_error(code)
    return ffi.string(value).decode()


def get_module_interface(alias):
    alias = to_bytes(alias)
    value = ffi.new('char []', MAX_STRING_LENGTH)
    code = handel.xiaGetModuleItem(alias, b'interface', value)
    check_error(code)
    return ffi.string(value).decode()


343
344
# Channels

345
346
347
348
349
350
351
352
def get_module_number_of_channels(alias):
    alias = to_bytes(alias)
    value = ffi.new('int *')
    code = handel.xiaGetModuleItem(alias, b'number_of_channels', value)
    check_error(code)
    return value[0]


Vincent Michel's avatar
Vincent Michel committed
353
354
355
356
357
358
359
360
361
362
def get_module_channel_at(alias, index):
    alias = to_bytes(alias)
    value = ffi.new('int *')
    key = b'channel%d_alias' % index
    code = handel.xiaGetModuleItem(alias, key, value)
    check_error(code)
    return value[0]


def get_module_channels(alias):
Vincent Michel's avatar
Vincent Michel committed
363
    """Return the module channels properly indexed."""
Vincent Michel's avatar
Vincent Michel committed
364
    number_of_channels = get_module_number_of_channels(alias)
Vincent Michel's avatar
Vincent Michel committed
365
366
    return tuple(get_module_channel_at(alias, index)
                 for index in range(number_of_channels))
Vincent Michel's avatar
Vincent Michel committed
367
368


Vincent Michel's avatar
Vincent Michel committed
369
def get_grouped_channels():
Vincent Michel's avatar
Vincent Michel committed
370
371
    """Return the indexed channels grouped by modules."""
    return tuple(get_module_channels(alias) for alias in get_modules())
Vincent Michel's avatar
Vincent Michel committed
372
373


Vincent Michel's avatar
Vincent Michel committed
374
375
def get_channels():
    """Return all the enabled channels."""
Vincent Michel's avatar
Vincent Michel committed
376
377
378
379
380
    return tuple(sorted(
        channel
        for channels in get_grouped_channels()
        for channel in channels
        if channel != -1))
Vincent Michel's avatar
Vincent Michel committed
381
382


383
384
# Not exposed

385
386
387
388
389
390
# int xiaNewModule(char *alias);
# int xiaAddModuleItem(char *alias, char *name, void *value);
# int xiaModifyModuleItem(char *alias, char *name, void *value);
# int xiaGetModuleItem(char *alias, char *name, void *value);
# int xiaGetModules_VB(unsigned int index, char *alias);
# int xiaRemoveModule(char *alias);
Vincent Michel's avatar
Vincent Michel committed
391
392
393
394


# Channel set

395
396
# Not exposed

397
398
399
# int xiaAddChannelSetElem(unsigned int detChanSet, unsigned int newChan);
# int xiaRemoveChannelSetElem(unsigned int detChan, unsigned int chan);
# int xiaRemoveChannelSet(unsigned int detChan);
Vincent Michel's avatar
Vincent Michel committed
400
401


402
403
# Parameters

404
def get_acquisition_value(name, channel):
405
    name = to_bytes(name)
406
407
    pointer = ffi.new('double *')
    code = handel.xiaGetAcquisitionValues(channel, name, pointer)
408
    check_error(code)
409
    return pointer[0]
410
411


412
413
414
def set_acquisition_value(name, value, channel=None):
    if channel is None:
        channel = -1  # All channels
415
    name = to_bytes(name)
416
417
    pointer = ffi.new('double *', value)
    code = handel.xiaSetAcquisitionValues(channel, name, pointer)
418
    check_error(code)
419

Vincent Michel's avatar
Vincent Michel committed
420

421
422
423
def remove_acquisition_value(name, channel=None):
    if channel is None:
        channel = -1  # All channels
424
425
426
427
428
    name = to_bytes(name)
    code = handel.xiaRemoveAcquisitionValues(channel, name)
    check_error(code)


429
430
431
432
433
434
435
436
437
def apply_acquisition_values(channel=None):
    # Apply all
    if channel is None:
        # Only one apply operation by module is required
        grouped = get_grouped_channels()
        for master in map(max, grouped):
            apply_acquisition_values(master)
        return
    # Apply single
438
439
440
441
442
443
444
    dummy = ffi.new('int *')
    code = handel.xiaBoardOperation(channel, b'apply', dummy)
    check_error(code)


# Not exposed

445
446
447
448
449
450
451
452
# int xiaUpdateUserParams(int detChan);
# int xiaGainOperation(int detChan, char *name, void *value);
# int xiaGainCalibrate(int detChan, double deltaGain);
# int xiaGetParameter(int detChan, const char *name, unsigned short *value);
# int xiaSetParameter(int detChan, const char *name, unsigned short value);
# int xiaGetNumParams(int detChan, unsigned short *numParams);
# int xiaGetParamData(int detChan, char *name, void *value);
# int xiaGetParamName(int detChan, unsigned short index, char *name);
Vincent Michel's avatar
Vincent Michel committed
453
454
455
456


# Operation

457
458
459
# Not exposed

# int xiaBoardOperation(int detChan, char *name, void *value) with mapping_pixel_next (int 0);
460
461
# int xiaMemoryOperation(int detChan, char *name, void *value);
# int xiaCommandOperation(int detChan, byte_t cmd, unsigned int lenS, byte_t *send, unsigned int lenR, byte_t *recv);
Vincent Michel's avatar
Vincent Michel committed
462
463
464
465


# Analysis

466
467
# Not exposed

468
469
# int xiaFitGauss(long data[], int lower, int upper, float *pos, float *fwhm);
# int xiaFindPeak(long *data, int numBins, float thresh, int *lower, int *upper);
Vincent Michel's avatar
Vincent Michel committed
470
471
472
473
474


# Debugging


475
def get_handel_version():
Vincent Michel's avatar
Vincent Michel committed
476
477
478
479
480
481
482
483
    rel = ffi.new('int *')
    min = ffi.new('int *')
    maj = ffi.new('int *')
    pretty = ffi.new('char *')
    handel.xiaGetVersionInfo(rel, min, maj, pretty)
    return maj[0], min[0], rel[0]


484
485
# Not exposed

486
487
488
489
# int xiaSetIOPriority(int pri);
# int xiaMemStatistics(unsigned long *total, unsigned long *current, unsigned long *peak);
# void xiaMemSetCheckpoint(void);
# void xiaMemLeaks(char *);
Vincent Michel's avatar
Vincent Michel committed
490
491
492
493


# Files

494
def get_config_files(*path):
Vincent Michel's avatar
Vincent Michel committed
495
    """Return all the ini files in path (including subdirectories)."""
496
    path = os.path.join(*path)
Vincent Michel's avatar
Vincent Michel committed
497
    ext = b'.ini' if isinstance(path, bytes) else '.ini'
Vincent Michel's avatar
Vincent Michel committed
498
499
    sep = b'/' if isinstance(path, bytes) else '/'
    return [os.path.join(dp, f).lstrip(path).lstrip(sep)
Vincent Michel's avatar
Vincent Michel committed
500
501
            for dp, dn, fn in os.walk(path)
            for f in fn
Vincent Michel's avatar
Vincent Michel committed
502
            if f.endswith(ext)]
Vincent Michel's avatar
Vincent Michel committed
503
504


505
def get_config(*path):
Vincent Michel's avatar
Vincent Michel committed
506
    """Read and return the given config file as a dictionary."""
507
    filename = os.path.join(*path)
508
    with open(filename) as f:
Vincent Michel's avatar
Vincent Michel committed
509
        return parse_xia_ini_file(f.read())