107 lines
5.5 KiB
Python
107 lines
5.5 KiB
Python
"""DSView trigger validation and API programming, independent of Qt/cffi."""
|
||
import math
|
||
|
||
|
||
def pattern(value):
|
||
return ''.join(str(value).upper().split())
|
||
|
||
|
||
def validate_trigger(trigger, channels, acquisition):
|
||
trigger = trigger or {'kind': 'none'}
|
||
kind = trigger.get('kind', 'none')
|
||
if kind not in ('none', 'simple', 'stages', 'serial'):
|
||
raise ValueError('Неизвестный тип триггера.')
|
||
if kind == 'none':
|
||
return
|
||
if acquisition == 'internal':
|
||
raise ValueError('Внутренний тест работает без триггера.')
|
||
if kind in ('stages', 'serial') and acquisition != 'buffer':
|
||
raise ValueError('Расширенный и последовательный триггеры требуют буферного режима.')
|
||
position = trigger.get('position', 50)
|
||
timeout = trigger.get('timeout', 30)
|
||
if type(position) is not int or not 0 <= position <= 90:
|
||
raise ValueError('Позиция триггера: 0…90 %.')
|
||
if not math.isfinite(timeout) or not 0 <= timeout <= 3600:
|
||
raise ValueError('Ожидание триггера: 0…3600 с; 0 — без тайм-аута.')
|
||
|
||
def check(value, data=False):
|
||
value = pattern(value)
|
||
if len(value) != 16 or any(c not in ('01X' if data else '01XRFC') for c in value):
|
||
raise ValueError('Условие должно содержать 16 символов: ' + ('0, 1, X.' if data else '0, 1, X, R, F, C.'))
|
||
if not data and any(c != 'X' and 15-i not in channels for i, c in enumerate(value)):
|
||
raise ValueError('В триггере указан выключенный канал.')
|
||
return value
|
||
|
||
if kind == 'simple':
|
||
value = check(trigger.get('pattern', 'X'*16))
|
||
if set(value) == {'X'}:
|
||
raise ValueError('Выберите хотя бы одно условие триггера.')
|
||
elif kind == 'stages':
|
||
stages = trigger.get('stages', [])
|
||
if not 1 <= len(stages) <= 16:
|
||
raise ValueError('Число ступеней: 1…16.')
|
||
for stage in stages:
|
||
a, b = check(stage['a']), check(stage['b'])
|
||
if a == b == 'X'*16:
|
||
raise ValueError('Задайте условие на каждой ступени.')
|
||
if stage.get('logic', 'and') not in ('and', 'or'):
|
||
raise ValueError('Логика ступени: И либо ИЛИ.')
|
||
if type(stage.get('count', 1)) is not int or not 1 <= stage.get('count', 1) <= 2147483647:
|
||
raise ValueError('Счётчик ступени: 1…2147483647.')
|
||
else:
|
||
check(trigger['start']); check(trigger['stop'])
|
||
clock = check(trigger['clock'])
|
||
check(trigger.get('compare', 'X'*16))
|
||
if not any(c in 'RFC' for c in clock):
|
||
raise ValueError('В условии такта нужен фронт R, F или C.')
|
||
if type(trigger['data_channel']) is not int or not 0 <= trigger['data_channel'] <= 15:
|
||
raise ValueError('Канал последовательных данных: D0…D15.')
|
||
if trigger['data_channel'] not in channels:
|
||
raise ValueError('Канал последовательных данных выключен.')
|
||
if type(trigger['bits']) is not int or not 1 <= trigger['bits'] <= 16:
|
||
raise ValueError('Число последовательных битов: 1…16.')
|
||
check(trigger['value'], data=True)
|
||
|
||
|
||
def program_trigger(lib, trigger, acquisition, check):
|
||
"""Follow DSView TriggerDock::commit_trigger, including stage count - 1."""
|
||
trigger = trigger or {'kind': 'none'}
|
||
kind = trigger.get('kind', 'none')
|
||
def call(name, *args):
|
||
check(getattr(lib, name)(*args), name)
|
||
call('ds_trigger_reset')
|
||
call('ds_trigger_set_en', int(kind != 'none'))
|
||
if kind == 'none':
|
||
return
|
||
call('ds_trigger_set_pos', 1 if acquisition == 'stream' else trigger.get('position', 50))
|
||
call('ds_trigger_set_mode', {'simple': 0, 'stages': 1, 'serial': 2}[kind])
|
||
if kind == 'simple':
|
||
for ch, value in enumerate(reversed(pattern(trigger['pattern']))):
|
||
call('ds_trigger_probe_set', ch, ord(value), ord('X'))
|
||
return
|
||
def values(index, a, b):
|
||
call('ds_trigger_stage_set_value', index, 16,
|
||
' '.join(pattern(a)).encode('ascii'), ' '.join(pattern(b)).encode('ascii'))
|
||
if kind == 'stages':
|
||
stages = trigger['stages']
|
||
call('ds_trigger_set_stage', len(stages)-1)
|
||
for index, stage in enumerate(stages):
|
||
values(index, stage['a'], stage['b'])
|
||
logic = int(stage.get('logic', 'and') == 'and') | (int(stage.get('contiguous', False)) << 1)
|
||
call('ds_trigger_stage_set_logic', index, 16, logic)
|
||
call('ds_trigger_stage_set_inv', index, 16, int(stage.get('inv_a', False)), int(stage.get('inv_b', False)))
|
||
call('ds_trigger_stage_set_count', index, 16, stage.get('count', 1), 0)
|
||
else:
|
||
call('ds_trigger_set_stage', 3)
|
||
values(0, trigger['start'], trigger['stop'])
|
||
values(1, trigger['clock'], trigger.get('compare', 'X'*16))
|
||
select = list('X'*16)
|
||
select[15-trigger['data_channel']] = '0'
|
||
values(2, ''.join(select), 'X'*16)
|
||
values(3, trigger['value'], 'X'*16)
|
||
for index in range(4):
|
||
call('ds_trigger_stage_set_logic', index, 16, 1)
|
||
call('ds_trigger_stage_set_inv', index, 16, 0, 0)
|
||
call('ds_trigger_stage_set_count', 1, 16, 1, 0)
|
||
call('ds_trigger_stage_set_count', 3, 16, trigger['bits']-1, 0)
|