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,60 @@
# 1SP0635 / 1SD536F2 timing decoder for DSView
This decoder checks driver control and status responses:
- `Vin1`: optional control sent to the first gate driver (legacy channel ID `vin`);
- `Vstat`: edge acknowledgement and fault feedback returned by the driver.
Built-in data-sheet profiles:
| Profile | ACK delay (typ.) | ACK width (specified) | Long pulse |
|---|---:|---:|---:|
| 1SP0635 | 250 ns | 400...1050 ns | fault above 1.5 us |
| 1SD536F2 | 380 ns | 600...1800 ns | fault above 1.8 us* |
`*` The 1.8 us fault threshold is a project classification choice based on the
data-sheet ACK maximum, not a verified universal manufacturer fault threshold.
The previously stated 1.5 us manual threshold could not be verified.
Correlated pulses within the ACK width limits pass. Uncorrelated pulses up to
the fault threshold are warnings; pulses above it are faults. Width and fault
annotations are emitted only when the pulse ends.
The data sheets specify only a *typical* ACK delay, not minimum and maximum
limits. DSView therefore emits a warning outside the configurable tolerance
(default +/-100 ns). The ACK-width check uses the published min/max values.
## Use in SETGUI
The DSView build recipe packages this adapter together with the canonical
`python/logic_analyzer/decoders/gate_timing.py` core. In the DSView tab,
choose **Анализаторы SET → IGBT · 1SP0635 / 1SD536F2** (or search Decode for
`IGBT 1SP/1SD`). Assign Vstat and, if available, Vin1, choose the driver profile, and set `Vstat acknowledge/fault level`
to match the receiver circuit. The diagrams in the application manual show
the logical event; some optical receiver circuits invert it.
Use at least 50 MS/s; 100 MS/s or more is recommended. Never connect a DSLogic
digital input directly to the IGBT gate (+15/-10 V) or to a power-stage node.
Use the isolated optical receiver logic signals and a common logic ground.
## Separate transistor pair analyzer
This decoder analyzes only Vin1/Vstat driver responses. Vin1 is optional;
Vstat alone retains pulse/fault classification, while Vin1 enables ACK timing
and full-cycle results. Vstat is required. The channel ID `vin` remains stable.
Pair analysis was moved to **Transistor pair** in the **Пользовательские**
section. Add that decoder separately and assign Vin1/Vin2. Old pair settings
on IGBT instances must be recreated in the new decoder. The pair checkbox,
Vin2 assignment and pulse limits are no longer part of the driver decoder.
## Full pulse cycle result
The **Vin1 / ACK full cycle** waveform row displays OK, WARNING or FAULT
across each complete sequence **Vin1 ON -> ACK -> Vin1 OFF -> ACK**.
The annotation starts at Vin1 ON and ends at the end of the OFF acknowledgment.
It is emitted only after both ACKs have been classified. A delay warning makes
that cycle WARNING; missing ACK, invalid width or fault feedback makes it FAULT.
A later normal cycle can be OK independently of previous cycles.
Unfinished cycles are never marked OK. Active-low Vin1 is supported.
The results panel retains its original layout without a large summary banner.
Deadtime and mintime are provided by the separate Transistor pair analyzer.

View File

@@ -0,0 +1,3 @@
"""DSView protocol decoder for SCALE gate-driver timing checks."""
from .pd import Decoder

View File

