Browse Source

Added calculations for PDO lists

Now there are two functions to perform calculations on PDO lists:
calculate_pdp and follows_power_rules.  calculate_pdp calculates an
estimate of the power supply's PDP, and follows_power_rules tries to
determine if the power supply follows the USB PD Power Rules.  Either
one could run into problems that cannot be detected, but for the
problems that can be detected there's a whole mound of new test code.
Clara Hobbs 5 years ago
parent
commit
b22569355b
2 changed files with 225 additions and 0 deletions
  1. 110
    0
      pdbuddy/__init__.py
  2. 115
    0
      test_pdbuddy/__init__.py

+ 110
- 0
pdbuddy/__init__.py View File

@@ -452,3 +452,113 @@ def read_pdo_list(text):
452 452
             pdo_list.append(pdo)
453 453
 
454 454
     return pdo_list
455
+
456
+
457
+def calculate_pdp(pdo_list):
458
+    """Calculate the PDP in watts of a list of PDOs
459
+
460
+    The result is only guaranteed to be correct if the power supply follows
461
+    the USB Power Delivery standard.  Since quite a few power supplies
462
+    unfortunately do not, this can really only be considered an estimate.
463
+    """
464
+    max_power = 0
465
+
466
+    # The Source Power Rules make it so the PDP can be determined by the
467
+    # highest power available from any fixed supply PDO.
468
+    for pdo in pdo_list:
469
+        if pdo.pdo_type == "fixed":
470
+            max_power = max(max_power, pdo.v / 1000.0 * pdo.i / 1000.0)
471
+
472
+    return max_power
473
+
474
+
475
+def follows_power_rules(pdo_list):
476
+    """Test whether a list of PDOs follows the Power Rules for PD 2.0
477
+
478
+    This function is a false-biased approximation; that is, when it returns
479
+    False it is definitely correct, but when it returns True it might be
480
+    incorrect.
481
+    """
482
+    # First, estimate the PDP assuming the rules are being followed
483
+    pdp = calculate_pdp(pdo_list)
484
+
485
+    # Make sure nothing exceeds the PDP
486
+    for pdo in pdo_list:
487
+        if pdo.pdo_type == "fixed":
488
+            if pdp < pdo.v / 1000.0 * pdo.i / 1000.0:
489
+                return False
490
+        # TODO: in the future, there will be more types of PDO checked here
491
+
492
+    # Check that the fixed supply PDOs look right
493
+    seen_5v = False
494
+    seen_9v = False
495
+    seen_15v = False
496
+    seen_20v = False
497
+    seen_normative_voltages = False
498
+    if pdp <= 15:
499
+        # Below 15 W, make sure the PDP is available at 5 V.
500
+        for pdo in pdo_list:
501
+            if pdo.pdo_type == "fixed" and pdo.v == 5000:
502
+                seen_5v = True
503
+                if pdo.v / 1000.0 * pdo.i / 1000.0 != pdp:
504
+                    return False
505
+        seen_normative_voltages = seen_5v
506
+    elif pdp <= 27:
507
+        # Between 15 and 27 W, make sure at least 3 A is available at 5 V and
508
+        # the PDP is available at 9 V.
509
+        for pdo in pdo_list:
510
+            if pdo.pdo_type == "fixed" and pdo.v == 5000:
511
+                seen_5v = True
512
+                if pdo.i < 3000.0:
513
+                    return False
514
+            elif pdo.pdo_type == "fixed" and pdo.v == 9000:
515
+                seen_9v = True
516
+                if pdo.v / 1000.0 * pdo.i / 1000.0 != pdp:
517
+                    return False
518
+        seen_normative_voltages = seen_5v and seen_9v
519
+    elif pdp <= 45:
520
+        # Between 27 and 45 W, make sure at least 3 A is available at 5 and
521
+        # 9 V, and the PDP is available at 15 V.
522
+        for pdo in pdo_list:
523
+            if pdo.pdo_type == "fixed" and pdo.v == 5000:
524
+                seen_5v = True
525
+                if pdo.i < 3000.0:
526
+                    return False
527
+            elif pdo.pdo_type == "fixed" and pdo.v == 9000:
528
+                seen_9v = True
529
+                if pdo.i < 3000.0:
530
+                    return False
531
+            elif pdo.pdo_type == "fixed" and pdo.v == 15000:
532
+                seen_15v = True
533
+                if pdo.v / 1000.0 * pdo.i / 1000.0 != pdp:
534
+                    return False
535
+        seen_normative_voltages = seen_5v and seen_9v and seen_15v
536
+    else:
537
+        # Above 45 W, make sure at least 3 A is available at 5, 9, and 15 V,
538
+        # and the PDP is available at 20 V.
539
+        for pdo in pdo_list:
540
+            if pdo.pdo_type == "fixed" and pdo.v == 5000:
541
+                seen_5v = True
542
+                if pdo.i < 3000.0:
543
+                    return False
544
+            elif pdo.pdo_type == "fixed" and pdo.v == 9000:
545
+                seen_9v = True
546
+                if pdo.i < 3000.0:
547
+                    return False
548
+            elif pdo.pdo_type == "fixed" and pdo.v == 15000:
549
+                seen_15v = True
550
+                if pdo.i < 3000.0:
551
+                    return False
552
+            elif pdo.pdo_type == "fixed" and pdo.v == 20000:
553
+                seen_20v = True
554
+                if pdo.v / 1000.0 * pdo.i / 1000.0 != pdp:
555
+                    return False
556
+        seen_normative_voltages = seen_5v and seen_9v and seen_15v and seen_20v
557
+
558
+    if not seen_normative_voltages:
559
+        return False
560
+
561
+    # TODO: there are several things this currently doesn't test, such as
562
+    # variable and battery PDOs.
563
+
564
+    return True

