import sys from pathlib import Path import unittest sys.path.insert(0, str(Path(__file__).resolve().parents[3] / 'python')) from logic_analyzer.decoders.transistor_pair import PairTimingChecker class TransistorPairTests(unittest.TestCase): def test_directional_deadtime_limits_and_equality(self): c = PairTimingChecker(1e9, deadtime_12_ns=30, deadtime_21_ns=50) c.update(0, 0, 0) c.update(10, 1, 0) c.update(110, 0, 0) events = c.update(140, 0, 1) self.assertEqual(events[0]['kind'], 'deadtime') self.assertEqual(events[0]['duration_ns'], 30) c.update(240, 0, 0) self.assertEqual(c.update(280, 1, 0)[0]['kind'], 'deadtime_fail') def test_independent_on_off_times_frequency_and_duty(self): c = PairTimingChecker(1e9, vin1_mintime_ns=100, vin2_mintime_ns=101, vin1_minoff_ns=201, vin2_minoff_ns=50) c.update(0, 0, 0) c.update(10, 1, 0) self.assertEqual(c.update(110, 0, 0)[0]['kind'], 'mintime_ok') c.update(140, 0, 1) self.assertEqual(c.update(240, 0, 0)[0]['kind'], 'mintime_fail') events = c.update(310, 1, 0) self.assertTrue(any(e['kind'] == 'off_fail' for e in events)) period = next(e for e in events if e['kind'] == 'period') self.assertAlmostEqual(period['frequency_hz'], 1e9/300) self.assertAlmostEqual(period['duty_percent'], 100/3) self.assertEqual((period['start'], period['end']), (10, 310)) def test_atomic_handover_active_low_and_overlap(self): c = PairTimingChecker(1e9, False, False, deadtime_12_ns=1) c.update(0, 1, 1) c.update(10, 0, 1) events = c.update(20, 1, 0) self.assertTrue(any(e['kind'] == 'deadtime_fail' and e['duration_ns'] == 0 for e in events)) c.update(30, 0, 0) self.assertEqual(c.finish(50)[0]['kind'], 'overlap') self.assertTrue(any(e['kind'] == 'overlap' for e in c.update(60, 1, 0))) def test_capture_boundary_does_not_invent_complete_pulses(self): c = PairTimingChecker(1e9) self.assertEqual(c.update(0, 1, 0), []) self.assertEqual(c.update(10, 0, 0), []) events = c.update(20, 1, 0) self.assertEqual([e['kind'] for e in events], ['off_time']) self.assertEqual(c.finish(30), []) def test_invalid_limits(self): for name in ('deadtime_12_ns', 'deadtime_21_ns', 'vin1_minoff_ns', 'vin2_minoff_ns'): for value in (-1, float('nan'), float('inf')): with self.subTest(name=name, value=value), self.assertRaises(ValueError): PairTimingChecker(1e9, **{name: value})