@@ -0,0 +1,168 @@
"""DSView/libsigrokdecode front end for SCALE gate-driver timing checks."""
import sigrokdecode as srd
from common.setgui_decoders.gate_timing import TimingChecker
class SamplerateError(Exception):
pass
class Ann(object):
CONTROL = 0
DELAY_OK = 1
WIDTH_OK = 2
WARNING = 3
ERROR = 4
FAULT = 5
CYCLE_OK = 6
CYCLE_WARNING = 7
CYCLE_FAULT = 8
ANN_FOR_KIND = {
'control': Ann.CONTROL,
'delay_ok': Ann.DELAY_OK,
'width_ok': Ann.WIDTH_OK,
'delay_warn': Ann.WARNING,
'width_fail': Ann.ERROR,
'missing': Ann.ERROR,
'orphan': Ann.WARNING,
'fault': Ann.FAULT,
'cycle_ok': Ann.CYCLE_OK,
'cycle_warning': Ann.CYCLE_WARNING,
'cycle_fault': Ann.CYCLE_FAULT,
}
class Decoder(srd.Decoder):
api_version = 3
id = 'gate_driver_timing'
name = 'IGBT 1SP/1SD'
longname = 'Power Integrations 1SP0635 / 1SD536F2 timing checker'
desc = 'Checks driver Vin1/Vstat ACK timing, faults and full pulse cycles.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Clock/timing', 'Util']
channels = ()
optional_channels = (
{'id': 'vin', 'name': 'Vin1', 'desc': 'First driver control input'},
{'id': 'vstat', 'name': 'Vstat', 'desc': 'Driver status/acknowledge output'},
)
options = (
{'id': 'profile', 'desc': 'Driver profile', 'default': '1SP0635',
'values': ('1SP0635', '1SD536F2', 'custom')},
{'id': 'vin_active', 'desc': 'Vin1 active level', 'default': 'high',
'values': ('high', 'low')},
{'id': 'vstat_active', 'desc': 'Vstat acknowledge/fault level', 'default': 'high',
'values': ('high', 'low')},
{'id': 'delay_tolerance_ns',
'desc': 'Warning tolerance around typical ACK delay (ns)', 'default': 100},
{'id': 'orphan_min_width_ns',
'desc': 'ORPHAN minimum pulse width (ns, 0 = off)', 'default': 0},
{'id': 'custom_delay_ns', 'desc': 'Custom typical ACK delay (ns)', 'default': 250},
{'id': 'custom_width_min_ns', 'desc': 'Custom ACK width minimum (ns)', 'default': 400},
{'id': 'custom_width_typ_ns', 'desc': 'Custom ACK width typical (ns)', 'default': 700},
{'id': 'custom_width_max_ns', 'desc': 'Custom ACK width maximum (ns)', 'default': 1050},
{'id': 'custom_fault_ns', 'desc': 'Custom fault pulse threshold (ns)', 'default': 1500},
)
annotations = (
('control', 'Vin1 edge'),
('delay-ok', 'ACK delay pass'),
('width-ok', 'ACK width pass'),
('warning', 'Warning'),
('error', 'Timing failure'),
('fault', 'Driver fault'),
('cycle-ok', 'Cycle OK'),
('cycle-warning', 'Cycle WARNING'),
('cycle-fault', 'Cycle FAULT'),
)
annotation_rows = (
('cycle', 'Vin1 / ACK full cycle', (Ann.CYCLE_OK, Ann.CYCLE_WARNING, Ann.CYCLE_FAULT)),
('vin', 'Vin1', (Ann.CONTROL,)),
('measurements', 'Measurements', (Ann.DELAY_OK, Ann.WIDTH_OK)),
('checks', 'Warnings / failures', (Ann.WARNING, Ann.ERROR, Ann.FAULT)),
)
def __init__(self):
self.reset()
# Состояние принадлежит одному запуску декодера. Сброс не должен
# переносить незавершённый кадр или частоту из предыдущей записи.
def reset(self):
self.samplerate = None
self.checker = None
# DSView передаёт время в номерах отсчётов. Частота нужна общему ядру
# для перевода тайм-аутов и измерений в физические единицы.
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# Выходы регистрируются в жизненном цикле sigrok, а не в конструкторе.
# Здесь регистрируются аннотации для дорожек измерений и предупреждений.
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
# Адаптер переводит настройки DSView в параметры общего TimingChecker.
# Пороговые алгоритмы остаются в templates/python/logic_analyzer/decoders.
def _make_checker(self):
custom = {
'ack_delay_typ_ns': float(self.options['custom_delay_ns']),
'ack_width_min_ns': float(self.options['custom_width_min_ns']),
'ack_width_typ_ns': float(self.options['custom_width_typ_ns']),
'ack_width_max_ns': float(self.options['custom_width_max_ns']),
'fault_threshold_ns': float(self.options['custom_fault_ns']),
}
return TimingChecker(
self.samplerate,
profile=self.options['profile'],
delay_tolerance_ns=float(self.options['delay_tolerance_ns']),
vin_active_high=self.options['vin_active'] == 'high',
vstat_active_high=self.options['vstat_active'] == 'high',
orphan_min_width_ns=self.options.get('orphan_min_width_ns', 0),
custom=custom, cycle_results=True)
def _put_events(self, events):
for event in events:
ann = ANN_FOR_KIND[event['kind']]
self.put(event['start'], max(event['start'], event['end']),
self.out_ann, [ann, [event['text'], event['short']]])
def decode(self):
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
self.checker = self._make_checker()
has_vin1 = self.has_channel(0)
if not self.has_channel(1):
raise ValueError('Connect Vstat for driver response analysis.')
while True:
conditions = [{0: 'e'}, {1: 'e'}] if has_vin1 else [{1: 'e'}]
timer_bit = 1 << len(conditions)
deadline = self.checker.next_deadline()
if deadline is not None:
conditions.append({'skip': max(1, deadline - self.samplenum)})
# DSView mutates and returns the same native tuple on every wait.
# Release that tuple before waiting again (PyTuple_SetItem requires
# exclusive ownership); tuple(...) would not make a copy.
pins = list(self.wait(conditions))
vin, vstat = pins[:2]
now = self.samplenum
# Both channels may transition at the same sample; preserve both.
if has_vin1 and self.matched & 0b001:
self._put_events(self.checker.on_control_edge(now, vin))
if self.matched & (0b010 if has_vin1 else 0b001):
self._put_events(self.checker.on_status_edge(now, vstat))
if deadline is not None and self.matched & timer_bit:
self._put_events(self.checker.expire(now))
def end(self):
if self.checker is not None:
self._put_events(self.checker.finish_cycles(self.samplenum))

