You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					201 lines
				
				8.3 KiB
			
		
		
			
		
	
	
					201 lines
				
				8.3 KiB
			| 
								 
											5 years ago
										 
									 | 
							
								#!/usr/bin/env python3
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								import pytest
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								from openpilot.common.params import Params
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								from openpilot.system.thermald.power_monitoring import PowerMonitoring, CAR_BATTERY_CAPACITY_uWh, \
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								                                                CAR_CHARGING_RATE_W, VBATT_PAUSE_CHARGING, DELAY_SHUTDOWN_TIME_S
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								# Create fake time
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								ssb = 0.
							 | 
						||
| 
								 | 
							
								def mock_time_monotonic():
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								  global ssb
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								  ssb += 1.
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								  return ssb
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								TEST_DURATION_S = 50
							 | 
						||
| 
								 
											3 years ago
										 
									 | 
							
								GOOD_VOLTAGE = 12 * 1e3
							 | 
						||
| 
								 | 
							
								VOLTAGE_BELOW_PAUSE_CHARGING = (VBATT_PAUSE_CHARGING - 1) * 1e3
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								def pm_patch(mocker, name, value, constant=False):
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								  if constant:
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    mocker.patch(f"openpilot.system.thermald.power_monitoring.{name}", value)
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  else:
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    mocker.patch(f"openpilot.system.thermald.power_monitoring.{name}", return_value=value)
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								@pytest.fixture(autouse=True)
							 | 
						||
| 
								 | 
							
								def mock_time(mocker):
							 | 
						||
| 
								 | 
							
								  mocker.patch("time.monotonic", mock_time_monotonic)
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								class TestPowerMonitoring:
							 | 
						||
| 
								 | 
							
								  def setup_method(self):
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								    self.params = Params()
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								  # Test to see that it doesn't do anything when pandaState is None
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_panda_state_present(self):
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    for _ in range(10):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								      pm.calculate(None, None)
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    assert pm.get_power_used() == 0
							 | 
						||
| 
								 | 
							
								    assert pm.get_car_battery_capacity() == (CAR_BATTERY_CAPACITY_uWh / 10)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to see that it doesn't integrate offroad when ignition is True
							 | 
						||
| 
								 
											3 years ago
										 
									 | 
							
								  def test_offroad_ignition(self):
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    for _ in range(10):
							 | 
						||
