| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "safety_declarations.h" | ||
| 4 | |||
| 5 | // Safety-relevant CAN messages for Ford vehicles. | ||
| 6 | #define FORD_EngBrakeData 0x165 // RX from PCM, for driver brake pedal and cruise state | ||
| 7 | #define FORD_EngVehicleSpThrottle 0x204 // RX from PCM, for driver throttle input | ||
| 8 | #define FORD_DesiredTorqBrk 0x213 // RX from ABS, for standstill state | ||
| 9 | #define FORD_BrakeSysFeatures 0x415 // RX from ABS, for vehicle speed | ||
| 10 | #define FORD_EngVehicleSpThrottle2 0x202 // RX from PCM, for second vehicle speed | ||
| 11 | #define FORD_Yaw_Data_FD1 0x91 // RX from RCM, for yaw rate | ||
| 12 | #define FORD_Steering_Data_FD1 0x083 // TX by OP, various driver switches and LKAS/CC buttons | ||
| 13 | #define FORD_ACCDATA 0x186 // TX by OP, ACC controls | ||
| 14 | #define FORD_ACCDATA_3 0x18A // TX by OP, ACC/TJA user interface | ||
| 15 | #define FORD_Lane_Assist_Data1 0x3CA // TX by OP, Lane Keep Assist | ||
| 16 | #define FORD_LateralMotionControl 0x3D3 // TX by OP, Lateral Control message | ||
| 17 | #define FORD_LateralMotionControl2 0x3D6 // TX by OP, alternate Lateral Control message | ||
| 18 | #define FORD_IPMA_Data 0x3D8 // TX by OP, IPMA and LKAS user interface | ||
| 19 | |||
| 20 | // CAN bus numbers. | ||
| 21 | #define FORD_MAIN_BUS 0U | ||
| 22 | #define FORD_CAM_BUS 2U | ||
| 23 | |||
| 24 | 601038 | static uint8_t ford_get_counter(const CANPacket_t *to_push) { | |
| 25 | 601038 | int addr = GET_ADDR(to_push); | |
| 26 | |||
| 27 | 601038 | uint8_t cnt = 0; | |
| 28 |
2/2✓ Branch 0 taken 295929 times.
✓ Branch 1 taken 305109 times.
|
601038 | if (addr == FORD_BrakeSysFeatures) { |
| 29 | // Signal: VehVActlBrk_No_Cnt | ||
| 30 | 295929 | cnt = (GET_BYTE(to_push, 2) >> 2) & 0xFU; | |
| 31 |
1/2✓ Branch 0 taken 305109 times.
✗ Branch 1 not taken.
|
305109 | } else if (addr == FORD_Yaw_Data_FD1) { |
| 32 | // Signal: VehRollYaw_No_Cnt | ||
| 33 | 305109 | cnt = GET_BYTE(to_push, 5); | |
| 34 | } else { | ||
| 35 | } | ||
| 36 | 601038 | return cnt; | |
| 37 | } | ||
| 38 | |||
| 39 | 601038 | static uint32_t ford_get_checksum(const CANPacket_t *to_push) { | |
| 40 | 601038 | int addr = GET_ADDR(to_push); | |
| 41 | |||
| 42 | 601038 | uint8_t chksum = 0; | |
| 43 |
2/2✓ Branch 0 taken 295929 times.
✓ Branch 1 taken 305109 times.
|
601038 | if (addr == FORD_BrakeSysFeatures) { |
| 44 | // Signal: VehVActlBrk_No_Cs | ||
| 45 | 295929 | chksum = GET_BYTE(to_push, 3); | |
| 46 |
1/2✓ Branch 0 taken 305109 times.
✗ Branch 1 not taken.
|
305109 | } else if (addr == FORD_Yaw_Data_FD1) { |
| 47 | // Signal: VehRollYawW_No_Cs | ||
| 48 | 305109 | chksum = GET_BYTE(to_push, 4); | |
| 49 | } else { | ||
| 50 | } | ||
| 51 | 601038 | return chksum; | |
| 52 | } | ||
| 53 | |||
| 54 | 601038 | static uint32_t ford_compute_checksum(const CANPacket_t *to_push) { | |
| 55 | 601038 | int addr = GET_ADDR(to_push); | |
| 56 | |||
| 57 | 601038 | uint8_t chksum = 0; | |
| 58 |
2/2✓ Branch 0 taken 295929 times.
✓ Branch 1 taken 305109 times.
|
601038 | if (addr == FORD_BrakeSysFeatures) { |
| 59 | 295929 | chksum += GET_BYTE(to_push, 0) + GET_BYTE(to_push, 1); // Veh_V_ActlBrk | |
| 60 | 295929 | chksum += GET_BYTE(to_push, 2) >> 6; // VehVActlBrk_D_Qf | |
| 61 | 295929 | chksum += (GET_BYTE(to_push, 2) >> 2) & 0xFU; // VehVActlBrk_No_Cnt | |
| 62 | 295929 | chksum = 0xFFU - chksum; | |
| 63 |
1/2✓ Branch 0 taken 305109 times.
✗ Branch 1 not taken.
|
305109 | } else if (addr == FORD_Yaw_Data_FD1) { |
| 64 | 305109 | chksum += GET_BYTE(to_push, 0) + GET_BYTE(to_push, 1); // VehRol_W_Actl | |
| 65 | 305109 | chksum += GET_BYTE(to_push, 2) + GET_BYTE(to_push, 3); // VehYaw_W_Actl | |
| 66 | 305109 | chksum += GET_BYTE(to_push, 5); // VehRollYaw_No_Cnt | |
| 67 | 305109 | chksum += GET_BYTE(to_push, 6) >> 6; // VehRolWActl_D_Qf | |
| 68 | 305109 | chksum += (GET_BYTE(to_push, 6) >> 4) & 0x3U; // VehYawWActl_D_Qf | |
| 69 | 305109 | chksum = 0xFFU - chksum; | |
| 70 | } else { | ||
| 71 | } | ||
| 72 | |||
| 73 | 601038 | return chksum; | |
| 74 | } | ||
| 75 | |||
| 76 | 625107 | static bool ford_get_quality_flag_valid(const CANPacket_t *to_push) { | |
| 77 | 625107 | int addr = GET_ADDR(to_push); | |
| 78 | |||
| 79 | 625107 | bool valid = false; | |
| 80 |
2/2✓ Branch 0 taken 295929 times.
✓ Branch 1 taken 329178 times.
|
625107 | if (addr == FORD_BrakeSysFeatures) { |
| 81 | 295929 | valid = (GET_BYTE(to_push, 2) >> 6) == 0x3U; // VehVActlBrk_D_Qf | |
| 82 |
2/2✓ Branch 0 taken 24069 times.
✓ Branch 1 taken 305109 times.
|
329178 | } else if (addr == FORD_EngVehicleSpThrottle2) { |
| 83 | 24069 | valid = ((GET_BYTE(to_push, 4) >> 5) & 0x3U) == 0x3U; // VehVActlEng_D_Qf | |
| 84 |
1/2✓ Branch 0 taken 305109 times.
✗ Branch 1 not taken.
|
305109 | } else if (addr == FORD_Yaw_Data_FD1) { |
| 85 | 305109 | valid = ((GET_BYTE(to_push, 6) >> 4) & 0x3U) == 0x3U; // VehYawWActl_D_Qf | |
| 86 | } else { | ||
| 87 | } | ||
| 88 | 625107 | return valid; | |
| 89 | } | ||
| 90 | |||
| 91 | static bool ford_longitudinal = false; | ||
| 92 | |||
| 93 | #define FORD_INACTIVE_CURVATURE 1000U | ||
| 94 | #define FORD_INACTIVE_CURVATURE_RATE 4096U | ||
| 95 | #define FORD_INACTIVE_PATH_OFFSET 512U | ||
| 96 | #define FORD_INACTIVE_PATH_ANGLE 1000U | ||
| 97 | |||
| 98 | #define FORD_CANFD_INACTIVE_CURVATURE_RATE 1024U | ||
| 99 | |||
| 100 | #define FORD_MAX_SPEED_DELTA 2.0 // m/s | ||
| 101 | |||
| 102 | 642006 | static bool ford_lkas_msg_check(int addr) { | |
| 103 | return (addr == FORD_ACCDATA_3) | ||
| 104 |
2/2✓ Branch 0 taken 641994 times.
✓ Branch 1 taken 6 times.
|
642000 | || (addr == FORD_Lane_Assist_Data1) |
| 105 |
2/2✓ Branch 0 taken 641988 times.
✓ Branch 1 taken 6 times.
|
641994 | || (addr == FORD_LateralMotionControl) |
| 106 |
2/2✓ Branch 0 taken 641982 times.
✓ Branch 1 taken 6 times.
|
641988 | || (addr == FORD_LateralMotionControl2) |
| 107 |
4/4✓ Branch 0 taken 642000 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 641976 times.
|
1284006 | || (addr == FORD_IPMA_Data); |
| 108 | } | ||
| 109 | |||
| 110 | // Curvature rate limits | ||
| 111 | static const SteeringLimits FORD_STEERING_LIMITS = { | ||
| 112 | .max_steer = 1000, | ||
| 113 | .angle_deg_to_can = 50000, // 1 / (2e-5) rad to can | ||
| 114 | .max_angle_error = 100, // 0.002 * FORD_STEERING_LIMITS.angle_deg_to_can | ||
| 115 | .angle_rate_up_lookup = { | ||
| 116 | {5., 25., 25.}, | ||
| 117 | {0.00045, 0.0001, 0.0001} | ||
| 118 | }, | ||
| 119 | .angle_rate_down_lookup = { | ||
| 120 | {5., 25., 25.}, | ||
| 121 | {0.00045, 0.00015, 0.00015} | ||
| 122 | }, | ||
| 123 | |||
| 124 | // no blending at low speed due to lack of torque wind-up and inaccurate current curvature | ||
| 125 | .angle_error_min_speed = 10.0, // m/s | ||
| 126 | |||
| 127 | .enforce_angle_error = true, | ||
| 128 | .inactive_angle_is_zero = true, | ||
| 129 | }; | ||
| 130 | |||
| 131 | 650454 | static void ford_rx_hook(const CANPacket_t *to_push) { | |
| 132 |
2/2✓ Branch 0 taken 633558 times.
✓ Branch 1 taken 16896 times.
|
650454 | if (GET_BUS(to_push) == FORD_MAIN_BUS) { |
| 133 | 633558 | int addr = GET_ADDR(to_push); | |
| 134 | |||
| 135 | // Update in motion state from standstill signal | ||
| 136 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 633534 times.
|
633558 | if (addr == FORD_DesiredTorqBrk) { |
| 137 | // Signal: VehStop_D_Stat | ||
| 138 | 24 | vehicle_moving = ((GET_BYTE(to_push, 3) >> 3) & 0x3U) != 1U; | |
| 139 | } | ||
| 140 | |||
| 141 | // Update vehicle speed | ||
| 142 |
2/2✓ Branch 0 taken 295890 times.
✓ Branch 1 taken 337668 times.
|
633558 | if (addr == FORD_BrakeSysFeatures) { |
| 143 | // Signal: Veh_V_ActlBrk | ||
| 144 | 295890 | UPDATE_VEHICLE_SPEED(((GET_BYTE(to_push, 0) << 8) | GET_BYTE(to_push, 1)) * 0.01 / 3.6); | |
| 145 | } | ||
| 146 | |||
| 147 | // Check vehicle speed against a second source | ||
| 148 |
2/2✓ Branch 0 taken 24033 times.
✓ Branch 1 taken 609525 times.
|
633558 | if (addr == FORD_EngVehicleSpThrottle2) { |
| 149 | // Disable controls if speeds from ABS and PCM ECUs are too far apart. | ||
| 150 | // Signal: Veh_V_ActlEng | ||
| 151 | 24033 | float filtered_pcm_speed = ((GET_BYTE(to_push, 6) << 8) | GET_BYTE(to_push, 7)) * 0.01 / 3.6; | |
| 152 |
2/2✓ Branch 0 taken 11760 times.
✓ Branch 1 taken 12273 times.
|
24033 | bool is_invalid_speed = ABS(filtered_pcm_speed - ((float)vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR)) > FORD_MAX_SPEED_DELTA; |
| 153 |
2/2✓ Branch 0 taken 13710 times.
✓ Branch 1 taken 10323 times.
|
24033 | if (is_invalid_speed) { |
| 154 | 13710 | controls_allowed = false; | |
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | // Update vehicle yaw rate | ||
| 159 |
2/2✓ Branch 0 taken 305070 times.
✓ Branch 1 taken 328488 times.
|
633558 | if (addr == FORD_Yaw_Data_FD1) { |
| 160 | // Signal: VehYaw_W_Actl | ||
| 161 | 305070 | float ford_yaw_rate = (((GET_BYTE(to_push, 2) << 8U) | GET_BYTE(to_push, 3)) * 0.0002) - 6.5; | |
| 162 |
2/2✓ Branch 0 taken 304968 times.
✓ Branch 1 taken 102 times.
|
305070 | float current_curvature = ford_yaw_rate / MAX(vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR, 0.1); |
| 163 | // convert current curvature into units on CAN for comparison with desired curvature | ||
| 164 | 305070 | update_sample(&angle_meas, ROUND(current_curvature * FORD_STEERING_LIMITS.angle_deg_to_can)); | |
| 165 | } | ||
| 166 | |||
| 167 | // Update gas pedal | ||
| 168 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 633525 times.
|
633558 | if (addr == FORD_EngVehicleSpThrottle) { |
| 169 | // Pedal position: (0.1 * val) in percent | ||
| 170 | // Signal: ApedPos_Pc_ActlArb | ||
| 171 | 33 | gas_pressed = (((GET_BYTE(to_push, 0) & 0x03U) << 8) | GET_BYTE(to_push, 1)) > 0U; | |
| 172 | } | ||
| 173 | |||
| 174 | // Update brake pedal and cruise state | ||
| 175 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 633480 times.
|
633558 | if (addr == FORD_EngBrakeData) { |
| 176 | // Signal: BpedDrvAppl_D_Actl | ||
| 177 | 78 | brake_pressed = ((GET_BYTE(to_push, 0) >> 4) & 0x3U) == 2U; | |
| 178 | |||
| 179 | // Signal: CcStat_D_Actl | ||
| 180 | 78 | unsigned int cruise_state = GET_BYTE(to_push, 1) & 0x07U; | |
| 181 |
3/4✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 48 times.
|
78 | bool cruise_engaged = (cruise_state == 4U) || (cruise_state == 5U); |
| 182 | 78 | pcm_cruise_check(cruise_engaged); | |
| 183 | } | ||
| 184 | |||
| 185 | // If steering controls messages are received on the destination bus, it's an indication | ||
| 186 | // that the relay might be malfunctioning. | ||
| 187 | 633558 | bool stock_ecu_detected = ford_lkas_msg_check(addr); | |
| 188 |
2/2✓ Branch 0 taken 422372 times.
✓ Branch 1 taken 211186 times.
|
633558 | if (ford_longitudinal) { |
| 189 |
4/4✓ Branch 0 taken 422362 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 422360 times.
|
422372 | stock_ecu_detected = stock_ecu_detected || (addr == FORD_ACCDATA); |
| 190 | } | ||
| 191 | 633558 | generic_rx_checks(stock_ecu_detected); | |
| 192 | } | ||
| 193 | |||
| 194 | 650454 | } | |
| 195 | |||
| 196 | 50482 | static bool ford_tx_hook(const CANPacket_t *to_send) { | |
| 197 | const LongitudinalLimits FORD_LONG_LIMITS = { | ||
| 198 | // acceleration cmd limits (used for brakes) | ||
| 199 | // Signal: AccBrkTot_A_Rq | ||
| 200 | .max_accel = 5641, // 1.9999 m/s^s | ||
| 201 | .min_accel = 4231, // -3.4991 m/s^2 | ||
| 202 | .inactive_accel = 5128, // -0.0008 m/s^2 | ||
| 203 | |||
| 204 | // gas cmd limits | ||
| 205 | // Signal: AccPrpl_A_Rq & AccPrpl_A_Pred | ||
| 206 | .max_gas = 700, // 2.0 m/s^2 | ||
| 207 | .min_gas = 450, // -0.5 m/s^2 | ||
| 208 | .inactive_gas = 0, // -5.0 m/s^2 | ||
| 209 | }; | ||
| 210 | |||
| 211 | 50482 | bool tx = true; | |
| 212 | |||
| 213 | 50482 | int addr = GET_ADDR(to_send); | |
| 214 | |||
| 215 | // Safety check for ACCDATA accel and brake requests | ||
| 216 |
2/2✓ Branch 0 taken 2062 times.
✓ Branch 1 taken 48420 times.
|
50482 | if (addr == FORD_ACCDATA) { |
| 217 | // Signal: AccPrpl_A_Rq | ||
| 218 | 2062 | int gas = ((GET_BYTE(to_send, 6) & 0x3U) << 8) | GET_BYTE(to_send, 7); | |
| 219 | // Signal: AccPrpl_A_Pred | ||
| 220 | 2062 | int gas_pred = ((GET_BYTE(to_send, 2) & 0x3U) << 8) | GET_BYTE(to_send, 3); | |
| 221 | // Signal: AccBrkTot_A_Rq | ||
| 222 | 2062 | int accel = ((GET_BYTE(to_send, 0) & 0x1FU) << 8) | GET_BYTE(to_send, 1); | |
| 223 | // Signal: CmbbDeny_B_Actl | ||
| 224 | 2062 | bool cmbb_deny = GET_BIT(to_send, 37U); | |
| 225 | |||
| 226 | // Signal: AccBrkPrchg_B_Rq & AccBrkDecel_B_Rq | ||
| 227 |
3/4✓ Branch 0 taken 1032 times.
✓ Branch 1 taken 1030 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1032 times.
|
2062 | bool brake_actuation = GET_BIT(to_send, 54U) || GET_BIT(to_send, 55U); |
| 228 | |||
| 229 | 2062 | bool violation = false; | |
| 230 | 2062 | violation |= longitudinal_accel_checks(accel, FORD_LONG_LIMITS); | |
| 231 | 2062 | violation |= longitudinal_gas_checks(gas, FORD_LONG_LIMITS); | |
| 232 | 2062 | violation |= longitudinal_gas_checks(gas_pred, FORD_LONG_LIMITS); | |
| 233 | |||
| 234 | // Safety check for stock AEB | ||
| 235 | 2062 | violation |= cmbb_deny; // do not prevent stock AEB actuation | |
| 236 | |||
| 237 |
4/4✓ Branch 1 taken 1032 times.
✓ Branch 2 taken 1030 times.
✓ Branch 3 taken 380 times.
✓ Branch 4 taken 652 times.
|
2062 | violation |= !get_longitudinal_allowed() && brake_actuation; |
| 238 | |||
| 239 |
2/2✓ Branch 0 taken 1504 times.
✓ Branch 1 taken 558 times.
|
2062 | if (violation) { |
| 240 | 1504 | tx = false; | |
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | // Safety check for Steering_Data_FD1 button signals | ||
| 245 | // Note: Many other signals in this message are not relevant to safety (e.g. blinkers, wiper switches, high beam) | ||
| 246 | // which we passthru in OP. | ||
| 247 |
2/2✓ Branch 0 taken 42 times.
✓ Branch 1 taken 50440 times.
|
50482 | if (addr == FORD_Steering_Data_FD1) { |
| 248 | // Violation if resume button is pressed while controls not allowed, or | ||
| 249 | // if cancel button is pressed when cruise isn't engaged. | ||
| 250 | 42 | bool violation = false; | |
| 251 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
|
42 | violation |= GET_BIT(to_send, 8U) && !cruise_engaged_prev; // Signal: CcAslButtnCnclPress (cancel) |
| 252 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
|
42 | violation |= GET_BIT(to_send, 25U) && !controls_allowed; // Signal: CcAsllButtnResPress (resume) |
| 253 | |||
| 254 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 30 times.
|
42 | if (violation) { |
| 255 | 12 | tx = false; | |
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | // Safety check for Lane_Assist_Data1 action | ||
| 260 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 50473 times.
|
50482 | if (addr == FORD_Lane_Assist_Data1) { |
| 261 | // Do not allow steering using Lane_Assist_Data1 (Lane-Departure Aid). | ||
| 262 | // This message must be sent for Lane Centering to work, and can include | ||
| 263 | // values such as the steering angle or lane curvature for debugging, | ||
| 264 | // but the action (LkaActvStats_D2_Req) must be set to zero. | ||
| 265 | 9 | unsigned int action = GET_BYTE(to_send, 0) >> 5; | |
| 266 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
|
9 | if (action != 0U) { |
| 267 | 6 | tx = false; | |
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | // Safety check for LateralMotionControl action | ||
| 272 |
2/2✓ Branch 0 taken 16121 times.
✓ Branch 1 taken 34361 times.
|
50482 | if (addr == FORD_LateralMotionControl) { |
| 273 | // Signal: LatCtl_D_Rq | ||
| 274 | 16121 | bool steer_control_enabled = ((GET_BYTE(to_send, 4) >> 2) & 0x7U) != 0U; | |
| 275 | 16121 | unsigned int raw_curvature = (GET_BYTE(to_send, 0) << 3) | (GET_BYTE(to_send, 1) >> 5); | |
| 276 | 16121 | unsigned int raw_curvature_rate = ((GET_BYTE(to_send, 1) & 0x1FU) << 8) | GET_BYTE(to_send, 2); | |
| 277 | 16121 | unsigned int raw_path_angle = (GET_BYTE(to_send, 3) << 3) | (GET_BYTE(to_send, 4) >> 5); | |
| 278 | 16121 | unsigned int raw_path_offset = (GET_BYTE(to_send, 5) << 2) | (GET_BYTE(to_send, 6) >> 6); | |
| 279 | |||
| 280 | // These signals are not yet tested with the current safety limits | ||
| 281 |
6/6✓ Branch 0 taken 6440 times.
✓ Branch 1 taken 9681 times.
✓ Branch 2 taken 2040 times.
✓ Branch 3 taken 4400 times.
✓ Branch 4 taken 400 times.
✓ Branch 5 taken 1640 times.
|
16121 | bool violation = (raw_curvature_rate != FORD_INACTIVE_CURVATURE_RATE) || (raw_path_angle != FORD_INACTIVE_PATH_ANGLE) || (raw_path_offset != FORD_INACTIVE_PATH_OFFSET); |
| 282 | |||
| 283 | // Check angle error and steer_control_enabled | ||
| 284 | 16121 | int desired_curvature = raw_curvature - FORD_INACTIVE_CURVATURE; // /FORD_STEERING_LIMITS.angle_deg_to_can to get real curvature | |
| 285 | 16121 | violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled, FORD_STEERING_LIMITS); | |
| 286 | |||
| 287 |
2/2✓ Branch 0 taken 15299 times.
✓ Branch 1 taken 822 times.
|
16121 | if (violation) { |
| 288 | 15299 | tx = false; | |
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | // Safety check for LateralMotionControl2 action | ||
| 293 |
2/2✓ Branch 0 taken 32242 times.
✓ Branch 1 taken 18240 times.
|
50482 | if (addr == FORD_LateralMotionControl2) { |
| 294 | // Signal: LatCtl_D2_Rq | ||
| 295 | 32242 | bool steer_control_enabled = ((GET_BYTE(to_send, 0) >> 4) & 0x7U) != 0U; | |
| 296 | 32242 | unsigned int raw_curvature = (GET_BYTE(to_send, 2) << 3) | (GET_BYTE(to_send, 3) >> 5); | |
| 297 | 32242 | unsigned int raw_curvature_rate = (GET_BYTE(to_send, 6) << 3) | (GET_BYTE(to_send, 7) >> 5); | |
| 298 | 32242 | unsigned int raw_path_angle = ((GET_BYTE(to_send, 3) & 0x1FU) << 6) | (GET_BYTE(to_send, 4) >> 2); | |
| 299 | 32242 | unsigned int raw_path_offset = ((GET_BYTE(to_send, 4) & 0x3U) << 8) | GET_BYTE(to_send, 5); | |
| 300 | |||
| 301 | // These signals are not yet tested with the current safety limits | ||
| 302 |
6/6✓ Branch 0 taken 12880 times.
✓ Branch 1 taken 19362 times.
✓ Branch 2 taken 4080 times.
✓ Branch 3 taken 8800 times.
✓ Branch 4 taken 800 times.
✓ Branch 5 taken 3280 times.
|
32242 | bool violation = (raw_curvature_rate != FORD_CANFD_INACTIVE_CURVATURE_RATE) || (raw_path_angle != FORD_INACTIVE_PATH_ANGLE) || (raw_path_offset != FORD_INACTIVE_PATH_OFFSET); |
| 303 | |||
| 304 | // Check angle error and steer_control_enabled | ||
| 305 | 32242 | int desired_curvature = raw_curvature - FORD_INACTIVE_CURVATURE; // /FORD_STEERING_LIMITS.angle_deg_to_can to get real curvature | |
| 306 | 32242 | violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled, FORD_STEERING_LIMITS); | |
| 307 | |||
| 308 |
2/2✓ Branch 0 taken 30598 times.
✓ Branch 1 taken 1644 times.
|
32242 | if (violation) { |
| 309 | 30598 | tx = false; | |
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | 50482 | return tx; | |
| 314 | } | ||
| 315 | |||
| 316 | 25344 | static int ford_fwd_hook(int bus_num, int addr) { | |
| 317 | 25344 | int bus_fwd = -1; | |
| 318 | |||
| 319 |
3/3✓ Branch 0 taken 8448 times.
✓ Branch 1 taken 8448 times.
✓ Branch 2 taken 8448 times.
|
25344 | switch (bus_num) { |
| 320 | 8448 | case FORD_MAIN_BUS: { | |
| 321 | // Forward all traffic from bus 0 onward | ||
| 322 | 8448 | bus_fwd = FORD_CAM_BUS; | |
| 323 | 8448 | break; | |
| 324 | } | ||
| 325 | 8448 | case FORD_CAM_BUS: { | |
| 326 |
2/2✓ Branch 1 taken 15 times.
✓ Branch 2 taken 8433 times.
|
8448 | if (ford_lkas_msg_check(addr)) { |
| 327 | // Block stock LKAS and UI messages | ||
| 328 | 15 | bus_fwd = -1; | |
| 329 |
4/4✓ Branch 0 taken 5622 times.
✓ Branch 1 taken 2811 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 5620 times.
|
8433 | } else if (ford_longitudinal && (addr == FORD_ACCDATA)) { |
| 330 | // Block stock ACC message | ||
| 331 | 2 | bus_fwd = -1; | |
| 332 | } else { | ||
| 333 | // Forward remaining traffic | ||
| 334 | 8431 | bus_fwd = FORD_MAIN_BUS; | |
| 335 | } | ||
| 336 | 8448 | break; | |
| 337 | } | ||
| 338 | 8448 | default: { | |
| 339 | // No other buses should be in use; fallback to do-not-forward | ||
| 340 | 8448 | bus_fwd = -1; | |
| 341 | 8448 | break; | |
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | 25344 | return bus_fwd; | |
| 346 | } | ||
| 347 | |||
| 348 | 96 | static safety_config ford_init(uint16_t param) { | |
| 349 | 96 | bool ford_canfd = false; | |
| 350 | |||
| 351 | // warning: quality flags are not yet checked in openpilot's CAN parser, | ||
| 352 | // this may be the cause of blocked messages | ||
| 353 | static RxCheck ford_rx_checks[] = { | ||
| 354 | {.msg = {{FORD_BrakeSysFeatures, 0, 8, .check_checksum = true, .max_counter = 15U, .quality_flag=true, .frequency = 50U}, { 0 }, { 0 }}}, | ||
| 355 | // FORD_EngVehicleSpThrottle2 has a counter that either randomly skips or by 2, likely ECU bug | ||
| 356 | // Some hybrid models also experience a bug where this checksum mismatches for one or two frames under heavy acceleration with ACC | ||
| 357 | // It has been confirmed that the Bronco Sport's camera only disallows ACC for bad quality flags, not counters or checksums, so we match that | ||
| 358 | {.msg = {{FORD_EngVehicleSpThrottle2, 0, 8, .check_checksum = false, .quality_flag=true, .frequency = 50U}, { 0 }, { 0 }}}, | ||
| 359 | {.msg = {{FORD_Yaw_Data_FD1, 0, 8, .check_checksum = true, .max_counter = 255U, .quality_flag=true, .frequency = 100U}, { 0 }, { 0 }}}, | ||
| 360 | // These messages have no counter or checksum | ||
| 361 | {.msg = {{FORD_EngBrakeData, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, | ||
| 362 | {.msg = {{FORD_EngVehicleSpThrottle, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, | ||
| 363 | {.msg = {{FORD_DesiredTorqBrk, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, | ||
| 364 | }; | ||
| 365 | |||
| 366 | #define FORD_COMMON_TX_MSGS \ | ||
| 367 | {FORD_Steering_Data_FD1, 0, 8}, \ | ||
| 368 | {FORD_Steering_Data_FD1, 2, 8}, \ | ||
| 369 | {FORD_ACCDATA_3, 0, 8}, \ | ||
| 370 | {FORD_Lane_Assist_Data1, 0, 8}, \ | ||
| 371 | {FORD_IPMA_Data, 0, 8}, \ | ||
| 372 | |||
| 373 | static const CanMsg FORD_CANFD_LONG_TX_MSGS[] = { | ||
| 374 | FORD_COMMON_TX_MSGS | ||
| 375 | {FORD_ACCDATA, 0, 8}, | ||
| 376 | {FORD_LateralMotionControl2, 0, 8}, | ||
| 377 | }; | ||
| 378 | |||
| 379 | static const CanMsg FORD_CANFD_STOCK_TX_MSGS[] = { | ||
| 380 | FORD_COMMON_TX_MSGS | ||
| 381 | {FORD_LateralMotionControl2, 0, 8}, | ||
| 382 | }; | ||
| 383 | |||
| 384 | static const CanMsg FORD_STOCK_TX_MSGS[] = { | ||
| 385 | FORD_COMMON_TX_MSGS | ||
| 386 | {FORD_LateralMotionControl, 0, 8}, | ||
| 387 | }; | ||
| 388 | |||
| 389 | static const CanMsg FORD_LONG_TX_MSGS[] = { | ||
| 390 | FORD_COMMON_TX_MSGS | ||
| 391 | {FORD_ACCDATA, 0, 8}, | ||
| 392 | {FORD_LateralMotionControl, 0, 8}, | ||
| 393 | }; | ||
| 394 | |||
| 395 | UNUSED(param); | ||
| 396 | #ifdef ALLOW_DEBUG | ||
| 397 | 96 | const uint16_t FORD_PARAM_LONGITUDINAL = 1; | |
| 398 | 96 | const uint16_t FORD_PARAM_CANFD = 2; | |
| 399 | 96 | ford_longitudinal = GET_FLAG(param, FORD_PARAM_LONGITUDINAL); | |
| 400 | 96 | ford_canfd = GET_FLAG(param, FORD_PARAM_CANFD); | |
| 401 | #endif | ||
| 402 | |||
| 403 | // Longitudinal is the default for CAN, and optional for CAN FD w/ ALLOW_DEBUG | ||
| 404 |
4/4✓ Branch 0 taken 63 times.
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 30 times.
|
96 | ford_longitudinal = !ford_canfd || ford_longitudinal; |
| 405 | |||
| 406 | safety_config ret; | ||
| 407 | // FIXME: cppcheck thinks that ford_canfd is always false. This is not true | ||
| 408 | // if ALLOW_DEBUG is defined but cppcheck is run without ALLOW_DEBUG | ||
| 409 | // cppcheck-suppress knownConditionTrueFalse | ||
| 410 |
2/2✓ Branch 0 taken 63 times.
✓ Branch 1 taken 33 times.
|
96 | if (ford_canfd) { |
| 411 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 30 times.
|
63 | ret = ford_longitudinal ? BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_LONG_TX_MSGS) : \ |
| 412 | BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_STOCK_TX_MSGS); | ||
| 413 | } else { | ||
| 414 | // cppcheck-suppress knownConditionTrueFalse | ||
| 415 |
1/2✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
|
33 | ret = ford_longitudinal ? BUILD_SAFETY_CFG(ford_rx_checks, FORD_LONG_TX_MSGS) : \ |
| 416 | BUILD_SAFETY_CFG(ford_rx_checks, FORD_STOCK_TX_MSGS); | ||
| 417 | } | ||
| 418 | 96 | return ret; | |
| 419 | } | ||
| 420 | |||
| 421 | const safety_hooks ford_hooks = { | ||
| 422 | .init = ford_init, | ||
| 423 | .rx = ford_rx_hook, | ||
| 424 | .tx = ford_tx_hook, | ||
| 425 | .fwd = ford_fwd_hook, | ||
| 426 | .get_counter = ford_get_counter, | ||
| 427 | .get_checksum = ford_get_checksum, | ||
| 428 | .compute_checksum = ford_compute_checksum, | ||
| 429 | .get_quality_flag_valid = ford_get_quality_flag_valid, | ||
| 430 | }; | ||
| 431 |