View File

@@ -0,0 +1 @@
from .pd import Decoder

View File

@@ -0,0 +1,82 @@
import sigrokdecode as srd
from common.setgui_decoders.pm35_uart import PM35Parser
class Decoder(srd.Decoder):
api_version = 3
id = 'pm35_uart'
name = 'PM35 UART'
longname = 'PM35 / TMS320F28335 terminal'
desc = 'MODBUS 03/06 register traffic and CRC16.'
license = 'gplv2+'
inputs = ['uart']
outputs = ['pm35']
tags = ['Embedded/industrial']
options = (
{'id': 'rx_role', 'desc': 'RX carries', 'default': 'response', 'values': ('request', 'response', 'auto')},
{'id': 'tx_role', 'desc': 'TX carries', 'default': 'request', 'values': ('request', 'response', 'auto')},
{'id': 'baudrate', 'desc': 'UART baudrate (RTU gap detection)', 'default': 115200},
{'id': 'bits_per_char', 'desc': 'Bits per UART character including start/stop/parity', 'default': 10},
)
annotations = (('frame', 'Frame'), ('error', 'Error'))
annotation_rows = (('frames', 'Frames', (0,)), ('errors', 'Errors', (1,)))
def __init__(self):
self.reset()
# Состояние принадлежит одному запуску декодера. Сброс не должен
# переносить незавершённый кадр или частоту из предыдущей записи.
def reset(self):
self.parsers = {}
self.last = {}
self.samplerate = None
# DSView передаёт время в номерах отсчётов. Частота нужна общему ядру
# для перевода тайм-аутов и измерений в физические единицы.
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# Выходы регистрируются в жизненном цикле sigrok, а не в конструкторе.
# Аннотации предназначены для дорожек, структурированные события — для стека.
def start(self):
if self.options['baudrate'] <= 0 or self.options['bits_per_char'] <= 0:
raise ValueError('Baudrate and bits per character must be positive')
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_python = self.register(srd.OUTPUT_PYTHON)
def emit(self, events, direction):
for ss, es, result, error in events:
self.put(ss, es, self.out_ann, [1 if error else 0,
[('RX', 'TX')[direction] + ' ' + (error or result['summary'])]])
if result:
result['direction'] = direction
self.put(ss, es, self.out_python, ['FRAME', result])
def decode(self, ss, es, data):
kind, direction, payload = data
if direction not in (0, 1):
return
parser = self.parsers.setdefault(direction, PM35Parser(self.options[('rx_role', 'tx_role')[direction]]))
if kind in ('INVALID STOPBIT', 'INVALID STARTBIT', 'PARITY ERROR', 'BREAK'):
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [1, [kind]])
return
if kind != 'FRAME':
return
if not payload[1]:
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [1, ['Invalid UART frame']])
return
# Граница MODBUS определяется паузой 3,5 символа. bits_per_char включает
# все биты символа; эта настройка должна соответствовать базовому UART.
if self.samplerate and direction in self.last:
gap = self.samplerate * 3.5 * self.options['bits_per_char'] / self.options['baudrate']
if ss - self.last[direction] >= gap:
self.emit(parser.flush(), direction)
self.last[direction] = es
if not 0 <= payload[0] <= 255:
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [1, ['Configure UART for 8 data bits']])
return
self.emit(parser.feed(payload[0], ss, es), direction)

