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