| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "safety_declarations.h" | ||
| 4 | #include "safety_volkswagen_common.h" | ||
| 5 | |||
| 6 | static bool volkswagen_mqb_brake_pedal_switch = false; | ||
| 7 | static bool volkswagen_mqb_brake_pressure_detected = false; | ||
| 8 | |||
| 9 | |||
| 10 | 67 | static safety_config volkswagen_mqb_init(uint16_t param) { | |
| 11 | // Transmit of GRA_ACC_01 is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration | ||
| 12 | static const CanMsg VOLKSWAGEN_MQB_STOCK_TX_MSGS[] = {{MSG_HCA_01, 0, 8}, {MSG_GRA_ACC_01, 0, 8}, {MSG_GRA_ACC_01, 2, 8}, | ||
| 13 | {MSG_LDW_02, 0, 8}, {MSG_LH_EPS_03, 2, 8}}; | ||
| 14 | |||
| 15 | static const CanMsg VOLKSWAGEN_MQB_LONG_TX_MSGS[] = {{MSG_HCA_01, 0, 8}, {MSG_LDW_02, 0, 8}, {MSG_LH_EPS_03, 2, 8}, | ||
| 16 | {MSG_ACC_02, 0, 8}, {MSG_ACC_06, 0, 8}, {MSG_ACC_07, 0, 8}}; | ||
| 17 | |||
| 18 | static RxCheck volkswagen_mqb_rx_checks[] = { | ||
| 19 | {.msg = {{MSG_ESP_19, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, | ||
| 20 | {.msg = {{MSG_LH_EPS_03, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, | ||
| 21 | {.msg = {{MSG_ESP_05, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, | ||
| 22 | {.msg = {{MSG_TSK_06, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, | ||
| 23 | {.msg = {{MSG_MOTOR_20, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, | ||
| 24 | {.msg = {{MSG_MOTOR_14, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 10U}, { 0 }, { 0 }}}, | ||
| 25 | {.msg = {{MSG_GRA_ACC_01, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 33U}, { 0 }, { 0 }}}, | ||
| 26 | }; | ||
| 27 | |||
| 28 | UNUSED(param); | ||
| 29 | |||
| 30 | 67 | volkswagen_set_button_prev = false; | |
| 31 | 67 | volkswagen_resume_button_prev = false; | |
| 32 | 67 | volkswagen_mqb_brake_pedal_switch = false; | |
| 33 | 67 | volkswagen_mqb_brake_pressure_detected = false; | |
| 34 | |||
| 35 | #ifdef ALLOW_DEBUG | ||
| 36 | 67 | volkswagen_longitudinal = GET_FLAG(param, FLAG_VOLKSWAGEN_LONG_CONTROL); | |
| 37 | #endif | ||
| 38 | 67 | gen_crc_lookup_table_8(0x2F, volkswagen_crc8_lut_8h2f); | |
| 39 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 32 times.
|
67 | return volkswagen_longitudinal ? BUILD_SAFETY_CFG(volkswagen_mqb_rx_checks, VOLKSWAGEN_MQB_LONG_TX_MSGS) : \ |
| 40 | BUILD_SAFETY_CFG(volkswagen_mqb_rx_checks, VOLKSWAGEN_MQB_STOCK_TX_MSGS); | ||
| 41 | } | ||
| 42 | |||
| 43 | 21021 | static void volkswagen_mqb_rx_hook(const CANPacket_t *to_push) { | |
| 44 |
2/2✓ Branch 0 taken 9757 times.
✓ Branch 1 taken 11264 times.
|
21021 | if (GET_BUS(to_push) == 0U) { |
| 45 | 9757 | int addr = GET_ADDR(to_push); | |
| 46 | |||
| 47 | // Update in-motion state by sampling wheel speeds | ||
| 48 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9741 times.
|
9757 | if (addr == MSG_ESP_19) { |
| 49 | // sum 4 wheel speeds | ||
| 50 | 16 | int speed = 0; | |
| 51 |
2/2✓ Branch 0 taken 64 times.
✓ Branch 1 taken 16 times.
|
80 | for (uint8_t i = 0U; i < 8U; i += 2U) { |
| 52 | 64 | int wheel_speed = GET_BYTE(to_push, i) | (GET_BYTE(to_push, i + 1U) << 8); | |
| 53 | 64 | speed += wheel_speed; | |
| 54 | } | ||
| 55 | // Check all wheel speeds for any movement | ||
| 56 | 16 | vehicle_moving = speed > 0; | |
| 57 | } | ||
| 58 | |||
| 59 | // Update driver input torque samples | ||
| 60 | // Signal: LH_EPS_03.EPS_Lenkmoment (absolute torque) | ||
| 61 | // Signal: LH_EPS_03.EPS_VZ_Lenkmoment (direction) | ||
| 62 |
2/2✓ Branch 0 taken 4024 times.
✓ Branch 1 taken 5733 times.
|
9757 | if (addr == MSG_LH_EPS_03) { |
| 63 | 4024 | int torque_driver_new = GET_BYTE(to_push, 5) | ((GET_BYTE(to_push, 6) & 0x1FU) << 8); | |
| 64 | 4024 | int sign = (GET_BYTE(to_push, 6) & 0x80U) >> 7; | |
| 65 |
2/2✓ Branch 0 taken 1976 times.
✓ Branch 1 taken 2048 times.
|
4024 | if (sign == 1) { |
| 66 | 1976 | torque_driver_new *= -1; | |
| 67 | } | ||
| 68 | 4024 | update_sample(&torque_driver, torque_driver_new); | |
| 69 | } | ||
| 70 | |||
| 71 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 9743 times.
|
9757 | if (addr == MSG_TSK_06) { |
| 72 | // When using stock ACC, enter controls on rising edge of stock ACC engage, exit on disengage | ||
| 73 | // Always exit controls on main switch off | ||
| 74 | // Signal: TSK_06.TSK_Status | ||
| 75 | 14 | int acc_status = (GET_BYTE(to_push, 3) & 0x7U); | |
| 76 |
4/6✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 11 times.
|
14 | bool cruise_engaged = (acc_status == 3) || (acc_status == 4) || (acc_status == 5); |
| 77 |
4/4✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 3 times.
|
14 | acc_main_on = cruise_engaged || (acc_status == 2); |
| 78 | |||
| 79 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if (!volkswagen_longitudinal) { |
| 80 | 7 | pcm_cruise_check(cruise_engaged); | |
| 81 | } | ||
| 82 | |||
| 83 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
|
14 | if (!acc_main_on) { |
| 84 | 3 | controls_allowed = false; | |
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 9750 times.
|
9757 | if (addr == MSG_GRA_ACC_01) { |
| 89 | // If using openpilot longitudinal, enter controls on falling edge of Set or Resume with main switch on | ||
| 90 | // Signal: GRA_ACC_01.GRA_Tip_Setzen | ||
| 91 | // Signal: GRA_ACC_01.GRA_Tip_Wiederaufnahme | ||
| 92 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (volkswagen_longitudinal) { |
| 93 | 7 | bool set_button = GET_BIT(to_push, 16U); | |
| 94 | 7 | bool resume_button = GET_BIT(to_push, 19U); | |
| 95 |
8/8✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
|
7 | if ((volkswagen_set_button_prev && !set_button) || (volkswagen_resume_button_prev && !resume_button)) { |
| 96 | 2 | controls_allowed = acc_main_on; | |
| 97 | } | ||
| 98 | 7 | volkswagen_set_button_prev = set_button; | |
| 99 | 7 | volkswagen_resume_button_prev = resume_button; | |
| 100 | } | ||
| 101 | // Always exit controls on rising edge of Cancel | ||
| 102 | // Signal: GRA_ACC_01.GRA_Abbrechen | ||
| 103 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
|
7 | if (GET_BIT(to_push, 13U)) { |
| 104 | 1 | controls_allowed = false; | |
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // Signal: Motor_20.MO_Fahrpedalrohwert_01 | ||
| 109 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 9737 times.
|
9757 | if (addr == MSG_MOTOR_20) { |
| 110 | 20 | gas_pressed = ((GET_BYTES(to_push, 0, 4) >> 12) & 0xFFU) != 0U; | |
| 111 | } | ||
| 112 | |||
| 113 | // Signal: Motor_14.MO_Fahrer_bremst (ECU detected brake pedal switch F63) | ||
| 114 |
2/2✓ Branch 0 taken 42 times.
✓ Branch 1 taken 9715 times.
|
9757 | if (addr == MSG_MOTOR_14) { |
| 115 | 42 | volkswagen_mqb_brake_pedal_switch = (GET_BYTE(to_push, 3) & 0x10U) >> 4; | |
| 116 | } | ||
| 117 | |||
| 118 | // Signal: ESP_05.ESP_Fahrer_bremst (ESP detected driver brake pressure above platform specified threshold) | ||
| 119 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9741 times.
|
9757 | if (addr == MSG_ESP_05) { |
| 120 | 16 | volkswagen_mqb_brake_pressure_detected = (GET_BYTE(to_push, 3) & 0x4U) >> 2; | |
| 121 | } | ||
| 122 | |||
| 123 |
4/4✓ Branch 0 taken 9727 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 9721 times.
|
9757 | brake_pressed = volkswagen_mqb_brake_pedal_switch || volkswagen_mqb_brake_pressure_detected; |
| 124 | |||
| 125 | 9757 | generic_rx_checks((addr == MSG_HCA_01)); | |
| 126 | } | ||
| 127 | 21021 | } | |
| 128 | |||
| 129 | 6855 | static bool volkswagen_mqb_tx_hook(const CANPacket_t *to_send) { | |
| 130 | // lateral limits | ||
| 131 | const SteeringLimits VOLKSWAGEN_MQB_STEERING_LIMITS = { | ||
| 132 | .max_steer = 300, // 3.0 Nm (EPS side max of 3.0Nm with fault if violated) | ||
| 133 | .max_rt_delta = 75, // 4 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 50 ; 50 * 1.5 for safety pad = 75 | ||
| 134 | .max_rt_interval = 250000, // 250ms between real time checks | ||
| 135 | .max_rate_up = 4, // 2.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) | ||
| 136 | .max_rate_down = 10, // 5.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) | ||
| 137 | .driver_torque_allowance = 80, | ||
| 138 | .driver_torque_multiplier = 3, | ||
| 139 | .type = TorqueDriverLimited, | ||
| 140 | }; | ||
| 141 | |||
| 142 | // longitudinal limits | ||
| 143 | // acceleration in m/s2 * 1000 to avoid floating point math | ||
| 144 | const LongitudinalLimits VOLKSWAGEN_MQB_LONG_LIMITS = { | ||
| 145 | .max_accel = 2000, | ||
| 146 | .min_accel = -3500, | ||
| 147 | .inactive_accel = 3010, // VW sends one increment above the max range when inactive | ||
| 148 | }; | ||
| 149 | |||
| 150 | 6855 | int addr = GET_ADDR(to_send); | |
| 151 | 6855 | bool tx = true; | |
| 152 | |||
| 153 | // Safety check for HCA_01 Heading Control Assist torque | ||
| 154 | // Signal: HCA_01.HCA_01_LM_Offset (absolute torque) | ||
| 155 | // Signal: HCA_01.HCA_01_LM_OffSign (direction) | ||
| 156 |
2/2✓ Branch 0 taken 4924 times.
✓ Branch 1 taken 1931 times.
|
6855 | if (addr == MSG_HCA_01) { |
| 157 | 4924 | int desired_torque = GET_BYTE(to_send, 2) | ((GET_BYTE(to_send, 3) & 0x1U) << 8); | |
| 158 | 4924 | bool sign = GET_BIT(to_send, 31U); | |
| 159 |
2/2✓ Branch 0 taken 2434 times.
✓ Branch 1 taken 2490 times.
|
4924 | if (sign) { |
| 160 | 2434 | desired_torque *= -1; | |
| 161 | } | ||
| 162 | |||
| 163 | 4924 | bool steer_req = GET_BIT(to_send, 30U); | |
| 164 | |||
| 165 |
2/2✓ Branch 1 taken 2750 times.
✓ Branch 2 taken 2174 times.
|
4924 | if (steer_torque_cmd_checks(desired_torque, steer_req, VOLKSWAGEN_MQB_STEERING_LIMITS)) { |
| 166 | 2750 | tx = false; | |
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | // Safety check for both ACC_06 and ACC_07 acceleration requests | ||
| 171 | // To avoid floating point math, scale upward and compare to pre-scaled safety m/s2 boundaries | ||
| 172 |
4/4✓ Branch 0 taken 6212 times.
✓ Branch 1 taken 643 times.
✓ Branch 2 taken 1277 times.
✓ Branch 3 taken 4935 times.
|
6855 | if ((addr == MSG_ACC_06) || (addr == MSG_ACC_07)) { |
| 173 | 1920 | bool violation = false; | |
| 174 | 1920 | int desired_accel = 0; | |
| 175 | |||
| 176 |
2/2✓ Branch 0 taken 643 times.
✓ Branch 1 taken 1277 times.
|
1920 | if (addr == MSG_ACC_06) { |
| 177 | // Signal: ACC_06.ACC_Sollbeschleunigung_02 (acceleration in m/s2, scale 0.005, offset -7.22) | ||
| 178 | 643 | desired_accel = ((((GET_BYTE(to_send, 4) & 0x7U) << 8) | GET_BYTE(to_send, 3)) * 5U) - 7220U; | |
| 179 | } else { | ||
| 180 | // Signal: ACC_07.ACC_Folgebeschl (acceleration in m/s2, scale 0.03, offset -4.6) | ||
| 181 | 1277 | int secondary_accel = (GET_BYTE(to_send, 4) * 30U) - 4600U; | |
| 182 | 1277 | violation |= (secondary_accel != 3020); // enforce always inactive (one increment above max range) at this time | |
| 183 | // Signal: ACC_07.ACC_Sollbeschleunigung_02 (acceleration in m/s2, scale 0.005, offset -7.22) | ||
| 184 | 1277 | desired_accel = (((GET_BYTE(to_send, 7) << 3) | ((GET_BYTE(to_send, 6) & 0xE0U) >> 5)) * 5U) - 7220U; | |
| 185 | } | ||
| 186 | |||
| 187 | 1920 | violation |= longitudinal_accel_checks(desired_accel, VOLKSWAGEN_MQB_LONG_LIMITS); | |
| 188 | |||
| 189 |
2/2✓ Branch 0 taken 1544 times.
✓ Branch 1 taken 376 times.
|
1920 | if (violation) { |
| 190 | 1544 | tx = false; | |
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | // FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off. | ||
| 195 | // This avoids unintended engagements while still allowing resume spam | ||
| 196 |
4/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6849 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
|
6855 | if ((addr == MSG_GRA_ACC_01) && !controls_allowed) { |
| 197 | // disallow resume and set: bits 16 and 19 | ||
| 198 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
|
5 | if ((GET_BYTE(to_send, 2) & 0x9U) != 0U) { |
| 199 | 2 | tx = false; | |
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | 6855 | return tx; | |
| 204 | } | ||
| 205 | |||
| 206 | 16896 | static int volkswagen_mqb_fwd_hook(int bus_num, int addr) { | |
| 207 | 16896 | int bus_fwd = -1; | |
| 208 | |||
| 209 |
3/3✓ Branch 0 taken 5632 times.
✓ Branch 1 taken 5632 times.
✓ Branch 2 taken 5632 times.
|
16896 | switch (bus_num) { |
| 210 | 5632 | case 0: | |
| 211 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5630 times.
|
5632 | if (addr == MSG_LH_EPS_03) { |
| 212 | // openpilot needs to replace apparent driver steering input torque to pacify VW Emergency Assist | ||
| 213 | 2 | bus_fwd = -1; | |
| 214 | } else { | ||
| 215 | // Forward all remaining traffic from Extended CAN onward | ||
| 216 | 5630 | bus_fwd = 2; | |
| 217 | } | ||
| 218 | 5632 | break; | |
| 219 | 5632 | case 2: | |
| 220 |
4/4✓ Branch 0 taken 5630 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 5628 times.
|
5632 | if ((addr == MSG_HCA_01) || (addr == MSG_LDW_02)) { |
| 221 | // openpilot takes over LKAS steering control and related HUD messages from the camera | ||
| 222 | 4 | bus_fwd = -1; | |
| 223 |
8/8✓ Branch 0 taken 2814 times.
✓ Branch 1 taken 2814 times.
✓ Branch 2 taken 2813 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2812 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 2811 times.
|
5628 | } else if (volkswagen_longitudinal && ((addr == MSG_ACC_02) || (addr == MSG_ACC_06) || (addr == MSG_ACC_07))) { |
| 224 | // openpilot takes over acceleration/braking control and related HUD messages from the stock ACC radar | ||
| 225 | 3 | bus_fwd = -1; | |
| 226 | } else { | ||
| 227 | // Forward all remaining traffic from Extended CAN devices to J533 gateway | ||
| 228 | 5625 | bus_fwd = 0; | |
| 229 | } | ||
| 230 | 5632 | break; | |
| 231 | 5632 | default: | |
| 232 | // No other buses should be in use; fallback to do-not-forward | ||
| 233 | 5632 | bus_fwd = -1; | |
| 234 | 5632 | break; | |
| 235 | } | ||
| 236 | |||
| 237 | 16896 | return bus_fwd; | |
| 238 | } | ||
| 239 | |||
| 240 | const safety_hooks volkswagen_mqb_hooks = { | ||
| 241 | .init = volkswagen_mqb_init, | ||
| 242 | .rx = volkswagen_mqb_rx_hook, | ||
| 243 | .tx = volkswagen_mqb_tx_hook, | ||
| 244 | .fwd = volkswagen_mqb_fwd_hook, | ||
| 245 | .get_counter = volkswagen_mqb_meb_get_counter, | ||
| 246 | .get_checksum = volkswagen_mqb_meb_get_checksum, | ||
| 247 | .compute_checksum = volkswagen_mqb_meb_compute_crc, | ||
| 248 | }; | ||
| 249 |