View File

@@ -0,0 +1 @@
from .pd import Decoder

View File

@@ -0,0 +1,66 @@
import sigrokdecode as srd
from common.setgui_decoders.set_can import Reassembler, legacy
class Decoder(srd.Decoder):
api_version = 3
id = 'set_can'
name = 'SET CAN'
longname = 'SET v2 CAN / ProtoCAN / Balsam'
desc = 'Application fields and segmented SET packets over classic CAN.'
license = 'gplv2+'
inputs = ['can']
outputs = ['set']
tags = ['Embedded/industrial']
options = ({'id': 'protocol', 'desc': 'CAN protocol (select explicitly)',
'default': 'protocan', 'values': ('protocan', 'set-v2', 'balsam')},)
annotations = (('frame', 'Frame'), ('payload', 'Payload'), ('error', 'Error'))
annotation_rows = (('frames', 'Frames', (0,)), ('payload', 'Payload', (1,)), ('errors', 'Errors', (2,)))
def __init__(self):
self.reset()
# Состояние принадлежит одному запуску декодера. Сброс не должен
# переносить незавершённый кадр или частоту из предыдущей записи.
def reset(self):
self.reassembler = Reassembler()
self.samplerate = None
# DSView передаёт время в номерах отсчётов. Частота нужна общему ядру
# для перевода тайм-аутов и измерений в физические единицы.
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# Выходы регистрируются в жизненном цикле sigrok, а не в конструкторе.
# Аннотации предназначены для дорожек, структурированные события — для стека.
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_python = self.register(srd.OUTPUT_PYTHON)
def decode(self, ss, es, data):
frame_type, ident, rtr, dlc, payload = data
if frame_type != 'extended' or rtr == 'remote':
return
mode = self.options['protocol']
if not 0 <= dlc <= 8 or len(payload) != dlc:
events = [(ss, es, None, 'Invalid classic CAN DLC')]
# Сегментированный SET v2 требует времени для тайм-аута сборки.
# Идентификатор, порядок сегментов и CRC проверяет общее ядро.
elif mode == 'set-v2':
if not self.samplerate:
raise ValueError('SET CAN requires samplerate for reassembly timeout')
events = self.reassembler.feed(ident, payload, ss, es, es * 1000.0 / self.samplerate)
else:
try:
result = legacy(ident, payload, mode)
events = [(ss, es, result, None)] if result else []
except ValueError as exc:
events = [(ss, es, None, str(exc))]
for start, end, result, error in events:
self.put(start, end, self.out_ann, [2 if error else 0, [error or result['summary']]])
if result:
result['can_id'] = ident
self.put(start, end, self.out_python, ['FRAME', result])
if events and payload:
self.put(ss, es, self.out_ann, [1, [' '.join('%02X' % b for b in payload)]])

View File

@@ -0,0 +1 @@
from .pd import Decoder

View File

