text: const int VOLKSWAGEN_MQB_MAX_STEER = 300;               // 3.0 Nm (EPS side max of 3.0Nm with fault if violated)
  const int VOLKSWAGEN_MQB_MAX_RT_DELTA = 75;             // 4 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 50 ; 50 * 1.5 for safety pad = 75
  const uint32_t VOLKSWAGEN_MQB_RT_INTERVAL = 250000;     // 250ms between real time checks
  const int VOLKSWAGEN_MQB_MAX_RATE_UP = 4;               // 2.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s)
  const int VOLKSWAGEN_MQB_MAX_RATE_DOWN = 10;            // 5.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s)
  const int VOLKSWAGEN_MQB_DRIVER_TORQUE_ALLOWANCE = 80;
  const int VOLKSWAGEN_MQB_DRIVER_TORQUE_FACTOR = 3;
  
  #define MSG_ESP_19      0x0B2   // RX from ABS, for wheel speeds
  #define MSG_LH_EPS_03   0x09F   // RX from EPS, for driver steering torque
  #define MSG_ESP_05      0x106   // RX from ABS, for brake switch state
  #define MSG_TSK_06      0x120   // RX from ECU, for ACC status from drivetrain coordinator
  #define MSG_MOTOR_20    0x121   // RX from ECU, for driver throttle input
  #define MSG_HCA_01      0x126   // TX by OP, Heading Control Assist steering torque
  #define MSG_GRA_ACC_01  0x12B   // TX by OP, ACC control buttons for cancel/resume
  #define MSG_LDW_02      0x397   // TX by OP, Lane line recognition and text alerts
  
  // Transmit of GRA_ACC_01 is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration
  const CanMsg VOLKSWAGEN_MQB_TX_MSGS[] = {{MSG_HCA_01, 0, 8}, {MSG_GRA_ACC_01, 0, 8}, {MSG_GRA_ACC_01, 2, 8}, {MSG_LDW_02, 0, 8}};
  #define VOLKSWAGEN_MQB_TX_MSGS_LEN (sizeof(VOLKSWAGEN_MQB_TX_MSGS) / sizeof(VOLKSWAGEN_MQB_TX_MSGS[0]))
  
  AddrCheckStruct volkswagen_mqb_addr_checks[] = {
    {.msg = {{MSG_ESP_19, 0, 8, .check_checksum = false, .max_counter = 0U,  .expected_timestep = 10000U}, { 0 }, { 0 }}},
    {.msg = {{MSG_LH_EPS_03, 0, 8, .check_checksum = true,  .max_counter = 15U, .expected_timestep = 10000U}, { 0 }, { 0 }}},
    {.msg = {{MSG_ESP_05, 0, 8, .check_checksum = true,  .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}},
    {.msg = {{MSG_TSK_06, 0, 8, .check_checksum = true,  .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}},
    {.msg = {{MSG_MOTOR_20, 0, 8, .check_checksum = true,  .max_counter = 15U, .expected_timestep = 20000U}, { 0 }, { 0 }}},
  };
  #define VOLKSWAGEN_MQB_ADDR_CHECKS_LEN (sizeof(volkswagen_mqb_addr_checks) / sizeof(volkswagen_mqb_addr_checks[0]))
  addr_checks volkswagen_mqb_rx_checks = {volkswagen_mqb_addr_checks, VOLKSWAGEN_MQB_ADDR_CHECKS_LEN};
  
  uint8_t volkswagen_crc8_lut_8h2f[256]; // Static lookup table for CRC8 poly 0x2F, aka 8H2F/AUTOSAR
  
  
  static uint32_t volkswagen_mqb_get_checksum(CANPacket_t *to_push) {
    return (uint8_t)GET_BYTE(to_push, 0);
  }
  
  static uint8_t volkswagen_mqb_get_counter(CANPacket_t *to_push) {
    // MQB message counters are consistently found at LSB 8.
    return (uint8_t)GET_BYTE(to_push, 1) & 0xFU;
  }
  
  static uint32_t volkswagen_mqb_compute_crc(CANPacket_t *to_push) {
    int addr = GET_ADDR(to_push);
    int len = GET_LEN(to_push);
  
    // This is CRC-8H2F/AUTOSAR with a twist. See the OpenDBC implementation
    // of this algorithm for a version with explanatory comments.
  
    uint8_t crc = 0xFFU;
    for (int i = 1; i < len; i++) {
      crc ^= (uint8_t)GET_BYTE(to_push, i);
      crc = volkswagen_crc8_lut_8h2f[crc];
    }
  
    uint8_t counter = volkswagen_mqb_get_counter(to_push);
    switch(addr) {
      case MSG_LH_EPS_03:
        crc ^= (uint8_t[]){0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5}[counter];
        break;
      case MSG_ESP_05:
        crc ^= (uint8_t[]){0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}[counter];
        break;
      case MSG_TSK_06:
        crc ^= (uint8_t[]){0xC4,0xE2,0x4F,0xE4,0xF8,0x2F,0x56,0x81,0x9F,0xE5,0x83,0x44,0x05,0x3F,0x97,0xDF}[counter];
        break;
      case MSG_MOTOR_20:
        crc ^= (uint8_t[]){0xE9,0x65,0xAE,0x6B,0x7B,0x35,0xE5,0x5F,0x4E,0xC7,0x86,0xA2,0xBB,0xDD,0xEB,0xB4}[counter];
        break;
      default: // Undefined CAN message, CRC check expected to fail
        break;
    }
    crc = volkswagen_crc8_lut_8h2f[crc];
  
    return (uint8_t)(crc ^ 0xFFU);
  }
  
  static const addr_checks* volkswagen_mqb_init(uint16_t param) {
    UNUSED(param);
  
    controls_allowed = false;
    relay_malfunction_reset();
    gen_crc_lookup_table(0x2F, volkswagen_crc8_lut_8h2f);
    return &volkswagen_mqb_rx_checks;
  }
  
  static int volkswagen_mqb_rx_hook(CANPacket_t *to_push) {
  
    bool valid = addr_safety_check(to_push, &volkswagen_mqb_rx_checks,
                                   volkswagen_mqb_get_checksum, volkswagen_mqb_compute_crc, volkswagen_mqb_get_counter);
  
    if (valid && (GET_BUS(to_push) == 0U)) {
      int addr = GET_ADDR(to_push);
  
      // Update in-motion state by sampling front wheel speeds
      // Signal: ESP_19.ESP_VL_Radgeschw_02 (front left) in scaled km/h
      // Signal: ESP_19.ESP_VR_Radgeschw_02 (front right) in scaled km/h
      if (addr == MSG_ESP_19) {
        int wheel_speed_fl = GET_BYTE(to_push, 4) | (GET_BYTE(to_push, 5) << 8);
        int wheel_speed_fr = GET_BYTE(to_push, 6) | (GET_BYTE(to_push, 7) << 8);
        // Check for average front speed in excess of 0.3m/s, 1.08km/h
        // DBC speed scale 0.0075: 0.3m/s = 144, sum both wheels to compare
        vehicle_moving = (wheel_speed_fl + wheel_speed_fr) > 288;
      }
  
      // Update driver input torque samples
      // Signal: LH_EPS_03.EPS_Lenkmoment (absolute torque)
      // Signal: LH_EPS_03.EPS_VZ_Lenkmoment (direction)
      if (addr == MSG_LH_EPS_03) {
        int torque_driver_new = GET_BYTE(to_push, 5) | ((GET_BYTE(to_push, 6) & 0x1FU) << 8);
        int sign = (GET_BYTE(to_push, 6) & 0x80U) >> 7;
        if (sign == 1) {
          torque_driver_new *= -1;
        }
        update_sample(&torque_driver, torque_driver_new);
      }
  
      // Enter controls on rising edge of stock ACC, exit controls if stock ACC disengages
      // Signal: TSK_06.TSK_Status
      if (addr == MSG_TSK_06) {
        int acc_status = (GET_BYTE(to_push, 3) & 0x7U);
        int cruise_engaged = ((acc_status == 3) || (acc_status == 4) || (acc_status == 5)) ? 1 : 0;
        if (cruise_engaged && !cruise_engaged_prev) {
          controls_allowed = 1;
        }
        if (!cruise_engaged) {
          controls_allowed = 0;
        }
        cruise_engaged_prev = cruise_engaged;
      }
  
      // Signal: Motor_20.MO_Fahrpedalrohwert_01
      if (addr == MSG_MOTOR_20) {
        gas_pressed = ((GET_BYTES_04(to_push) >> 12) & 0xFFU) != 0U;
      }
  
      // Signal: ESP_05.ESP_Fahrer_bremst
      if (addr == MSG_ESP_05) {
        brake_pressed = (GET_BYTE(to_push, 3) & 0x4U) >> 2;
      }
  
      generic_rx_checks((addr == MSG_HCA_01));
    }
    return valid;
  }
  
  static int volkswagen_mqb_tx_hook(CANPacket_t *to_send, bool longitudinal_allowed) {
    UNUSED(longitudinal_allowed);
  
    int addr = GET_ADDR(to_send);
    int tx = 1;
  
    if (!msg_allowed(to_send, VOLKSWAGEN_MQB_TX_MSGS, VOLKSWAGEN_MQB_TX_MSGS_LEN)) {
      tx = 0;
    }
  
    // Safety check for HCA_01 Heading Control Assist torque
    // Signal: HCA_01.Assist_Torque (absolute torque)
    // Signal: HCA_01.Assist_VZ (direction)
    if (addr == MSG_HCA_01) {
      int desired_torque = GET_BYTE(to_send, 2) | ((GET_BYTE(to_send, 3) & 0x3FU) << 8);
      int sign = (GET_BYTE(to_send, 3) & 0x80U) >> 7;
      if (sign == 1) {
        desired_torque *= -1;
      }
  
      bool violation = false;
      uint32_t ts = microsecond_timer_get();
  
      if (controls_allowed) {
        // *** global torque limit check ***
        violation |= max_limit_check(desired_torque, VOLKSWAGEN_MQB_MAX_STEER, -VOLKSWAGEN_MQB_MAX_STEER);
  
        // *** torque rate limit check ***
        violation |= driver_limit_check(desired_torque, desired_torque_last, &torque_driver,
          VOLKSWAGEN_MQB_MAX_STEER, VOLKSWAGEN_MQB_MAX_RATE_UP, VOLKSWAGEN_MQB_MAX_RATE_DOWN,
          VOLKSWAGEN_MQB_DRIVER_TORQUE_ALLOWANCE, VOLKSWAGEN_MQB_DRIVER_TORQUE_FACTOR);
        desired_torque_last = desired_torque;
  
        // *** torque real time rate limit check ***
        violation |= rt_rate_limit_check(desired_torque, rt_torque_last, VOLKSWAGEN_MQB_MAX_RT_DELTA);
  
        // every RT_INTERVAL set the new limits
        uint32_t ts_elapsed = get_ts_elapsed(ts, ts_last);
        if (ts_elapsed > VOLKSWAGEN_MQB_RT_INTERVAL) {
          rt_torque_last = desired_torque;
          ts_last = ts;
        }
      }
  
      // no torque if controls is not allowed
      if (!controls_allowed && (desired_torque != 0)) {
        violation = true;
      }
  
      // reset to 0 if either controls is not allowed or there's a violation
      if (violation || !controls_allowed) {
        desired_torque_last = 0;
        rt_torque_last = 0;
        ts_last = ts;
      }
  
      if (violation) {
        tx = 0;
      }
    }
  
    // FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off.
    // This avoids unintended engagements while still allowing resume spam
    if ((addr == MSG_GRA_ACC_01) && !controls_allowed) {
      // disallow resume and set: bits 16 and 19
      if ((GET_BYTE(to_send, 2) & 0x9U) != 0U) {
        tx = 0;
      }
    }
  
    // 1 allows the message through
    return tx;
  }
  
  static int volkswagen_mqb_fwd_hook(int bus_num, CANPacket_t *to_fwd) {
    int addr = GET_ADDR(to_fwd);
    int bus_fwd = -1;
  
    switch (bus_num) {
      case 0:
        // Forward all traffic from the Extended CAN onward
        bus_fwd = 2;
        break;
      case 2:
        if ((addr == MSG_HCA_01) || (addr == MSG_LDW_02)) {
          // OP takes control of the Heading Control Assist and Lane Departure Warning messages from the camera
          bus_fwd = -1;
        } else {
          // Forward all remaining traffic from Extended CAN devices to J533 gateway
          bus_fwd = 0;
        }
        break;
      default:
        // No other buses should be in use; fallback to do-not-forward
        bus_fwd = -1;
        break;
    }
  
    return bus_fwd;
  }
  
  const safety_hooks volkswagen_mqb_hooks = {
    .init = volkswagen_mqb_init,
    .rx = volkswagen_mqb_rx_hook,
    .tx = volkswagen_mqb_tx_hook,
    .tx_lin = nooutput_tx_lin_hook,
    .fwd = volkswagen_mqb_fwd_hook,
  };
  