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,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))