Add DSLogic capture, signal conversion and DSView decoders

This commit is contained in:
2026-09-27 01:45:08 +03:00
parent cc22c803d1
commit 513e79b127
44 changed files with 5982 additions and 36 deletions

View File

@@ -0,0 +1,106 @@
"""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)