| 
								 
											3 years ago
										 
									 | 
							
								      pm.calculate(GOOD_VOLTAGE, True)
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    assert pm.get_power_used() == 0
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to see that it integrates with discharging battery
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_offroad_integration_discharging(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 4
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    for _ in range(TEST_DURATION_S + 1):
							 | 
						||
| 
								 | 
							
								      pm.calculate(GOOD_VOLTAGE, False)
							 | 
						||
| 
								 | 
							
								    expected_power_usage = ((TEST_DURATION_S/3600) * POWER_DRAW * 1e6)
							 | 
						||
| 
								 | 
							
								    assert abs(pm.get_power_used() - expected_power_usage) < 10
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to check positive integration of car_battery_capacity
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_car_battery_integration_onroad(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 4
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = 0
							 | 
						||
| 
								 | 
							
								    for _ in range(TEST_DURATION_S + 1):
							 | 
						||
| 
								 | 
							
								      pm.calculate(GOOD_VOLTAGE, True)
							 | 
						||
| 
								 | 
							
								    expected_capacity = ((TEST_DURATION_S/3600) * CAR_CHARGING_RATE_W * 1e6)
							 | 
						||
| 
								 | 
							
								    assert abs(pm.get_car_battery_capacity() - expected_capacity) < 10
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to check positive integration upper limit
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_car_battery_integration_upper_limit(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 4
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh - 1000
							 | 
						||
| 
								 | 
							
								    for _ in range(TEST_DURATION_S + 1):
							 | 
						||
| 
								 | 
							
								      pm.calculate(GOOD_VOLTAGE, True)
							 | 
						||
| 
								 | 
							
								    estimated_capacity = CAR_BATTERY_CAPACITY_uWh + (CAR_CHARGING_RATE_W / 3600 * 1e6)
							 | 
						||
| 
								 | 
							
								    assert abs(pm.get_car_battery_capacity() - estimated_capacity) < 10
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to check negative integration of car_battery_capacity
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_car_battery_integration_offroad(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 4
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
							 | 
						||
| 
								 | 
							
								    for _ in range(TEST_DURATION_S + 1):
							 | 
						||
| 
								 | 
							
								      pm.calculate(GOOD_VOLTAGE, False)
							 | 
						||
| 
								 | 
							
								    expected_capacity = CAR_BATTERY_CAPACITY_uWh - ((TEST_DURATION_S/3600) * POWER_DRAW * 1e6)
							 | 
						||
| 
								 | 
							
								    assert abs(pm.get_car_battery_capacity() - expected_capacity) < 10
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to check negative integration lower limit
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_car_battery_integration_lower_limit(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 4
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = 1000
							 | 
						||
| 
								 | 
							
								    for _ in range(TEST_DURATION_S + 1):
							 | 
						||
| 
								 | 
							
								      pm.calculate(GOOD_VOLTAGE, False)
							 | 
						||
| 
								 | 
							
								    estimated_capacity = 0 - ((1/3600) * POWER_DRAW * 1e6)
							 | 
						||
| 
								 | 
							
								    assert abs(pm.get_car_battery_capacity() - estimated_capacity) < 10
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to check policy of stopping charging after MAX_TIME_OFFROAD_S
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_max_time_offroad(self, mocker):
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    MOCKED_MAX_OFFROAD_TIME = 3600
							 | 
						||
| 
								 
											3 years ago
										 
									 | 
							
								    POWER_DRAW = 0 # To stop shutting down for other reasons
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "MAX_TIME_OFFROAD_S", MOCKED_MAX_OFFROAD_TIME, constant=True)
							 | 
						||
| 
								 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
							 | 
						||
| 
								 | 
							
								    start_time = ssb
							 | 
						||
| 
								 | 
							
								    ignition = False
							 | 
						||
| 
								 | 
							
								    while ssb <= start_time + MOCKED_MAX_OFFROAD_TIME:
							 | 
						||
| 
								 | 
							
								      pm.calculate(GOOD_VOLTAGE, ignition)
							 | 
						||
| 
								 | 
							
								      if (ssb - start_time) % 1000 == 0 and ssb < start_time + MOCKED_MAX_OFFROAD_TIME:
							 | 
						||
| 
								 | 
							
								        assert not pm.should_shutdown(ignition, True, start_time, False)
							 | 
						||
| 
								 | 
							
								    assert pm.should_shutdown(ignition, True, start_time, False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def test_car_voltage(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 0 # To stop shutting down for other reasons
							 | 
						||
| 
								 
											3 years ago
										 
									 | 
							
								    TEST_TIME = 350
							 | 
						||
| 
								 
											3 years ago
										 
									 | 
							
								    VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S = 50
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S", VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S, constant=True)
							 | 
						||
| 
								 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
							 | 
						||
| 
								 | 
							
								    ignition = False
							 | 
						||
| 
								 | 
							
								    start_time = ssb
							 | 
						||
| 
								 | 
							
								    for i in range(TEST_TIME):
							 | 
						||
| 
								 | 
							
								      pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
							 | 
						||
| 
								 | 
							
								      if i % 10 == 0:
							 | 
						||
| 
								 | 
							
								        assert pm.should_shutdown(ignition, True, start_time, True) == \
							 | 
						||
| 
								 | 
							
								                          (pm.car_voltage_mV < VBATT_PAUSE_CHARGING * 1e3 and \
							 | 
						||
| 
								 | 
							
								                          (ssb - start_time) > VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S and \
							 | 
						||
| 
								 | 
							
								                            (ssb - start_time) > DELAY_SHUTDOWN_TIME_S)
							 | 
						||
| 
								 | 
							
								    assert pm.should_shutdown(ignition, True, start_time, True)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to check policy of not stopping charging when DisablePowerDown is set
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_disable_power_down(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 0 # To stop shutting down for other reasons
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    TEST_TIME = 100
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								    self.params.put_bool("DisablePowerDown", True)
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
							 | 
						||
| 
								 | 
							
								    ignition = False
							 | 
						||
| 
								 | 
							
								    for i in range(TEST_TIME):
							 | 
						||
| 
								 | 
							
								      pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
							 | 
						||
| 
								 | 
							
								      if i % 10 == 0:
							 | 
						||
| 
								 | 
							
								        assert not pm.should_shutdown(ignition, True, ssb, False)
							 | 
						||
| 
								 | 
							
								    assert not pm.should_shutdown(ignition, True, ssb, False)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 | 
							
								  # Test to check policy of not stopping charging when ignition
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_ignition(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 0 # To stop shutting down for other reasons
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								    TEST_TIME = 100
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
							 | 
						||
| 
								 | 
							
								    ignition = True
							 | 
						||
| 
								 | 
							
								    for i in range(TEST_TIME):
							 | 
						||
| 
								 | 
							
								      pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
							 | 
						||
| 
								 | 
							
								      if i % 10 == 0:
							 | 
						||
| 
								 | 
							
								        assert not pm.should_shutdown(ignition, True, ssb, False)
							 | 
						||
| 
								 | 
							
								    assert not pm.should_shutdown(ignition, True, ssb, False)
							 | 
						||
| 
								 
											5 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								  # Test to check policy of not stopping charging when harness is not connected
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								  def test_harness_connection(self, mocker):
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    POWER_DRAW = 0 # To stop shutting down for other reasons
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								    TEST_TIME = 100
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    pm_patch(mocker, "HARDWARE.get_current_power_draw", POWER_DRAW)
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = CAR_BATTERY_CAPACITY_uWh
							 | 
						||
| 
								 
											4 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								    ignition = False
							 | 
						||
| 
								 | 
							
								    for i in range(TEST_TIME):
							 | 
						||
| 
								 | 
							
								      pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
							 | 
						||
| 
								 | 
							
								      if i % 10 == 0:
							 | 
						||
| 
								 | 
							
								        assert not pm.should_shutdown(ignition, False, ssb, False)
							 | 
						||
| 
								 | 
							
								    assert not pm.should_shutdown(ignition, False, ssb, False)
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								
							 | 
						||
| 
								 
											3 years ago
										 
									 | 
							
								  def test_delay_shutdown_time(self):
							 | 
						||
| 
								 | 
							
								    pm = PowerMonitoring()
							 | 
						||
| 
								 | 
							
								    pm.car_battery_capacity_uWh = 0
							 | 
						||
| 
								 | 
							
								    ignition = False
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								    in_car = True
							 | 
						||
| 
								 
											3 years ago
										 
									 | 
							
								    offroad_timestamp = ssb
							 | 
						||
| 
								 | 
							
								    started_seen = True
							 | 
						||
| 
								 | 
							
								    pm.calculate(VOLTAGE_BELOW_PAUSE_CHARGING, ignition)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while ssb < offroad_timestamp + DELAY_SHUTDOWN_TIME_S:
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								      assert not pm.should_shutdown(ignition, in_car,
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								                                          offroad_timestamp,
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								                                          started_seen), \
							 | 
						||
| 
								 | 
							
								                       f"Should not shutdown before {DELAY_SHUTDOWN_TIME_S} seconds offroad time"
							 | 
						||
| 
								 | 
							
								    assert pm.should_shutdown(ignition, in_car,
							 | 
						||
| 
								 
											2 years ago
										 
									 | 
							
								                                       offroad_timestamp,
							 | 
						||
| 
								 
											1 year ago
										 
									 | 
							
								                                       started_seen), \
							 | 
						||
| 
								 | 
							
								                    f"Should shutdown after {DELAY_SHUTDOWN_TIME_S} seconds offroad time"
							 |