@@ -0,0 +1,89 @@
import sigrokdecode as srd
from common.setgui_decoders.set_uart import StreamParser
class Decoder(srd.Decoder):
api_version = 3
id = 'set_uart'
name = 'SET UART'
longname = 'SET v2 / SETGUI v1 / ProtoCAN bridge'
desc = 'SET frames, fields and CRC over UART.'
license = 'gplv2+'
inputs = ['uart']
outputs = ['set']
tags = ['Embedded/industrial']
options = (
{'id': 'protocol', 'desc': 'Protocol', 'default': 'auto',
'values': ('auto', 'v2', 'v1', 'bridge')},
{'id': 'gap_ms', 'desc': 'Reset incomplete frame after gap (ms, 0 disables)', 'default': 100},
)
annotations = (('frame', 'Frame'), ('payload', 'Payload'), ('error', 'Error'))
annotation_rows = (('frames', 'Frames', (0,)), ('payload', 'Payload', (1,)), ('errors', 'Errors', (2,)))
def __init__(self):
self.reset()
# Состояние принадлежит одному запуску декодера. Сброс не должен
# переносить незавершённый кадр или частоту из предыдущей записи.
def reset(self):
self.parsers = {}
self.last = {}
self.samplerate = None
# DSView передаёт время в номерах отсчётов. Частота нужна общему ядру
# для перевода тайм-аутов и измерений в физические единицы.
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
# Выходы регистрируются в жизненном цикле sigrok, а не в конструкторе.
# Аннотации предназначены для дорожек, структурированные события — для стека.
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
self.out_python = self.register(srd.OUTPUT_PYTHON)
def emit(self, events, direction):
for ss, es, frame, error in events:
prefix = ('RX', 'TX')[direction] + ' '
self.put(ss, es, self.out_ann, [2 if error else 0, [prefix + (error or frame['summary'])]])
if frame:
if frame['protocol'] == 'ProtoCAN bridge' and frame['flags'] & 1 and not frame['flags'] & 10:
# Imported only at decode time: both packages are installed together.
from common.setgui_decoders.set_can import legacy
try:
application = legacy(frame['can_id'], frame['payload'])
frame['application'] = application
self.put(ss, es, self.out_ann, [0, [application['summary']]])
except ValueError as exc:
self.put(ss, es, self.out_ann, [2, [str(exc)]])
frame['direction'] = direction
self.put(ss, es, self.out_python, ['FRAME', frame])
if frame['payload']:
self.put(ss, es, self.out_ann, [1, [prefix + ' '.join('%02X' % b for b in frame['payload'])]])
def decode(self, ss, es, data):
kind, direction, payload = data
if direction not in (0, 1):
return
# RX и TX имеют независимые парсеры: перемежающиеся байты двух направлений
# нельзя объединять в один прикладной кадр.
parser = self.parsers.setdefault(direction, StreamParser(self.options['protocol']))
if kind in ('INVALID STOPBIT', 'INVALID STARTBIT', 'PARITY ERROR', 'BREAK'):
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [2, [kind]])
return
if kind != 'FRAME':
return
if not payload[1]:
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [2, ['Invalid UART frame']])
return
gap = float(self.options['gap_ms'])
if self.samplerate and gap > 0 and direction in self.last and ss - self.last[direction] > self.samplerate * gap / 1000:
self.emit(parser.flush(), direction)
self.last[direction] = es
if not 0 <= payload[0] <= 255:
self.emit(parser.flush(), direction)
self.put(ss, es, self.out_ann, [2, ['Configure UART for 8 data bits']])
return
self.emit(parser.feed(payload[0], ss, es), direction)

View File

@@ -0,0 +1,26 @@
# Transistor pair
Independent two-channel analyzer. Assign Vin1 and Vin2; Vstat is not used.
Available in DSView's **Пользовательские** section as **Transistor pair**.
- Minimum active ON pulse duration (mintime), independently per input.
- Minimum inactive OFF duration, independently per input.
- Deadtime from Vin1 OFF to Vin2 ON and vice versa, with independent minimums.
- Active overlap is a fault, regardless of the deadtime settings.
- Period, frequency and active duty cycle for complete cycles on each input.
- Independent active-high/active-low polarity for both inputs.
All limits are nanoseconds. Zero disables the corresponding lower limit;
equality passes. Simultaneous handovers measure zero deadtime. These settings
check measured pulses and do not debounce or filter the source waveform.
Measurements occupy separate waveform rows. Timing violations appear in the
FAULT row. Unknown edges outside the capture are not fabricated; partial
pulses do not produce full pulse widths or full-cycle frequency/duty values.
Python: `PairTimingChecker` in `logic_analyzer.decoders.transistor_pair`.
Offline API: `analyze_capture(..., dict(mode='Transistor pair', channel=0,
vin2_channel=1, vin1_mintime_ns=100, deadtime_12_ns=50))`.
For Vin/Vstat ACK checks and full driver-cycle OK/WARNING/FAULT results use
the separate **IGBT 1SP/1SD** decoder.

View File

@@ -0,0 +1 @@
from .pd import Decoder

View File

