astra/pd-power-supply
USB-PD Power Supply with RP2040 controller
- Version
- 1.1.6
- License
- unset
- Stars
- 0
scripts/test-pd-control.py
import sys, unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'firmware'))
from pd_control import CH224Q, KnobSelector, knob_voltage, parse_pdos, supports_pps
def fixed(mv,ma=3000): return (mv//50)<<10 | ma//10
def pps(lo=3300,hi=20000,ma=3000): return 3<<30 | (hi//100)<<17 | (lo//100)<<8 | ma//50
def pack(pdos): return b''.join(x.to_bytes(4,'little') for x in pdos)
class Bus:
def __init__(self,address=0x22): self.address=address; self.writes=[]
def scan(self): return [self.address]
def writeto_mem(self,a,r,v): self.writes.append((a,r,v[0]))
class Tests(unittest.TestCase):
def test_all_dial_combinations(self):
for coarse in range(0,65536,257):
for fine in range(0,65536,257):
v=knob_voltage(coarse,fine)
self.assertTrue(3300<=v<=20000);self.assertEqual(v%100,0)
self.assertEqual(knob_voltage(0,0),3300)
self.assertEqual(knob_voltage(65535,65535),20000)
self.assertEqual(knob_voltage(32768,65535)-knob_voltage(32768,0),1000)
def test_capability_framing(self):
raw=pack([fixed(5000),fixed(9000),pps()])
bare=parse_pdos(raw+b'\x00'*20)
self.assertEqual(bare,parse_pdos((3<<12|0x81).to_bytes(2,'little')+raw))
self.assertTrue(supports_pps(bare,7300))
self.assertFalse(supports_pps(parse_pdos(pack([fixed(5000),fixed(20000)])),7300))
self.assertFalse(supports_pps(parse_pdos(pack([fixed(5000),pps(ma=1000)])),7300))
self.assertFalse(supports_pps(parse_pdos(pack([fixed(5000),pps(5000,11000)])),3300))
self.assertFalse(supports_pps(bare,20100))
def test_invalid_caps_fail_closed(self):
for data in (b'',b'\xff'*48,pack([fixed(9000),pps()]),pack([fixed(5000),pps(12000,5000)])):
with self.assertRaises(ValueError):parse_pdos(data)
def test_voltage_before_pps_enable_and_both_addresses(self):
for address in (0x22,0x23):
bus=Bus(address);pd=CH224Q(bus);pd.connect()
self.assertEqual(bus.writes[-1],(address,0x0a,0))
pd.pdos=parse_pdos(pack([fixed(5000),pps()]))
pd.request(7300)
self.assertEqual(bus.writes[-2:],[(address,0x53,73),(address,0x0a,6)])
pd.request(7400);self.assertEqual(bus.writes[-1],(address,0x53,74))
def test_unsupported_restores_5v(self):
bus=Bus();pd=CH224Q(bus);pd.connect()
pd.pdos=parse_pdos(pack([fixed(5000),fixed(9000)]))
with self.assertRaises(ValueError):pd.request(7300)
self.assertEqual(pd.requested_mv,5000);self.assertEqual(pd.mode,'fixed')
self.assertEqual(bus.writes[-1],(0x22,0x0a,0))
def test_off_grid_and_out_of_range(self):
pd=CH224Q(Bus());pd.connect()
for mv in (0,3200,3333,20100,28000):
with self.assertRaises(ValueError):pd.request(mv)
def test_debounce(self):
k=KnobSelector()
for i in range(3):self.assertEqual(k.update(0,0),5000)
self.assertEqual(k.update(0,0),3300)
for i in range(10):k.update(65535 if i%2 else 0,32768)
self.assertEqual(k.value,3300)
if __name__=='__main__':unittest.main()