Continuous harness detection (#1402)

* read in mV and make threshold board-dependent

* refactor and add SBU voltages to health

* refactor relay driving

* more refactoring and readout lock

* avoid race condition

* fix misra

* continuous detection

* 1Hz is fine

* another race condition

* use harness detection to trigger bootkick

* update orientation detection test

* more in-depth harness tests

* fix ignition

* fix

* raise threshold
This commit is contained in:
Robbe Derks
2023-05-08 13:30:23 +02:00
committed by GitHub
parent 35609dfdce
commit 2c937656cf
18 changed files with 161 additions and 80 deletions

View File

@@ -25,7 +25,7 @@ void black_enable_can_transceiver(uint8_t transceiver, bool enabled) {
void black_enable_can_transceivers(bool enabled) {
for(uint8_t i=1U; i<=4U; i++){
// Leave main CAN always on for CAN-based ignition detection
if((car_harness_status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){
if((harness.status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){
black_enable_can_transceiver(i, true);
} else {
black_enable_can_transceiver(i, enabled);
@@ -83,7 +83,7 @@ void black_set_can_mode(uint8_t mode){
switch (mode) {
case CAN_MODE_NORMAL:
case CAN_MODE_OBD_CAN2:
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(car_harness_status == HARNESS_STATUS_FLIPPED)) {
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) {
// B12,B13: disable OBD mode
set_gpio_mode(GPIOB, 12, MODE_INPUT);
set_gpio_mode(GPIOB, 13, MODE_INPUT);
@@ -160,7 +160,7 @@ void black_init(void) {
black_set_can_mode(CAN_MODE_NORMAL);
// flip CAN0 and CAN2 if we are flipped
if (car_harness_status == HARNESS_STATUS_FLIPPED) {
if (harness.status == HARNESS_STATUS_FLIPPED) {
can_flip_buses(0, 2);
}
}

View File

@@ -11,7 +11,7 @@ typedef void (*board_set_ir_power)(uint8_t percentage);
typedef void (*board_set_fan_enabled)(bool enabled);
typedef void (*board_set_phone_power)(bool enabled);
typedef void (*board_set_siren)(bool enabled);
typedef void (*board_board_tick)(bool ignition, bool usb_enum, bool heartbeat_seen);
typedef void (*board_board_tick)(bool ignition, bool usb_enum, bool heartbeat_seen, bool harness_inserted);
typedef bool (*board_read_som_gpio)(void);
struct board {

View File

@@ -25,7 +25,7 @@ void dos_enable_can_transceiver(uint8_t transceiver, bool enabled) {
void dos_enable_can_transceivers(bool enabled) {
for(uint8_t i=1U; i<=4U; i++){
// Leave main CAN always on for CAN-based ignition detection
if((car_harness_status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){
if((harness.status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){
dos_enable_can_transceiver(i, true);
} else {
dos_enable_can_transceiver(i, enabled);
@@ -53,9 +53,9 @@ void dos_set_bootkick(bool enabled){
set_gpio_output(GPIOC, 4, !enabled);
}
void dos_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen) {
if (ignition && !usb_enum) {
// enable bootkick if ignition seen
void dos_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen, bool harness_inserted) {
if ((ignition && !usb_enum) || harness_inserted) {
// enable bootkick if ignition seen or if plugged into a harness
dos_set_bootkick(true);
} else if (heartbeat_seen) {
// disable once openpilot is up
@@ -69,7 +69,7 @@ void dos_set_can_mode(uint8_t mode){
switch (mode) {
case CAN_MODE_NORMAL:
case CAN_MODE_OBD_CAN2:
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(car_harness_status == HARNESS_STATUS_FLIPPED)) {
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) {
// B12,B13: disable OBD mode
set_gpio_mode(GPIOB, 12, MODE_INPUT);
set_gpio_mode(GPIOB, 13, MODE_INPUT);
@@ -177,7 +177,7 @@ void dos_init(void) {
dos_set_can_mode(CAN_MODE_NORMAL);
// flip CAN0 and CAN2 if we are flipped
if (car_harness_status == HARNESS_STATUS_FLIPPED) {
if (harness.status == HARNESS_STATUS_FLIPPED) {
can_flip_buses(0, 2);
}

View File

@@ -22,7 +22,7 @@ void red_enable_can_transceiver(uint8_t transceiver, bool enabled) {
}
void red_enable_can_transceivers(bool enabled) {
uint8_t main_bus = (car_harness_status == HARNESS_STATUS_FLIPPED) ? 3U : 1U;
uint8_t main_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 3U : 1U;
for (uint8_t i=1U; i<=4U; i++) {
// Leave main CAN always on for CAN-based ignition detection
if (i == main_bus) {
@@ -53,7 +53,7 @@ void red_set_can_mode(uint8_t mode) {
switch (mode) {
case CAN_MODE_NORMAL:
case CAN_MODE_OBD_CAN2:
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(car_harness_status == HARNESS_STATUS_FLIPPED)) {
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) {
// B12,B13: disable normal mode
set_gpio_pullup(GPIOB, 12, PULL_NONE);
set_gpio_mode(GPIOB, 12, MODE_ANALOG);
@@ -147,7 +147,7 @@ void red_init(void) {
red_set_can_mode(CAN_MODE_NORMAL);
// flip CAN0 and CAN2 if we are flipped
if (car_harness_status == HARNESS_STATUS_FLIPPED) {
if (harness.status == HARNESS_STATUS_FLIPPED) {
can_flip_buses(0, 2);
}
}

View File

@@ -24,7 +24,7 @@ void red_chiplet_enable_can_transceiver(uint8_t transceiver, bool enabled) {
}
void red_chiplet_enable_can_transceivers(bool enabled) {
uint8_t main_bus = (car_harness_status == HARNESS_STATUS_FLIPPED) ? 3U : 1U;
uint8_t main_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 3U : 1U;
for (uint8_t i=1U; i<=4U; i++) {
// Leave main CAN always on for CAN-based ignition detection
if (i == main_bus) {
@@ -92,7 +92,7 @@ void red_chiplet_init(void) {
red_set_can_mode(CAN_MODE_NORMAL);
// flip CAN0 and CAN2 if we are flipped
if (car_harness_status == HARNESS_STATUS_FLIPPED) {
if (harness.status == HARNESS_STATUS_FLIPPED) {
can_flip_buses(0, 2);
}
}

View File

@@ -19,9 +19,9 @@ void tres_set_bootkick(bool enabled){
}
bool tres_ignition_prev = false;
void tres_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen) {
void tres_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen, bool harness_inserted) {
UNUSED(usb_enum);
if (ignition && !tres_ignition_prev) {
if ((ignition && !tres_ignition_prev) || harness_inserted) {
// enable bootkick on rising edge of ignition
tres_set_bootkick(true);
} else if (heartbeat_seen) {

View File

@@ -27,7 +27,7 @@ void uno_enable_can_transceiver(uint8_t transceiver, bool enabled) {
void uno_enable_can_transceivers(bool enabled) {
for(uint8_t i=1U; i<=4U; i++){
// Leave main CAN always on for CAN-based ignition detection
if((car_harness_status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){
if((harness.status == HARNESS_STATUS_FLIPPED) ? (i == 3U) : (i == 1U)){
uno_enable_can_transceiver(i, true);
} else {
uno_enable_can_transceiver(i, enabled);
@@ -102,7 +102,7 @@ void uno_set_can_mode(uint8_t mode){
switch (mode) {
case CAN_MODE_NORMAL:
case CAN_MODE_OBD_CAN2:
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(car_harness_status == HARNESS_STATUS_FLIPPED)) {
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) {
// B12,B13: disable OBD mode
set_gpio_mode(GPIOB, 12, MODE_INPUT);
set_gpio_mode(GPIOB, 13, MODE_INPUT);
@@ -126,10 +126,11 @@ void uno_set_can_mode(uint8_t mode){
}
}
void uno_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen) {
void uno_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen, bool harness_inserted) {
UNUSED(ignition);
UNUSED(usb_enum);
UNUSED(heartbeat_seen);
UNUSED(harness_inserted);
if (bootkick_timer != 0U) {
bootkick_timer--;
} else {
@@ -210,7 +211,7 @@ void uno_init(void) {
uno_set_can_mode(CAN_MODE_NORMAL);
// flip CAN0 and CAN2 if we are flipped
if (car_harness_status == HARNESS_STATUS_FLIPPED) {
if (harness.status == HARNESS_STATUS_FLIPPED) {
can_flip_buses(0, 2);
}

View File

@@ -22,10 +22,11 @@ uint32_t unused_read_current(void) {
return 0U;
}
void unused_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen) {
void unused_board_tick(bool ignition, bool usb_enum, bool heartbeat_seen, bool harness_inserted) {
UNUSED(ignition);
UNUSED(usb_enum);
UNUSED(heartbeat_seen);
UNUSED(harness_inserted);
}
bool unused_read_som_gpio(void) {

View File

@@ -1,8 +1,16 @@
uint8_t car_harness_status = 0U;
#define HARNESS_STATUS_NC 0U
#define HARNESS_STATUS_NORMAL 1U
#define HARNESS_STATUS_FLIPPED 2U
struct harness_t {
uint8_t status;
uint16_t sbu1_voltage_mV;
uint16_t sbu2_voltage_mV;
bool relay_driven;
bool sbu_adc_lock;
};
struct harness_t harness;
struct harness_configuration {
const bool has_harness;
GPIO_TypeDef *GPIO_SBU1;
@@ -17,26 +25,40 @@ struct harness_configuration {
uint8_t adc_channel_SBU2;
};
// this function will be the API for tici
void set_intercept_relay(bool intercept) {
if (car_harness_status != HARNESS_STATUS_NC) {
if (intercept) {
print("switching harness to intercept (relay on)\n");
} else {
print("switching harness to passthrough (relay off)\n");
}
bool drive_relay = intercept;
if (harness.status == HARNESS_STATUS_NC) {
// no harness, no relay to drive
drive_relay = false;
}
if(car_harness_status == HARNESS_STATUS_NORMAL){
set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !intercept);
} else {
set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !intercept);
}
if (drive_relay) {
harness.relay_driven = true;
}
// wait until we're not reading the analog voltages anymore
while (harness.sbu_adc_lock == true) {}
if (harness.status == HARNESS_STATUS_NORMAL) {
set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, true);
set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, !drive_relay);
} else {
set_gpio_output(current_board->harness_config->GPIO_relay_SBU1, current_board->harness_config->pin_relay_SBU1, !drive_relay);
set_gpio_output(current_board->harness_config->GPIO_relay_SBU2, current_board->harness_config->pin_relay_SBU2, true);
}
if (!drive_relay) {
harness.relay_driven = false;
}
}
bool harness_check_ignition(void) {
bool ret = false;
switch(car_harness_status){
// wait until we're not reading the analog voltages anymore
while (harness.sbu_adc_lock == true) {}
switch(harness.status){
case HARNESS_STATUS_NORMAL:
ret = !get_gpio_input(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1);
break;
@@ -50,27 +72,46 @@ bool harness_check_ignition(void) {
}
uint8_t harness_detect_orientation(void) {
uint8_t ret = HARNESS_STATUS_NC;
uint8_t ret = harness.status;
#ifndef BOOTSTUB
uint32_t sbu1_voltage = adc_get_raw(current_board->harness_config->adc_channel_SBU1);
uint32_t sbu2_voltage = adc_get_raw(current_board->harness_config->adc_channel_SBU2);
// We can't detect orientation if the relay is being driven
if (!harness.relay_driven && current_board->harness_config->has_harness) {
harness.sbu_adc_lock = true;
set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_ANALOG);
set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_ANALOG);
// Detect connection and orientation
if((sbu1_voltage < HARNESS_CONNECTED_THRESHOLD) || (sbu2_voltage < HARNESS_CONNECTED_THRESHOLD)){
if (sbu1_voltage < sbu2_voltage) {
// orientation flipped (PANDA_SBU1->HARNESS_SBU1(relay), PANDA_SBU2->HARNESS_SBU2(ign))
ret = HARNESS_STATUS_FLIPPED;
harness.sbu1_voltage_mV = adc_get_mV(current_board->harness_config->adc_channel_SBU1);
harness.sbu2_voltage_mV = adc_get_mV(current_board->harness_config->adc_channel_SBU2);
uint16_t detection_threshold = current_board->avdd_mV / 2U;
// Detect connection and orientation
if((harness.sbu1_voltage_mV < detection_threshold) || (harness.sbu2_voltage_mV < detection_threshold)){
if (harness.sbu1_voltage_mV < harness.sbu2_voltage_mV) {
// orientation flipped (PANDA_SBU1->HARNESS_SBU1(relay), PANDA_SBU2->HARNESS_SBU2(ign))
ret = HARNESS_STATUS_FLIPPED;
} else {
// orientation normal (PANDA_SBU2->HARNESS_SBU1(relay), PANDA_SBU1->HARNESS_SBU2(ign))
ret = HARNESS_STATUS_NORMAL;
}
} else {
// orientation normal (PANDA_SBU2->HARNESS_SBU1(relay), PANDA_SBU1->HARNESS_SBU2(ign))
ret = HARNESS_STATUS_NORMAL;
ret = HARNESS_STATUS_NC;
}
// Pins are not 5V tolerant in ADC mode
set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT);
set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT);
harness.sbu_adc_lock = false;
}
#endif
return ret;
}
void harness_tick(void) {
harness.status = harness_detect_orientation();
}
void harness_init(void) {
// delay such that the connection is fully made before trying orientation detection
current_board->set_led(LED_BLUE, true);
@@ -78,18 +119,13 @@ void harness_init(void) {
current_board->set_led(LED_BLUE, false);
// try to detect orientation
uint8_t ret = harness_detect_orientation();
if (ret != HARNESS_STATUS_NC) {
print("detected car harness with orientation "); puth2(ret); print("\n");
car_harness_status = ret;
// set the SBU lines to be inputs before using the relay. The lines are not 5V tolerant in ADC mode!
set_gpio_mode(current_board->harness_config->GPIO_SBU1, current_board->harness_config->pin_SBU1, MODE_INPUT);
set_gpio_mode(current_board->harness_config->GPIO_SBU2, current_board->harness_config->pin_SBU2, MODE_INPUT);
// keep busses connected by default
set_intercept_relay(false);
harness.status = harness_detect_orientation();
if (harness.status != HARNESS_STATUS_NC) {
print("detected car harness with orientation "); puth2(harness.status); print("\n");
} else {
print("failed to detect car harness!\n");
}
// keep buses connected by default
set_intercept_relay(false);
}

View File

@@ -1,6 +1,6 @@
// When changing these structs, python/__init__.py needs to be kept up to date!
#define HEALTH_PACKET_VERSION 13
#define HEALTH_PACKET_VERSION 14
struct __attribute__((packed)) health_t {
uint32_t uptime_pkt;
uint32_t voltage_pkt;
@@ -27,6 +27,8 @@ struct __attribute__((packed)) health_t {
uint8_t safety_rx_checks_invalid;
uint16_t spi_checksum_error_count;
uint8_t fan_stall_count;
uint16_t sbu1_voltage_mV;
uint16_t sbu2_voltage_mV;
};
#define CAN_HEALTH_PACKET_VERSION 4

View File

@@ -144,6 +144,7 @@ void __attribute__ ((noinline)) enable_fpu(void) {
// called at 8Hz
uint8_t loop_counter = 0U;
uint8_t previous_harness_status = HARNESS_STATUS_NC;
void tick_handler(void) {
if (TICK_TIMER->SR != 0) {
// siren
@@ -180,8 +181,11 @@ void tick_handler(void) {
current_board->set_led(LED_BLUE, (uptime_cnt & 1U) && (power_save_status == POWER_SAVE_STATUS_ENABLED));
// tick drivers at 1Hz
harness_tick();
const bool recent_heartbeat = heartbeat_counter == 0U;
current_board->board_tick(check_started(), usb_enumerated, recent_heartbeat);
current_board->board_tick(check_started(), usb_enumerated, recent_heartbeat, ((harness.status != previous_harness_status) && (harness.status != HARNESS_STATUS_NC)));
previous_harness_status = harness.status;
// increase heartbeat counter and cap it at the uint32 limit
if (heartbeat_counter < __UINT32_MAX__) {

View File

@@ -23,7 +23,7 @@ int get_health_pkt(void *dat) {
health->tx_buffer_overflow_pkt = tx_buffer_overflow;
health->rx_buffer_overflow_pkt = rx_buffer_overflow;
health->gmlan_send_errs_pkt = gmlan_send_errs;
health->car_harness_status_pkt = car_harness_status;
health->car_harness_status_pkt = harness.status;
health->safety_mode_pkt = (uint8_t)(current_safety_mode);
health->safety_param_pkt = current_safety_param;
health->alternative_experience_pkt = alternative_experience;
@@ -41,6 +41,9 @@ int get_health_pkt(void *dat) {
health->fan_power = fan_state.power;
health->fan_stall_count = fan_state.total_stall_count;
health->sbu1_voltage_mV = harness.sbu1_voltage_mV;
health->sbu2_voltage_mV = harness.sbu2_voltage_mV;
return sizeof(*health);
}

View File

@@ -2,7 +2,7 @@ void EXTI_IRQ_Handler(void);
void exti_irq_init(void) {
SYSCFG->EXTICR[2] &= ~(SYSCFG_EXTICR3_EXTI8_Msk);
if (car_harness_status == HARNESS_STATUS_FLIPPED) {
if (harness.status == HARNESS_STATUS_FLIPPED) {
// CAN2_RX
current_board->enable_can_transceiver(3U, false);
SYSCFG->EXTICR[2] |= (SYSCFG_EXTICR3_EXTI8_PA);

View File

@@ -23,9 +23,6 @@
#define MAX_LED_FADE 8192U
// Threshold voltage (mV) for either of the SBUs to be below before deciding harness is connected
#define HARNESS_CONNECTED_THRESHOLD 2500U
#define NUM_INTERRUPTS 102U // There are 102 external interrupt sources (see stm32f413.h)
#define TICK_TIMER_IRQ TIM1_BRK_TIM9_IRQn

View File

@@ -1,7 +1,7 @@
void EXTI_IRQ_Handler(void);
void exti_irq_init(void) {
if (car_harness_status == HARNESS_STATUS_FLIPPED) {
if (harness.status == HARNESS_STATUS_FLIPPED) {
// CAN2_RX IRQ on falling edge (EXTI10)
current_board->enable_can_transceiver(3U, false);
SYSCFG->EXTICR[2] &= ~(SYSCFG_EXTICR3_EXTI10_Msk);

View File

@@ -30,9 +30,6 @@ separate IRQs for RX and TX.
#define MAX_LED_FADE 10240U
// Threshold voltage (mV) for either of the SBUs to be below before deciding harness is connected
#define HARNESS_CONNECTED_THRESHOLD 40000U
// There are 163 external interrupt sources (see stm32f735xx.h)
#define NUM_INTERRUPTS 163U

View File

@@ -182,9 +182,9 @@ class Panda:
HW_TYPE_TRES = b'\x09'
CAN_PACKET_VERSION = 4
HEALTH_PACKET_VERSION = 13
HEALTH_PACKET_VERSION = 14
CAN_HEALTH_PACKET_VERSION = 4
HEALTH_STRUCT = struct.Struct("<IIIIIIIIIBBBBBBHBBBHfBBHB")
HEALTH_STRUCT = struct.Struct("<IIIIIIIIIBBBBBBHBBBHfBBHBHH")
CAN_HEALTH_STRUCT = struct.Struct("<BIBBBBBBBBIIIIIIIHHBBB")
F2_DEVICES = (HW_TYPE_PEDAL, )
@@ -200,6 +200,10 @@ class Panda:
HW_TYPE_TRES: 6600,
}
HARNESS_STATUS_NC = 0
HARNESS_STATUS_NORMAL = 1
HARNESS_STATUS_FLIPPED = 2
# first byte is for EPS scaling factor
FLAG_TOYOTA_ALT_BRAKE = (1 << 8)
FLAG_TOYOTA_STOCK_LONGITUDINAL = (2 << 8)
@@ -566,6 +570,8 @@ class Panda:
"safety_rx_checks_invalid": a[22],
"spi_checksum_error_count": a[23],
"fan_stall_count": a[24],
"sbu1_voltage_mV": a[25],
"sbu2_voltage_mV": a[26],
}
@ensure_can_health_packet_version

View File

@@ -18,17 +18,51 @@ def test_ignition(p, panda_jungle):
@pytest.mark.test_panda_types(PandaGroup.GEN2)
def test_orientation_detection(p, panda_jungle):
seen_orientations = []
for i in range(3):
panda_jungle.set_harness_orientation(i)
p.reset()
def test_harness_status(p, panda_jungle):
flipped = None
for ignition in [True, False]:
for orientation in [Panda.HARNESS_STATUS_NC, Panda.HARNESS_STATUS_NORMAL, Panda.HARNESS_STATUS_FLIPPED]:
panda_jungle.set_harness_orientation(orientation)
panda_jungle.set_ignition(ignition)
time.sleep(1)
health = p.health()
detected_orientation = health['car_harness_status']
print(f"set: {orientation} detected: {detected_orientation}")
# Orientation
if orientation == Panda.HARNESS_STATUS_NC:
assert detected_orientation == Panda.HARNESS_STATUS_NC
else:
if flipped is None:
flipped = (detected_orientation != orientation)
if orientation == Panda.HARNESS_STATUS_NORMAL:
assert detected_orientation == (Panda.HARNESS_STATUS_FLIPPED if flipped else Panda.HARNESS_STATUS_NORMAL)
else:
assert detected_orientation == (Panda.HARNESS_STATUS_NORMAL if flipped else Panda.HARNESS_STATUS_FLIPPED)
# Line ignition
assert health['ignition_line'] == (False if orientation == Panda.HARNESS_STATUS_NC else ignition)
# SBU voltages
supply_voltage_mV = 1800 if p.get_type() in [Panda.HW_TYPE_TRES, ] else 3300
if orientation == Panda.HARNESS_STATUS_NC:
assert health['sbu1_voltage_mV'] > 0.9 * supply_voltage_mV
assert health['sbu2_voltage_mV'] > 0.9 * supply_voltage_mV
else:
relay_line = 'sbu1_voltage_mV' if (detected_orientation == Panda.HARNESS_STATUS_FLIPPED) else 'sbu2_voltage_mV'
ignition_line = 'sbu2_voltage_mV' if (detected_orientation == Panda.HARNESS_STATUS_FLIPPED) else 'sbu1_voltage_mV'
assert health[relay_line] < 0.1 * supply_voltage_mV
assert health[ignition_line] > health[relay_line]
if ignition:
assert health[ignition_line] < 0.3 * supply_voltage_mV
else:
assert health[ignition_line] > 0.9 * supply_voltage_mV
detected_harness_orientation = p.health()['car_harness_status']
print(f"Detected orientation: {detected_harness_orientation}")
if (i == 0 and detected_harness_orientation != 0) or detected_harness_orientation in seen_orientations:
assert False
seen_orientations.append(detected_harness_orientation)
@pytest.mark.skip_panda_types((Panda.HW_TYPE_DOS, ))
def test_voltage(p):