+ 115
- 0
test_pdbuddy/__init__.py View File

@@ -319,6 +319,10 @@ class ReadPDOTestCase(unittest.TestCase):
319 319
         rp_unknown_notzero = pdbuddy.read_pdo([b"PDO 1: FFFFFFFF"])
320 320
         self.assertEqual(self.unknown_notzero, rp_unknown_notzero)
321 321
 
322
+    def test_read_none(self):
323
+        none_pdo = pdbuddy.read_pdo([b"No Source_Capabilities"])
324
+        self.assertEqual(none_pdo, None)
325
+
322 326
 
323 327
 class ReadPDOListTestCase(unittest.TestCase):
324 328
 
@@ -353,3 +357,114 @@ class ReadPDOListTestCase(unittest.TestCase):
353 357
         self.assertEqual(pdo_list[1], self.src_fixed_minimal)
354 358
         self.assertEqual(pdo_list[2], self.unknown_zero)
355 359
         self.assertEqual(pdo_list[3], self.unknown_notzero)
360
+
361
+
362
+class PDOListCalculationsTestCase(unittest.TestCase):
363
+
364
+    def setUp(self):
365
+        self.src_fixed_5v_1p5a = pdbuddy.SrcFixedPDO(False, False, True,
366
+                False, False, 0, 5000, 1500)
367
+        self.src_fixed_5v_3a = pdbuddy.SrcFixedPDO(False, False, True, False,
368
+                False, 0, 5000, 3000)
369
+        self.src_fixed_9v_1p6a = pdbuddy.SrcFixedPDO(False, False, False,
370
+                False, False, 0, 9000, 1600)
371
+        self.src_fixed_9v_3a = pdbuddy.SrcFixedPDO(False, False, False, False,
372
+                False, 0, 9000, 3000)
373
+        self.src_fixed_10v_1p5a = pdbuddy.SrcFixedPDO(False, False, False,
374
+                False, False, 0, 10000, 1500)
375
+        self.src_fixed_12v_5a = pdbuddy.SrcFixedPDO(False, False, False, False,
376
+                False, 0, 12000, 5000)
377
+        self.src_fixed_15v_1p8a = pdbuddy.SrcFixedPDO(False, False, False,
378
+                False, False, 0, 15000, 1800)
379
+        self.src_fixed_15v_3a = pdbuddy.SrcFixedPDO(False, False, False, False,
380
+                False, 0, 15000, 3000)
381
+        self.src_fixed_20v_2p25a = pdbuddy.SrcFixedPDO(False, False, False,
382
+                False, False, 0, 20000, 2250)
383
+        self.src_fixed_20v_5a = pdbuddy.SrcFixedPDO(False, False, False, False,
384
+                False, 0, 20000, 5000)
385
+
386
+    def test_calculate_pdp_15w(self):
387
+        self.assertEqual(pdbuddy.calculate_pdp([self.src_fixed_5v_3a]), 15)
388
+        self.assertEqual(pdbuddy.calculate_pdp([self.src_fixed_5v_3a,
389
+                self.src_fixed_9v_1p6a]), 15)
390
+
391
+    def test_calculate_pdp_27w(self):
392
+        self.assertEqual(pdbuddy.calculate_pdp([self.src_fixed_5v_3a,
393
+                self.src_fixed_9v_3a]), 27)
394
+        self.assertEqual(pdbuddy.calculate_pdp([self.src_fixed_5v_3a,
395
+                self.src_fixed_9v_3a, self.src_fixed_15v_1p8a]), 27)
396
+
397
+    def test_calculate_pdp_45w(self):
398
+        self.assertEqual(pdbuddy.calculate_pdp([self.src_fixed_5v_3a,
399
+                self.src_fixed_9v_3a, self.src_fixed_15v_3a]), 45)
400
+        self.assertEqual(pdbuddy.calculate_pdp([self.src_fixed_5v_3a,
401
+                self.src_fixed_9v_3a, self.src_fixed_15v_3a,
402
+                self.src_fixed_20v_2p25a]), 45)
403
+
404
+    def test_calculate_pdp_100w(self):
405
+        self.assertEqual(pdbuddy.calculate_pdp([self.src_fixed_5v_3a,
406
+                self.src_fixed_9v_3a, self.src_fixed_15v_3a,
407
+                self.src_fixed_20v_5a]), 100)
408
+        self.assertEqual(pdbuddy.calculate_pdp([self.src_fixed_5v_1p5a,
409
+                self.src_fixed_12v_5a, self.src_fixed_20v_5a]), 100)
410
+
411
+    def test_follows_power_rules_true(self):
412
+        # <= 15 W
413
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_1p5a]))
414
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_3a]))
415
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
416
+                self.src_fixed_9v_1p6a]))
417
+        # <= 27 W
418
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
419
+                self.src_fixed_9v_3a]))
420
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
421
+                self.src_fixed_9v_3a, self.src_fixed_15v_1p8a]))
422
+        # <= 45 W
423
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
424
+                self.src_fixed_9v_3a, self.src_fixed_15v_3a]))
425
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
426
+                self.src_fixed_9v_3a, self.src_fixed_15v_3a,
427
+                self.src_fixed_20v_2p25a]))
428
+        # <= 100 W
429
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
430
+                self.src_fixed_9v_3a, self.src_fixed_15v_3a,
431
+                self.src_fixed_20v_5a]))
432
+        self.assertTrue(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
433
+                self.src_fixed_9v_3a, self.src_fixed_10v_1p5a,
434
+                self.src_fixed_12v_5a, self.src_fixed_15v_3a,
435
+                self.src_fixed_20v_5a]))
436
+
437
+    def test_follows_power_rules_false(self):
438
+        # <= 15 W
439
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_10v_1p5a]))
440
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_1p5a,
441
+            self.src_fixed_10v_1p5a]))
442
+        # <= 27 W
443
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_9v_3a]))
444
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_1p5a,
445
+                self.src_fixed_9v_3a]))
446
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
447
+                self.src_fixed_9v_1p6a, self.src_fixed_15v_1p8a]))
448
+        # <= 45 W
449
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_20v_2p25a]))
450
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_1p5a,
451
+                self.src_fixed_9v_3a, self.src_fixed_15v_3a]))
452
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
453
+                self.src_fixed_9v_1p6a, self.src_fixed_15v_3a]))
454
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
455
+                self.src_fixed_9v_3a, self.src_fixed_15v_1p8a,
456
+                self.src_fixed_20v_2p25a]))
457
+        # <= 100 W
458
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_20v_5a]))
459
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_1p5a,
460
+                self.src_fixed_9v_3a, self.src_fixed_15v_3a,
461
+                self.src_fixed_20v_5a]))
462
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
463
+                self.src_fixed_9v_1p6a, self.src_fixed_15v_3a,
464
+                self.src_fixed_20v_5a]))
465
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
466
+                self.src_fixed_9v_3a, self.src_fixed_15v_1p8a,
467
+                self.src_fixed_20v_5a]))
468
+        self.assertFalse(pdbuddy.follows_power_rules([self.src_fixed_5v_3a,
469
+                self.src_fixed_9v_3a, self.src_fixed_12v_5a,
470
+                self.src_fixed_15v_3a, self.src_fixed_20v_2p25a]))

Loading…
Cancel
Save