@@ -0,0 +1,88 @@
"""Two-input transistor pair analyzer, independent of driver feedback."""
import sigrokdecode as srd
from common.setgui_decoders.transistor_pair import PairTimingChecker
class Decoder(srd.Decoder):
api_version = 3
id = 'transistor_pair'
name = 'Transistor pair'
longname = 'Transistor pair: mintime / deadtime / overlap'
desc = 'Checks ON/OFF pulse limits, deadtime, overlap, frequency and duty.'
license = 'gplv2+'
inputs = ['logic']
outputs = []
tags = ['Clock/timing', 'Util']
channels = (
{'id': 'vin1', 'name': 'Vin1', 'desc': 'First transistor command'},
{'id': 'vin2', 'name': 'Vin2', 'desc': 'Second transistor command'},
)
options = (
{'id': 'vin1_active', 'desc': 'Vin1 active level', 'default': 'high', 'values': ('high', 'low')},
{'id': 'vin2_active', 'desc': 'Vin2 active level', 'default': 'high', 'values': ('high', 'low')},
{'id': 'vin1_mintime_ns', 'desc': 'Vin1 minimum ON time (ns)', 'default': 0},
{'id': 'vin2_mintime_ns', 'desc': 'Vin2 minimum ON time (ns)', 'default': 0},
{'id': 'vin1_minoff_ns', 'desc': 'Vin1 minimum OFF time (ns)', 'default': 0},
{'id': 'vin2_minoff_ns', 'desc': 'Vin2 minimum OFF time (ns)', 'default': 0},
{'id': 'deadtime_12_ns', 'desc': 'Minimum deadtime Vin1 -> Vin2 (ns)', 'default': 0},
{'id': 'deadtime_21_ns', 'desc': 'Minimum deadtime Vin2 -> Vin1 (ns)', 'default': 0},
)
annotations = (
('vin1-on', 'Vin1 ON duration'), ('vin2-on', 'Vin2 ON duration'),
('deadtime', 'Deadtime'), ('failure', 'FAULT'),
('vin1-off', 'Vin1 OFF duration'), ('vin2-off', 'Vin2 OFF duration'),
('vin1-period', 'Vin1 period / frequency / duty'),
('vin2-period', 'Vin2 period / frequency / duty'),
)
annotation_rows = (
('deadtime', 'Deadtime', (2,)),
('faults', 'FAULT: overlap / timing', (3,)),
('vin1', 'Vin1 mintime / OFF', (0, 4)),
('vin2', 'Vin2 mintime / OFF', (1, 5)),
('pwm1', 'Vin1 period / frequency / duty', (6,)),
('pwm2', 'Vin2 period / frequency / duty', (7,)),
)
def __init__(self):
self.reset()
def reset(self):
self.samplerate = None
self.checker = None
def metadata(self, key, value):
if key == srd.SRD_CONF_SAMPLERATE:
self.samplerate = value
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
def _put_events(self, events):
for event in events:
kind = event['kind']
channel = event.get('channel', 1) - 1
ann = (3 if kind in ('mintime_fail', 'deadtime_fail', 'off_fail', 'overlap') else
2 if kind == 'deadtime' else channel if kind == 'mintime_ok' else
4 + channel if kind == 'off_time' else 6 + channel)
self.put(event['start'], event['end'], self.out_ann, [ann, [event['text'], event['short']]])
def decode(self):
if not self.samplerate:
raise ValueError('Cannot decode without samplerate.')
if not self.has_channel(0) or not self.has_channel(1):
raise ValueError('Connect both Vin1 and Vin2.')
numeric = {name: self.options[name] for name in (
'vin1_mintime_ns', 'vin2_mintime_ns', 'vin1_minoff_ns', 'vin2_minoff_ns',
'deadtime_12_ns', 'deadtime_21_ns')}
self.checker = PairTimingChecker(self.samplerate,
self.options['vin1_active'] == 'high', self.options['vin2_active'] == 'high', **numeric)
# Do not retain DSView's mutable native wait tuple.
pins = list(self.wait({}))
self._put_events(self.checker.update(self.samplenum, *pins))
while True:
pins = list(self.wait([{0: 'e'}, {1: 'e'}]))
self._put_events(self.checker.update(self.samplenum, *pins))
def end(self):
if self.checker is not None:
self._put_events(self.checker.finish(self.samplenum))