mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-20 07:43:48 +08:00
Rivian Angle Support
This commit is contained in:
@@ -538,6 +538,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"RelaxedJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"ReverseCruise", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RivianAngleControl", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"RivianAngleSaturated", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"RivianToiRecoveryFailed", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"RecoveryPower", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2, SETTINGS_SIMPLE}},
|
||||
{"RoadNameUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
|
||||
@@ -374,6 +374,7 @@ struct CarControl {
|
||||
brake @1: Float32; # [0.0, 1.0]
|
||||
torqueOutputCan @8: Float32; # value sent over can to the car
|
||||
speed @6: Float32; # m/s
|
||||
lateralControlMode @9: LateralControlMode;
|
||||
|
||||
enum LongControlState @0xe40f3a917d908282{
|
||||
off @0;
|
||||
@@ -381,6 +382,13 @@ struct CarControl {
|
||||
stopping @2;
|
||||
starting @3;
|
||||
}
|
||||
|
||||
enum LateralControlMode {
|
||||
inactive @0;
|
||||
torque @1;
|
||||
angle @2;
|
||||
torqueRecovering @3;
|
||||
}
|
||||
}
|
||||
|
||||
struct CruiseControl {
|
||||
@@ -510,6 +518,7 @@ struct CarParams {
|
||||
startingState @70 :Bool; # Does this car make use of special starting state
|
||||
|
||||
steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds
|
||||
lateralSmoothSeconds @78 :Float32; # Speed-scheduled curvature smoothing used by select angle-control platforms
|
||||
longitudinalActuatorDelay @58 :Float32; # Gas/Brake actuator delay in seconds
|
||||
openpilotLongitudinalControl @37 :Bool; # is openpilot doing the longitudinal control?
|
||||
carVin @38 :Text; # VIN number queried during fingerprinting
|
||||
|
||||
@@ -142,6 +142,7 @@ class CarHarness(EnumBase):
|
||||
ford_q3 = BaseCarHarness("Ford Q3 connector")
|
||||
ford_q4 = BaseCarHarness("Ford Q4 connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
rivian = BaseCarHarness("Rivian A connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
rivian_b = BaseCarHarness("Rivian B connector", parts=[Accessory.harness_box, Accessory.comma_power, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
tesla_a = BaseCarHarness("Tesla A connector", parts=[Accessory.harness_box, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
tesla_b = BaseCarHarness("Tesla B connector", parts=[Accessory.harness_box, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
psa_a = BaseCarHarness("PSA A connector", parts=[Accessory.harness_box, Cable.long_obdc_cable, Cable.usbc_coupler])
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.rivian.riviancan import create_lka_steering, create_longitudinal, create_wheel_touch, create_adas_status
|
||||
from opendbc.car.rivian.values import CarControllerParams
|
||||
from opendbc.car.rivian.ext_controller import HIGH_ANGLE_CAP_FRAC, HIGH_ANGLE_THRESHOLD_DEG, ExternalController
|
||||
from opendbc.car.rivian.riviancan import (create_acm_status, create_adas_status, create_angle_steering,
|
||||
create_lka_steering, create_longitudinal, create_wheel_touch)
|
||||
from opendbc.car.rivian.toi_controller import TOI_MAX_ANGLE_DEG, ToiController
|
||||
from opendbc.car.rivian.values import CarControllerParams, RivianFlags
|
||||
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
LateralControlMode = structs.CarControl.Actuators.LateralControlMode
|
||||
|
||||
|
||||
def get_longitudinal_accel(requested_accel: float, gas_pressed: bool, long_active: bool = False,
|
||||
v_ego: float = 0.0) -> float:
|
||||
# Keep Rivian's command stream continuous when Panda's gas safety check becomes active.
|
||||
if gas_pressed:
|
||||
return 0.0
|
||||
|
||||
accel = requested_accel
|
||||
if long_active:
|
||||
accel += float(np.interp(v_ego, CarControllerParams.ACCEL_FF_DRAG_BP, CarControllerParams.ACCEL_FF_DRAG_V))
|
||||
return float(np.clip(accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
@@ -14,29 +32,104 @@ class CarController(CarControllerBase):
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
|
||||
self.cancel_frames = 0
|
||||
self.toi_controller = ToiController()
|
||||
self.angle_harness = bool(CP.flags & RivianFlags.ANGLE_HARNESS)
|
||||
self.ext_controller = ExternalController(CP) if self.angle_harness else None
|
||||
self.angle_saturation_last = None
|
||||
self.angle_saturation_params = None
|
||||
self.toi_recovery_failed_last = None
|
||||
self.toi_recovery_params = None
|
||||
try:
|
||||
# Keep opendbc importable standalone while exposing controller-only state
|
||||
# to selfdrived's existing car-specific warning bridge.
|
||||
from openpilot.common.params import Params
|
||||
status_params = Params(memory=True)
|
||||
self.toi_recovery_params = status_params
|
||||
if self.angle_harness:
|
||||
self.angle_saturation_params = status_params
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _publish_angle_saturation(self) -> None:
|
||||
params = getattr(self, "angle_saturation_params", None)
|
||||
if params is None:
|
||||
return
|
||||
|
||||
saturated = bool(self.ext_controller.angle_saturated)
|
||||
if saturated != getattr(self, "angle_saturation_last", None):
|
||||
put_bool = getattr(params, "put_bool_nonblocking", None) or params.put_bool
|
||||
put_bool("RivianAngleSaturated", saturated)
|
||||
self.angle_saturation_last = saturated
|
||||
|
||||
def _publish_toi_recovery_failed(self) -> None:
|
||||
params = getattr(self, "toi_recovery_params", None)
|
||||
if params is None:
|
||||
return
|
||||
|
||||
toi_controller = self.ext_controller.toi_controller if self.angle_harness else self.toi_controller
|
||||
failed = bool(toi_controller.recovery_failed)
|
||||
if failed != getattr(self, "toi_recovery_failed_last", None):
|
||||
put_bool = getattr(params, "put_bool_nonblocking", None) or params.put_bool
|
||||
put_bool("RivianToiRecoveryFailed", failed)
|
||||
self.toi_recovery_failed_last = failed
|
||||
|
||||
def update_live_params(self, roll, angle_offset_deg, stiffness_factor, steer_ratio):
|
||||
if self.ext_controller is not None:
|
||||
self.ext_controller.roll = roll
|
||||
self.ext_controller.angle_offset_deg = angle_offset_deg
|
||||
self.ext_controller.VM.update_params(max(stiffness_factor, 0.1), max(steer_ratio, 0.1))
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
can_sends = []
|
||||
|
||||
lat_active = CC.latActive and CS.out.gearShifter == GearShifter.drive
|
||||
apply_torque = 0
|
||||
torque_request = False
|
||||
steer_max = round(float(np.interp(CS.out.vEgoRaw, CarControllerParams.STEER_MAX_LOOKUP[0],
|
||||
CarControllerParams.STEER_MAX_LOOKUP[1])))
|
||||
if CC.latActive:
|
||||
new_torque = int(round(CC.actuators.torque * steer_max))
|
||||
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last,
|
||||
CS.out.steeringTorque, CarControllerParams, steer_max)
|
||||
if self.angle_harness:
|
||||
# The Panda permission is capability-gated at fingerprint time; this
|
||||
# runtime setting only selects which already-safe channel is active.
|
||||
self.ext_controller.force_torque = not bool(getattr(starpilot_toggles, "rivian_angle_control", False))
|
||||
self.ext_controller.update(CS, lat_active, actuators)
|
||||
self._publish_angle_saturation()
|
||||
apply_torque = self.ext_controller.torque_cmd
|
||||
torque_request = self.ext_controller.toi_act_cmd
|
||||
else:
|
||||
torque_request, torque_allowed = self.toi_controller.update(
|
||||
lat_active,
|
||||
abs(CS.out.steeringAngleDeg) >= TOI_MAX_ANGLE_DEG,
|
||||
bool(getattr(CS, "toi_fault", False)),
|
||||
bool(getattr(CS, "toi_active", False)),
|
||||
bool(getattr(CS, "toi_unavailable", False)),
|
||||
)
|
||||
|
||||
if lat_active and torque_allowed:
|
||||
new_torque = int(round(CC.actuators.torque * steer_max))
|
||||
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last,
|
||||
CS.out.steeringTorque, CarControllerParams, steer_max)
|
||||
if abs(CS.out.steeringAngleDeg) > HIGH_ANGLE_THRESHOLD_DEG:
|
||||
cap = int(round(steer_max * HIGH_ANGLE_CAP_FRAC))
|
||||
apply_torque = int(np.clip(apply_torque, -cap, cap))
|
||||
|
||||
# send steering command
|
||||
self.apply_torque_last = apply_torque
|
||||
can_sends.append(create_lka_steering(self.packer, self.frame, CS.acm_lka_hba_cmd, apply_torque, CC.enabled, CC.latActive))
|
||||
self._publish_toi_recovery_failed()
|
||||
can_sends.append(create_lka_steering(self.packer, self.frame, CS.acm_lka_hba_cmd,
|
||||
apply_torque, CC.enabled, torque_request))
|
||||
|
||||
if self.frame % 5 == 0:
|
||||
can_sends.append(create_wheel_touch(self.packer, CS.sccm_wheel_touch, CC.enabled))
|
||||
if self.angle_harness:
|
||||
can_sends.append(create_angle_steering(self.packer, self.frame, self.ext_controller.apply_angle_last,
|
||||
self.ext_controller.angle_active))
|
||||
feature_status = (1 if self.ext_controller.torque_active else 2) if lat_active else 0
|
||||
can_sends.append(create_acm_status(self.packer, self.frame, feature_status))
|
||||
|
||||
if self.frame % 5 == 0 and not (self.CP.flags & RivianFlags.GEN2):
|
||||
can_sends.append(create_wheel_touch(self.packer, CS.sccm_wheel_touch, lat_active if self.angle_harness else CC.enabled))
|
||||
|
||||
# Longitudinal control
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
|
||||
accel = get_longitudinal_accel(actuators.accel, CS.out.gasPressed, CC.longActive, CS.out.vEgo)
|
||||
can_sends.append(create_longitudinal(self.packer, self.frame, accel, CC.enabled))
|
||||
else:
|
||||
interface_status = None
|
||||
@@ -48,11 +141,27 @@ class CarController(CarControllerBase):
|
||||
else:
|
||||
self.cancel_frames = 0
|
||||
|
||||
can_sends.append(create_adas_status(self.packer, CS.vdm_adas_status, interface_status))
|
||||
for msg in CS.vdm_adas_status:
|
||||
can_sends.append(create_adas_status(self.packer, msg, interface_status))
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
# Always report the actual applied torque. In angle mode this is zero, which
|
||||
# deliberately freezes the torque PID integrator instead of letting it wind
|
||||
# up and dump saturated torque into the first cooperative handoff.
|
||||
new_actuators.torque = apply_torque / steer_max
|
||||
new_actuators.torqueOutputCan = apply_torque
|
||||
lateral_mode = LateralControlMode.inactive
|
||||
if self.angle_harness:
|
||||
new_actuators.steeringAngleDeg = self.ext_controller.apply_angle_last
|
||||
if lat_active and self.ext_controller.toi_controller.recovering:
|
||||
lateral_mode = LateralControlMode.torqueRecovering
|
||||
elif lat_active and (self.ext_controller.torque_active or self.ext_controller.torque_prearm):
|
||||
lateral_mode = LateralControlMode.torque
|
||||
elif lat_active and self.ext_controller.angle_active:
|
||||
lateral_mode = LateralControlMode.angle
|
||||
elif lat_active:
|
||||
lateral_mode = LateralControlMode.torqueRecovering if self.toi_controller.recovering else LateralControlMode.torque
|
||||
new_actuators.lateralControlMode = lateral_mode
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -3,20 +3,36 @@ from cereal import custom
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.rivian.values import DBC, GEAR_MAP
|
||||
from opendbc.car.rivian.carstate_ext import RivianLongitudinalState
|
||||
from opendbc.car.rivian.faults import TOI_FAULT_ALERT_FRAMES, get_steering_faults
|
||||
from opendbc.car.rivian.values import DBC, GEAR_MAP, RivianFlags
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
|
||||
|
||||
def get_cruise_available(flags: int, acm_feature_status: int) -> bool:
|
||||
# VDM reports unavailable until after stock ACC activates, so it cannot gate
|
||||
# the PCM enable edge. The ACM reports standby before activation and ACC once active.
|
||||
return bool(flags & RivianFlags.LONGITUDINAL_HARNESS) or acm_feature_status in (0, 1)
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP, FPCP):
|
||||
super().__init__(CP, FPCP)
|
||||
self.last_speed = 30
|
||||
self.longitudinal_state = RivianLongitudinalState(CP)
|
||||
|
||||
self.acm_lka_hba_cmd = None
|
||||
self.sccm_wheel_touch = None
|
||||
self.vdm_adas_status = None
|
||||
self.acm_lka_hba_cmd: dict | None = None
|
||||
self.sccm_wheel_touch: dict | None = None
|
||||
self.vdm_adas_status: list[dict] = []
|
||||
self.hands_on_level = 0
|
||||
self.eac_status = 0
|
||||
self.eac_error_code = 0
|
||||
self.toi_fault = False
|
||||
self.toi_active = False
|
||||
self.toi_unavailable = False
|
||||
self.toi_fault_frames = 0
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
@@ -44,17 +60,30 @@ class CarState(CarStateBase):
|
||||
ret.steeringTorque = cp.vl["EPAS_SystemStatus"]["EPAS_TorsionBarTorque"]
|
||||
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > 1.0, 5)
|
||||
|
||||
ret.steerFaultTemporary = cp.vl["EPAS_AdasStatus"]["EPAS_EacErrorCode"] != 0
|
||||
self.eac_error_code = int(cp.vl["EPAS_AdasStatus"]["EPAS_EacErrorCode"])
|
||||
self.eac_status = int(cp.vl["EPAS_AdasStatus"]["EPAS_EacStatus"])
|
||||
self.hands_on_level = int(cp.vl["EPAS_SystemStatus"]["EPAS_HandsOnLevel"])
|
||||
self.toi_fault = cp.vl["EPAS_SystemStatus"]["H_CAN_EPSS_ToiFlt"] != 0
|
||||
self.toi_active = cp.vl["EPAS_SystemStatus"]["H_CAN_EPSS_ToiActive"] != 0
|
||||
self.toi_unavailable = cp.vl["EPAS_SystemStatus"]["H_CAN_EPS_ToiUnavailable"] != 0
|
||||
toi_fault = self.toi_fault or self.toi_unavailable
|
||||
self.toi_fault_frames = self.toi_fault_frames + 1 if toi_fault else 0
|
||||
toi_fault_persistent = self.toi_fault_frames >= TOI_FAULT_ALERT_FRAMES
|
||||
ret.steerFaultPermanent, ret.steerFaultTemporary, ret.steeringDisengage = get_steering_faults(
|
||||
bool(self.CP.flags & RivianFlags.ANGLE_HARNESS), toi_fault, toi_fault_persistent,
|
||||
self.eac_status, self.eac_error_code,
|
||||
)
|
||||
|
||||
# Cruise state
|
||||
speed = min(int(cp_adas.vl["ACM_tsrCmd"]["ACM_tsrSpdDisClsMain"]), 85)
|
||||
self.last_speed = speed if speed != 0 else self.last_speed
|
||||
ret.cruiseState.enabled = cp_cam.vl["ACM_Status"]["ACM_FeatureStatus"] == 1
|
||||
acm_feature_status = int(cp_cam.vl["ACM_Status"]["ACM_FeatureStatus"])
|
||||
ret.cruiseState.enabled = acm_feature_status == 1
|
||||
# TODO: find cruise set speed on CAN
|
||||
ret.cruiseState.speed = self.last_speed * CV.MPH_TO_MS # detected speed limit
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
ret.cruiseState.speed = -1
|
||||
ret.cruiseState.available = cp.vl["VDM_AdasSts"]["VDM_AdasInterfaceStatus"] in (1, 2)
|
||||
ret.cruiseState.available = get_cruise_available(self.CP.flags, acm_feature_status)
|
||||
ret.cruiseState.standstill = cp.vl["VDM_AdasSts"]["VDM_AdasVehicleHoldStatus"] == 1
|
||||
|
||||
# ACM_Status->ACM_FaultSupervisorState normally 1, appears to go to 3 when either:
|
||||
@@ -71,36 +100,47 @@ class CarState(CarStateBase):
|
||||
# Gear
|
||||
ret.gearShifter = GEAR_MAP.get(int(cp.vl["VDM_PropStatus"]["VDM_Prndl_Status"]), GearShifter.unknown)
|
||||
|
||||
# Doors
|
||||
ret.doorOpen = any(cp_adas.vl["IndicatorLights"][door] != 2 for door in ("RearDriverDoor", "FrontPassengerDoor", "DriverDoor", "RearPassengerDoor"))
|
||||
# Gen 2 does not publish these signals. Stock ACC handles their disengage
|
||||
# behavior at standstill, and the doors cannot be opened while driving.
|
||||
if not (self.CP.flags & RivianFlags.GEN2):
|
||||
ret.doorOpen = any(cp_adas.vl["IndicatorLights"][door] != 2 for door in ("RearDriverDoor", "FrontPassengerDoor", "DriverDoor", "RearPassengerDoor"))
|
||||
ret.seatbeltUnlatched = cp.vl["RCM_Status"]["RCM_Status_IND_WARN_BELT_DRIVER"] != 0
|
||||
|
||||
# Blinkers
|
||||
ret.leftBlinker = cp_adas.vl["IndicatorLights"]["TurnLightLeft"] in (1, 2)
|
||||
ret.rightBlinker = cp_adas.vl["IndicatorLights"]["TurnLightRight"] in (1, 2)
|
||||
|
||||
# Seatbelt
|
||||
ret.seatbeltUnlatched = cp.vl["RCM_Status"]["RCM_Status_IND_WARN_BELT_DRIVER"] != 0
|
||||
|
||||
# Blindspot
|
||||
# ret.leftBlindspot = False
|
||||
# ret.rightBlindspot = False
|
||||
|
||||
# AEB
|
||||
ret.stockAeb = cp_cam.vl["ACM_AebRequest"]["ACM_EnableRequest"] != 0
|
||||
|
||||
# Messages needed by carcontroller
|
||||
self.acm_lka_hba_cmd = copy.copy(cp_cam.vl["ACM_lkaHbaCmd"])
|
||||
self.sccm_wheel_touch = copy.copy(cp.vl["SCCM_WheelTouch"])
|
||||
self.vdm_adas_status = copy.copy(cp.vl["VDM_AdasSts"])
|
||||
if not (self.CP.flags & RivianFlags.GEN2):
|
||||
self.sccm_wheel_touch = copy.copy(cp.vl["SCCM_WheelTouch"])
|
||||
# This message can lag and deliver two samples in one parser cycle. Forward
|
||||
# every sample so cancelling stock ACC remains reliable.
|
||||
adas_status_msgs = cp.vl_all["VDM_AdasSts"]
|
||||
self.vdm_adas_status = [dict(zip(adas_status_msgs, vals, strict=True))
|
||||
for vals in zip(*adas_status_msgs.values(), strict=True)]
|
||||
if not self.vdm_adas_status:
|
||||
self.vdm_adas_status = [copy.copy(cp.vl["VDM_AdasSts"])]
|
||||
|
||||
self.longitudinal_state.update(ret, can_parsers)
|
||||
|
||||
fp_ret = custom.StarPilotCarState.new_message()
|
||||
|
||||
return ret, fp_ret
|
||||
|
||||
def set_cruise_speed(self, speed: float) -> float:
|
||||
return self.longitudinal_state.set_cruise_speed(speed)
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers(CP):
|
||||
return {
|
||||
parsers = {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
|
||||
Bus.adas: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 1),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
|
||||
}
|
||||
if CP.flags & RivianFlags.LONGITUDINAL_HARNESS:
|
||||
parsers[Bus.alt] = CANParser(DBC[CP.carFingerprint][Bus.alt], [], 1)
|
||||
return parsers
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
"""Rivian Extreme-harness state support."""
|
||||
|
||||
import math
|
||||
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.rivian.values import RivianFlags
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
MAX_SET_SPEED = 85 * CV.MPH_TO_MS
|
||||
MIN_SET_SPEED = 20 * CV.MPH_TO_MS
|
||||
|
||||
|
||||
class RivianLongitudinalState:
|
||||
"""State provided by the Extreme harness park-assist CAN bridge."""
|
||||
|
||||
def __init__(self, CP):
|
||||
self.CP = CP
|
||||
self.set_speed = 10
|
||||
self.increase_button = False
|
||||
self.decrease_button = False
|
||||
self.distance_button = 0
|
||||
self.scroll_click_pressed = False
|
||||
self.increase_counter = 0
|
||||
self.decrease_counter = 0
|
||||
self.stalk_down_counter = 0
|
||||
|
||||
def set_cruise_speed(self, speed: float) -> float:
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
self.set_speed = max(MIN_SET_SPEED, min(float(speed), MAX_SET_SPEED))
|
||||
return self.set_speed
|
||||
|
||||
def update_longitudinal_upgrade(self, ret: structs.CarState, can_parsers) -> list:
|
||||
cp_park = can_parsers[Bus.alt]
|
||||
cp_adas = can_parsers[Bus.adas]
|
||||
cp = can_parsers[Bus.pt]
|
||||
button_events = []
|
||||
|
||||
prev_increase_button = self.increase_button
|
||||
prev_decrease_button = self.decrease_button
|
||||
prev_scroll_click_pressed = self.scroll_click_pressed
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
right_scroll = int(cp_park.vl["WheelButtons_Fwd"]["RightButton_Scroll"])
|
||||
if right_scroll != 255:
|
||||
if self.distance_button != right_scroll:
|
||||
# Rivian's rotary value changes once per detent. A release-only gap
|
||||
# event preserves the existing three-profile personality cycling.
|
||||
button_events.append(structs.CarState.ButtonEvent(pressed=False, type=ButtonType.gapAdjustCruise))
|
||||
self.distance_button = right_scroll
|
||||
|
||||
# Scroll-click is a separate two-bit signal from rotary movement. Treat
|
||||
# it as a held distance button so the existing short/long/very-long
|
||||
# mappings apply, including Traffic Mode on a very-long hold.
|
||||
self.scroll_click_pressed = cp_park.vl["WheelButtons_Fwd"]["RightButton_ScrollClick"] == 2
|
||||
if self.scroll_click_pressed != prev_scroll_click_pressed:
|
||||
button_events.append(structs.CarState.ButtonEvent(pressed=self.scroll_click_pressed, type=ButtonType.gapAdjustCruise))
|
||||
|
||||
self.increase_button = cp_park.vl["WheelButtons_Fwd"]["RightButton_RightClick"] == 2
|
||||
self.decrease_button = cp_park.vl["WheelButtons_Fwd"]["RightButton_LeftClick"] == 2
|
||||
self.increase_counter = self.increase_counter + 1 if self.increase_button else 0
|
||||
self.decrease_counter = self.decrease_counter + 1 if self.decrease_button else 0
|
||||
|
||||
metric = cp_adas.vl["Cluster"]["Cluster_Unit"] == 0
|
||||
conversion = CV.KPH_TO_MS if metric else CV.MPH_TO_MS
|
||||
long_press_step = 10.0 if metric else 5.0
|
||||
set_speed_display = self.set_speed * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH)
|
||||
|
||||
if self.increase_button:
|
||||
if self.increase_counter % 66 == 0:
|
||||
self.set_speed = math.ceil((set_speed_display + 1) / long_press_step) * long_press_step * conversion
|
||||
elif not prev_increase_button:
|
||||
self.set_speed += conversion
|
||||
|
||||
if self.decrease_button:
|
||||
if self.decrease_counter % 66 == 0:
|
||||
self.set_speed = math.floor((set_speed_display - 1) / long_press_step) * long_press_step * conversion
|
||||
elif not prev_decrease_button:
|
||||
self.set_speed -= conversion
|
||||
|
||||
if not ret.cruiseState.enabled:
|
||||
self.set_speed = ret.vEgoCluster
|
||||
|
||||
stalk_down = int(cp.vl["VDM_AdasSts"]["VDM_UserAdasRequest"]) in (3, 4)
|
||||
self.stalk_down_counter = self.stalk_down_counter + 1 if stalk_down else 0
|
||||
if self.stalk_down_counter == 50:
|
||||
self.set_speed = max(self.set_speed, ret.vEgoCluster)
|
||||
|
||||
self.set_speed = max(MIN_SET_SPEED, min(self.set_speed, MAX_SET_SPEED))
|
||||
ret.cruiseState.speed = self.set_speed
|
||||
|
||||
ret.leftBlindspot = cp_park.vl["BSM_BlindSpotIndicator_Fwd"]["BSM_BlindSpotIndicator_Left"] != 0
|
||||
ret.rightBlindspot = cp_park.vl["BSM_BlindSpotIndicator_Fwd"]["BSM_BlindSpotIndicator_Right"] != 0
|
||||
|
||||
return button_events
|
||||
|
||||
def update(self, ret: structs.CarState, can_parsers) -> None:
|
||||
button_events = []
|
||||
|
||||
if self.CP.flags & RivianFlags.LONGITUDINAL_HARNESS:
|
||||
button_events.extend(self.update_longitudinal_upgrade(ret, can_parsers))
|
||||
|
||||
ret.buttonEvents = button_events
|
||||
@@ -0,0 +1,432 @@
|
||||
"""Rivian Gen 1 hybrid steering for the Extreme harness."""
|
||||
|
||||
import math
|
||||
from collections import deque
|
||||
|
||||
import numpy as np
|
||||
|
||||
from opendbc.car import rate_limit
|
||||
from opendbc.car.common.filter_simple import FirstOrderFilter
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, get_max_angle_delta_vm, get_max_angle_vm
|
||||
from opendbc.car.rivian.toi_controller import TOI_MAX_ANGLE_DEG, TOI_REARM_ANGLE_DEG, ToiController
|
||||
from opendbc.car.rivian.values import CAR, CarControllerParams as CCP, RivianFlags
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
# Limits observed in the Gen 1 EPAS firmware. Margins keep commands inside the
|
||||
# firmware's absolute-angle and sliding-window rate checks.
|
||||
EPAS_FW_MAX_ANGLE_BP = [0.0, 2.78, 5.56, 8.33, 12.50, 16.67, 22.22, 27.78]
|
||||
EPAS_FW_MAX_ANGLE_V = [500, 500, 250, 150, 85, 56, 40, 25]
|
||||
EPAS_FW_RATE_BP = [5.56, 8.33, 12.50, 16.67]
|
||||
EPAS_FW_RATE_V = [4.50, 1.50, 0.60, 0.18]
|
||||
EPAS_FW_ANGLE_MARGIN = 0.98
|
||||
EPAS_FW_RATE_MARGIN = 0.94
|
||||
PANDA_STEP_MARGIN = 0.9
|
||||
|
||||
MIN_TORQUE_FRAMES = 50
|
||||
HANDOFF_EXIT_DEG = 15.0
|
||||
UNWIND_HANDOFF_RATE = 40.0
|
||||
HANDOFF_MAX_ANGLE_DEG = 25.0
|
||||
DRIVER_HANDS_OFF_EXIT_FRAMES = 100
|
||||
EAC_RECOVER_FRAMES = 15
|
||||
|
||||
EAC_REARM_RELEASE_FRAMES = 25
|
||||
|
||||
# The wheel's inertia loads the torsion bar while the column is swinging. Reject
|
||||
# those transients so they cannot be mistaken for a driver override.
|
||||
TORSION_RATE_WINDOW = 25
|
||||
TORSION_MAX_RATE_SWING = 90.0
|
||||
DRIVER_OVERRIDE_TORQUE = 6.0
|
||||
DRIVER_OVERRIDE_FRAMES = 5
|
||||
|
||||
# Sustained light torsion bridges capacitive-sensor dropouts while the driver's
|
||||
# hands slide on the wheel. This only delays recovery from a driver override.
|
||||
PRESENCE_LPF_RC = 0.032
|
||||
PRESENCE_TORQUE_THRESHOLD = 1.5
|
||||
PRESENCE_MIN_FRAMES = 30
|
||||
PRESENCE_HOLD_FRAMES = 100
|
||||
|
||||
# Above this angle the rack spends most of its time saturated. Keeping a small
|
||||
# amount of headroom helps the torque controller recover as geometry unwinds.
|
||||
HIGH_ANGLE_THRESHOLD_DEG = 90
|
||||
HIGH_ANGLE_CAP_FRAC = 0.95
|
||||
|
||||
# A live angle->torque selection cannot release the EPAS angle servo before the
|
||||
# rate-limited torque channel is ready to carry the curve. Ramp torque under the
|
||||
# still-active angle command, then release angle once sufficient hold torque is
|
||||
# available. These values come from AdventurePilot's road-tested handoff.
|
||||
TORQUE_PREARM_EXIT_FRAC = 0.85
|
||||
TORQUE_PREARM_MIN_HOLD = 20
|
||||
TORQUE_PREARM_MAX_FRAMES = 150
|
||||
TORQUE_PREARM_STALL_FRAMES = 12
|
||||
TORQUE_PREARM_ABORT_LOCKOUT = 50
|
||||
|
||||
# The torque controller is deliberately frozen while angle steering is active,
|
||||
# so angle saturation needs its own debounced envelope check for the stock
|
||||
# "turn exceeds steering limit" warning.
|
||||
ANGLE_SAT_MIN_LAT_ACCEL = 1.0
|
||||
ANGLE_SAT_FRAMES = 30
|
||||
|
||||
|
||||
def apply_rivian_steer_angle_limits_vm(apply_angle: float, apply_angle_last: float, v_ego_raw: float, steering_angle: float,
|
||||
lat_active: bool, limits, VM: VehicleModel) -> float:
|
||||
"""Apply Rivian's jerk, accel, and safety constraints to its angle channel."""
|
||||
v_ego_raw = max(v_ego_raw, 1)
|
||||
|
||||
# When the speed-scheduled angle envelope shrinks, Rivian must unwind toward
|
||||
# it through the jerk limit instead of snapping directly to the new bound.
|
||||
max_angle = get_max_angle_vm(v_ego_raw, VM, limits)
|
||||
new_apply_angle = np.clip(apply_angle, -max_angle, max_angle)
|
||||
|
||||
max_angle_delta = get_max_angle_delta_vm(v_ego_raw, VM, limits)
|
||||
max_angle_delta = min(max_angle_delta, limits.ANGLE_LIMITS.MAX_ANGLE_RATE)
|
||||
new_apply_angle = rate_limit(new_apply_angle, apply_angle_last, -max_angle_delta, max_angle_delta)
|
||||
|
||||
if not lat_active:
|
||||
new_apply_angle = steering_angle
|
||||
|
||||
return float(np.clip(new_apply_angle, -limits.ANGLE_LIMITS.STEER_ANGLE_MAX, limits.ANGLE_LIMITS.STEER_ANGLE_MAX))
|
||||
|
||||
|
||||
class _RateBudget:
|
||||
WINDOW_USER_FRAMES = 16
|
||||
WINDOW_TIME_S = 0.16
|
||||
|
||||
def __init__(self):
|
||||
self.history = deque([0.0] * self.WINDOW_USER_FRAMES, maxlen=self.WINDOW_USER_FRAMES)
|
||||
|
||||
def push(self, sent_angle: float) -> None:
|
||||
self.history.append(round(sent_angle * 10) / 10)
|
||||
|
||||
def bounds(self, threshold_dps: float, margin: float) -> tuple[float, float]:
|
||||
cmd_oldest = self.history[0]
|
||||
budget = threshold_dps * self.WINDOW_TIME_S * margin
|
||||
return cmd_oldest - budget, cmd_oldest + budget
|
||||
|
||||
|
||||
def get_safety_CP():
|
||||
from opendbc.car.rivian.interface import CarInterface
|
||||
return CarInterface.get_non_essential_params(CAR.RIVIAN_R1_GEN1)
|
||||
|
||||
|
||||
class ExternalController:
|
||||
"""Hybrid Gen 1 steering: EPAS angle control with cooperative torque fallback."""
|
||||
|
||||
def __init__(self, CP):
|
||||
self.VM = VehicleModel(CP)
|
||||
self.VM_safety = VehicleModel(get_safety_CP())
|
||||
self.gen2 = bool(CP.flags & RivianFlags.GEN2)
|
||||
|
||||
self.wheel_touch_cnt = 0
|
||||
self.torsion_cnt = 0
|
||||
self.torsion_sign = 0
|
||||
self.driver_override_cnt = 0
|
||||
self.rate_hist = deque([0.0] * TORSION_RATE_WINDOW, maxlen=TORSION_RATE_WINDOW)
|
||||
self.hands_on = False
|
||||
self.torsion_lpf = FirstOrderFilter(0.0, PRESENCE_LPF_RC, 0.01)
|
||||
self.presence_cnt = 0
|
||||
self.presence_sign = 0
|
||||
self.presence_hold = 0
|
||||
self.hands_off_frames = 0
|
||||
|
||||
self.torque_active = False
|
||||
self.torque_active_frames = 0
|
||||
# Handback protections depend on why torque mode was entered. Driver
|
||||
# recovery gets the release timer; both driver and Galaxy recovery get the
|
||||
# low-angle gate.
|
||||
self.driver_override_recovery = False
|
||||
self.galaxy_torque_recovery = False
|
||||
self.lat_active_last = False
|
||||
self.eac_dead_frames = 0
|
||||
self.eac_rearm_release_frames = 0
|
||||
self.eac_rearm_attempted = False
|
||||
|
||||
# Galaxy can switch an angle-capable truck to torque while driving. The
|
||||
# prearm state makes that transition make-before-break.
|
||||
self.force_torque = False
|
||||
self.torque_prearm = False
|
||||
self.prearm_frames = 0
|
||||
self.prearm_torque_peak = 0
|
||||
self.prearm_stall_frames = 0
|
||||
self.prearm_abort_lockout = 0
|
||||
self.prearm_last_outcome = ""
|
||||
self.prearm_last_hold = 0
|
||||
self.prearm_last_frames = 0
|
||||
self.prearm_last_peak = 0
|
||||
|
||||
self.apply_angle_last = 0.0
|
||||
self.angle_active = False
|
||||
self.angle_saturated = False
|
||||
self.angle_sat_frames = 0
|
||||
self.rate_budget = _RateBudget()
|
||||
self.roll = 0.0
|
||||
self.angle_offset_deg = 0.0
|
||||
|
||||
self.apply_torque_last = 0
|
||||
self.torque_cmd = 0
|
||||
self.toi_controller = ToiController()
|
||||
self.toi_act_cmd = False
|
||||
|
||||
def update(self, CS, lat_active: bool, actuators) -> None:
|
||||
self._update_hands_on(CS)
|
||||
desired_angle = math.degrees(self.VM.get_steer_from_curvature(
|
||||
-float(actuators.curvature), CS.out.vEgo, self.roll)) + self.angle_offset_deg
|
||||
self._update_torque_active(CS, lat_active, desired_angle, actuators)
|
||||
desired_lat_accel = float(actuators.curvature) * CS.out.vEgo ** 2
|
||||
self._update_angle(CS, lat_active, desired_angle, desired_lat_accel)
|
||||
self._update_torque(CS, actuators)
|
||||
|
||||
def _update_wheel_touched(self, wheel_touched: bool, minimum_count: int) -> bool:
|
||||
self.wheel_touch_cnt += 1 if wheel_touched else -1
|
||||
self.wheel_touch_cnt = int(np.clip(self.wheel_touch_cnt, 0, minimum_count * 2 + 1))
|
||||
return self.wheel_touch_cnt > minimum_count
|
||||
|
||||
def _update_torsion(self, torque: float, steering_rate: float, threshold: float, minimum_count: int) -> bool:
|
||||
self.rate_hist.append(steering_rate)
|
||||
if max(self.rate_hist) - min(self.rate_hist) > TORSION_MAX_RATE_SWING:
|
||||
self.torsion_cnt = 0
|
||||
return False
|
||||
|
||||
abs_torque = abs(torque)
|
||||
pressed = abs_torque > threshold
|
||||
sign = int(np.sign(torque))
|
||||
if pressed and self.torsion_sign and sign != self.torsion_sign:
|
||||
self.torsion_cnt = 0
|
||||
else:
|
||||
self.torsion_cnt += max(1, math.ceil(abs_torque / threshold)) if pressed else -1
|
||||
self.torsion_cnt = int(np.clip(self.torsion_cnt, 0, minimum_count * 2 + 1))
|
||||
if pressed:
|
||||
self.torsion_sign = sign
|
||||
return self.torsion_cnt > minimum_count
|
||||
|
||||
def _update_torsion_presence(self, torque: float) -> bool:
|
||||
filtered = self.torsion_lpf.update(torque)
|
||||
sign = 1 if filtered > PRESENCE_TORQUE_THRESHOLD else -1 if filtered < -PRESENCE_TORQUE_THRESHOLD else 0
|
||||
self.presence_cnt = self.presence_cnt + 1 if sign != 0 and sign == self.presence_sign else int(sign != 0)
|
||||
self.presence_sign = sign
|
||||
if self.presence_cnt >= PRESENCE_MIN_FRAMES:
|
||||
self.presence_hold = PRESENCE_HOLD_FRAMES
|
||||
elif self.presence_hold > 0:
|
||||
self.presence_hold -= 1
|
||||
return self.presence_hold > 0
|
||||
|
||||
def _update_hands_on(self, CS) -> None:
|
||||
wheel_touch = False
|
||||
if not self.gen2:
|
||||
calibration = CS.sccm_wheel_touch["SETME_X52"]
|
||||
capacitive = CS.sccm_wheel_touch["SCCM_WheelTouch_CapacitiveValue"] > calibration * 0.9
|
||||
wheel_touch = self._update_wheel_touched(capacitive, 25)
|
||||
torsion = self._update_torsion(CS.out.steeringTorque, CS.out.steeringRateDeg, 4.0, 9)
|
||||
# Keep rejecting ordinary inertial torsion, but do not mask a sustained,
|
||||
# high-effort driver override just because the wheel is already moving fast.
|
||||
strong_override = abs(CS.out.steeringTorque) >= DRIVER_OVERRIDE_TORQUE and CS.out.steeringPressed
|
||||
self.driver_override_cnt += 1 if strong_override else -1
|
||||
self.driver_override_cnt = int(np.clip(self.driver_override_cnt, 0, DRIVER_OVERRIDE_FRAMES))
|
||||
presence = self._update_torsion_presence(CS.out.steeringTorque)
|
||||
self.hands_on = wheel_touch or torsion or self.driver_override_cnt >= DRIVER_OVERRIDE_FRAMES or CS.hands_on_level > 1
|
||||
self.hands_off_frames = 0 if self.hands_on or presence else self.hands_off_frames + 1
|
||||
|
||||
def _reset_prearm(self) -> None:
|
||||
self.torque_prearm = False
|
||||
self.prearm_frames = 0
|
||||
self.prearm_torque_peak = 0
|
||||
self.prearm_stall_frames = 0
|
||||
|
||||
def _end_prearm(self, outcome: str, hold_target: int) -> None:
|
||||
self.prearm_last_outcome = outcome
|
||||
self.prearm_last_hold = hold_target
|
||||
self.prearm_last_frames = self.prearm_frames
|
||||
self.prearm_last_peak = self.prearm_torque_peak
|
||||
self._reset_prearm()
|
||||
|
||||
def _update_torque_active(self, CS, lat_active: bool, desired_angle: float, actuators) -> None:
|
||||
self.torque_active_frames = self.torque_active_frames + 1 if self.torque_active else 0
|
||||
epas_ready = CS.eac_status == 1 and CS.eac_error_code == 0
|
||||
eac_active = CS.eac_status == 2
|
||||
epas_inhibited = CS.eac_status == 0 and CS.eac_error_code == 0
|
||||
gap = abs(desired_angle - CS.out.steeringAngleDeg)
|
||||
|
||||
if not lat_active:
|
||||
self.torque_active = False
|
||||
self.driver_override_recovery = False
|
||||
self.galaxy_torque_recovery = False
|
||||
self.eac_dead_frames = 0
|
||||
self.eac_rearm_release_frames = 0
|
||||
self.eac_rearm_attempted = False
|
||||
self.prearm_abort_lockout = 0
|
||||
self._reset_prearm()
|
||||
self.lat_active_last = False
|
||||
return
|
||||
|
||||
if self.force_torque:
|
||||
# A stale angle-recovery count must not trigger after switching modes.
|
||||
self.eac_dead_frames = 0
|
||||
self.eac_rearm_release_frames = 0
|
||||
self.eac_rearm_attempted = False
|
||||
self.driver_override_recovery = False
|
||||
self.lat_active_last = True
|
||||
|
||||
if self.torque_active:
|
||||
self.galaxy_torque_recovery = True
|
||||
self._reset_prearm()
|
||||
return
|
||||
|
||||
if self.prearm_abort_lockout > 0:
|
||||
self.prearm_abort_lockout -= 1
|
||||
self._reset_prearm()
|
||||
return
|
||||
|
||||
steer_max = round(float(np.interp(CS.out.vEgoRaw, CCP.STEER_MAX_LOOKUP[0], CCP.STEER_MAX_LOOKUP[1])))
|
||||
hold_target = abs(int(round(float(actuators.torque) * steer_max)))
|
||||
epas_holding = eac_active
|
||||
driver_took_over = self.hands_on and CS.out.steeringPressed
|
||||
if not epas_holding or driver_took_over or hold_target < TORQUE_PREARM_MIN_HOLD:
|
||||
self.torque_active = True
|
||||
self.galaxy_torque_recovery = True
|
||||
self._reset_prearm()
|
||||
return
|
||||
|
||||
# Keep angle active while the independently rate-limited torque channel
|
||||
# ramps underneath it. Progress is evaluated from the prior frame.
|
||||
self.torque_prearm = True
|
||||
self.prearm_frames += 1
|
||||
if abs(self.apply_torque_last) > self.prearm_torque_peak:
|
||||
self.prearm_torque_peak = abs(self.apply_torque_last)
|
||||
self.prearm_stall_frames = 0
|
||||
else:
|
||||
self.prearm_stall_frames += 1
|
||||
|
||||
reached = abs(self.apply_torque_last) >= TORQUE_PREARM_EXIT_FRAC * hold_target
|
||||
stalled = self.prearm_stall_frames >= TORQUE_PREARM_STALL_FRAMES
|
||||
if reached:
|
||||
self.torque_active = True
|
||||
self.galaxy_torque_recovery = True
|
||||
self._end_prearm("reached", hold_target)
|
||||
elif self.prearm_frames >= TORQUE_PREARM_MAX_FRAMES:
|
||||
if stalled:
|
||||
self._end_prearm("abort", hold_target)
|
||||
self.prearm_abort_lockout = TORQUE_PREARM_ABORT_LOCKOUT
|
||||
else:
|
||||
self.torque_active = True
|
||||
self.galaxy_torque_recovery = True
|
||||
self._end_prearm("backstop", hold_target)
|
||||
return
|
||||
|
||||
# Cancelling a force request during prearm immediately returns to the
|
||||
# already-active angle channel. A completed torque handoff uses the normal
|
||||
# cooperative release logic below to return to angle safely.
|
||||
self.prearm_abort_lockout = 0
|
||||
self._reset_prearm()
|
||||
if not self.torque_active:
|
||||
self.galaxy_torque_recovery = False
|
||||
|
||||
if (self.torque_active and epas_inhibited and
|
||||
not self.hands_on and not CS.out.steeringPressed):
|
||||
self.eac_rearm_release_frames = min(self.eac_rearm_release_frames + 1, EAC_REARM_RELEASE_FRAMES)
|
||||
else:
|
||||
self.eac_rearm_release_frames = 0
|
||||
|
||||
if self.hands_on and CS.out.steeringPressed:
|
||||
self.torque_active = True
|
||||
self.driver_override_recovery = True
|
||||
self.galaxy_torque_recovery = False
|
||||
self.eac_rearm_attempted = False
|
||||
elif self.eac_dead_frames >= EAC_RECOVER_FRAMES:
|
||||
self.torque_active = True
|
||||
self.driver_override_recovery = False
|
||||
self.galaxy_torque_recovery = False
|
||||
elif not self.lat_active_last and not epas_ready:
|
||||
self.torque_active = True
|
||||
self.driver_override_recovery = False
|
||||
self.galaxy_torque_recovery = False
|
||||
self.eac_rearm_attempted = False
|
||||
elif self.torque_active and self.torque_active_frames >= MIN_TORQUE_FRAMES and not self.hands_on:
|
||||
fw_max = float(np.interp(CS.out.vEgoRaw, EPAS_FW_MAX_ANGLE_BP, EPAS_FW_MAX_ANGLE_V)) * EPAS_FW_ANGLE_MARGIN
|
||||
in_envelope = abs(CS.out.steeringAngleDeg) < fw_max
|
||||
threshold_dps = float(np.interp(CS.out.vEgoRaw, EPAS_FW_RATE_BP, EPAS_FW_RATE_V)) * 100.0
|
||||
lower, upper = self.rate_budget.bounds(threshold_dps, EPAS_FW_RATE_MARGIN)
|
||||
rate_settled = lower <= CS.out.steeringAngleDeg <= upper and abs(CS.out.steeringRateDeg) < UNWIND_HANDOFF_RATE
|
||||
|
||||
rearm_ready = (epas_inhibited and not self.eac_rearm_attempted and
|
||||
self.eac_rearm_release_frames >= EAC_REARM_RELEASE_FRAMES and
|
||||
not CS.out.steeringPressed and not CS.out.steerFaultTemporary and
|
||||
not CS.out.steerFaultPermanent)
|
||||
handoff_angle_ready = (not (self.driver_override_recovery or self.galaxy_torque_recovery) or
|
||||
abs(CS.out.steeringAngleDeg) < HANDOFF_MAX_ANGLE_DEG)
|
||||
driver_handoff_ready = (not self.driver_override_recovery or
|
||||
self.hands_off_frames >= DRIVER_HANDS_OFF_EXIT_FRAMES)
|
||||
if ((epas_ready or rearm_ready) and in_envelope and rate_settled and
|
||||
gap < HANDOFF_EXIT_DEG and handoff_angle_ready and driver_handoff_ready):
|
||||
self.torque_active = False
|
||||
self.driver_override_recovery = False
|
||||
self.galaxy_torque_recovery = False
|
||||
self.eac_rearm_attempted = rearm_ready
|
||||
|
||||
if eac_active:
|
||||
self.eac_rearm_attempted = False
|
||||
|
||||
if not self.torque_active and not eac_active:
|
||||
self.eac_dead_frames += 1
|
||||
else:
|
||||
self.eac_dead_frames = 0
|
||||
self.lat_active_last = True
|
||||
|
||||
def _update_angle(self, CS, lat_active: bool, desired_angle: float, desired_lat_accel: float) -> None:
|
||||
self.angle_active = lat_active and not self.torque_active
|
||||
v_lookahead = max(CS.out.vEgoRaw + max(CS.out.aEgo, 0.0), 1.0)
|
||||
apply_angle = apply_rivian_steer_angle_limits_vm(desired_angle, self.apply_angle_last, v_lookahead,
|
||||
CS.out.steeringAngleDeg, self.angle_active, CCP, self.VM_safety)
|
||||
|
||||
saturated = False
|
||||
if self.angle_active:
|
||||
fw_max = float(np.interp(CS.out.vEgoRaw, EPAS_FW_MAX_ANGLE_BP, EPAS_FW_MAX_ANGLE_V)) * EPAS_FW_ANGLE_MARGIN
|
||||
safety_max = get_max_angle_vm(max(CS.out.vEgoRaw, 1.0), self.VM_safety, CCP)
|
||||
deliverable_angle = min(fw_max, safety_max)
|
||||
saturated = abs(desired_lat_accel) > ANGLE_SAT_MIN_LAT_ACCEL and abs(desired_angle) > deliverable_angle
|
||||
apply_angle = float(np.clip(apply_angle, -fw_max, fw_max))
|
||||
threshold_dps = float(np.interp(CS.out.vEgoRaw, EPAS_FW_RATE_BP, EPAS_FW_RATE_V)) * 100.0
|
||||
lower, upper = self.rate_budget.bounds(threshold_dps, EPAS_FW_RATE_MARGIN)
|
||||
apply_angle = float(np.clip(apply_angle, lower, upper))
|
||||
step = get_max_angle_delta_vm(max(CS.out.vEgoRaw, 1.0), self.VM_safety, CCP) * PANDA_STEP_MARGIN
|
||||
apply_angle = float(np.clip(apply_angle, self.apply_angle_last - step, self.apply_angle_last + step))
|
||||
|
||||
self.angle_sat_frames = self.angle_sat_frames + 1 if saturated else 0
|
||||
self.angle_saturated = self.angle_sat_frames >= ANGLE_SAT_FRAMES
|
||||
self.apply_angle_last = apply_angle
|
||||
self.rate_budget.push(apply_angle)
|
||||
|
||||
def _update_torque(self, CS, actuators) -> None:
|
||||
torque_requested = self.torque_active or self.torque_prearm
|
||||
self.toi_act_cmd, torque_allowed = self.toi_controller.update(
|
||||
torque_requested,
|
||||
abs(CS.out.steeringAngleDeg) >= TOI_MAX_ANGLE_DEG,
|
||||
bool(getattr(CS, "toi_fault", False)),
|
||||
bool(getattr(CS, "toi_active", False)),
|
||||
bool(getattr(CS, "toi_unavailable", False)),
|
||||
prearming=self.torque_prearm,
|
||||
high_angle_rearm=abs(CS.out.steeringAngleDeg) <= TOI_REARM_ANGLE_DEG,
|
||||
hold_high_angle_release=self.driver_override_recovery,
|
||||
)
|
||||
|
||||
if not torque_requested:
|
||||
self.apply_torque_last = 0
|
||||
self.torque_cmd = 0
|
||||
return
|
||||
|
||||
if not torque_allowed:
|
||||
# The EPAS has either not acknowledged the request yet or is completing a
|
||||
# high-angle release. Reset the limiter to the torque actually sent.
|
||||
self.apply_torque_last = 0
|
||||
self.torque_cmd = 0
|
||||
return
|
||||
|
||||
steer_max = round(float(np.interp(CS.out.vEgoRaw, CCP.STEER_MAX_LOOKUP[0], CCP.STEER_MAX_LOOKUP[1])))
|
||||
requested_torque = int(round(float(actuators.torque) * steer_max))
|
||||
torque_cmd = apply_driver_steer_torque_limits(requested_torque, self.apply_torque_last,
|
||||
CS.out.steeringTorque, CCP, steer_max)
|
||||
|
||||
if abs(CS.out.steeringAngleDeg) > HIGH_ANGLE_THRESHOLD_DEG:
|
||||
cap = int(round(steer_max * HIGH_ANGLE_CAP_FRAC))
|
||||
torque_cmd = int(np.clip(torque_cmd, -cap, cap))
|
||||
|
||||
self.apply_torque_last = torque_cmd
|
||||
self.torque_cmd = torque_cmd
|
||||
@@ -0,0 +1,12 @@
|
||||
TOI_FAULT_ALERT_FRAMES = 25
|
||||
|
||||
|
||||
def get_steering_faults(angle_harness: bool, toi_fault: bool, toi_fault_persistent: bool,
|
||||
eac_status: int, eac_error_code: int) -> tuple[bool, bool, bool]:
|
||||
if angle_harness:
|
||||
# The torque controller handles the short ToiFlt release/rearm handshake
|
||||
# internally. Escalate only if EPAS does not recover inside that window.
|
||||
temporary = toi_fault_persistent or (eac_status == 2 and eac_error_code != 0)
|
||||
return eac_status == 4, temporary, eac_status == 2 and eac_error_code == 12
|
||||
|
||||
return False, toi_fault or eac_error_code != 0, False
|
||||
@@ -3,7 +3,7 @@ from opendbc.car.interfaces import CarInterfaceBase
|
||||
from opendbc.car.rivian.carcontroller import CarController
|
||||
from opendbc.car.rivian.carstate import CarState
|
||||
from opendbc.car.rivian.radar_interface import RadarInterface
|
||||
from opendbc.car.rivian.values import RivianSafetyFlags
|
||||
from opendbc.car.rivian.values import RivianFlags, RivianSafetyFlags
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
@@ -11,27 +11,58 @@ class CarInterface(CarInterfaceBase):
|
||||
CarController = CarController
|
||||
RadarInterface = RadarInterface
|
||||
|
||||
@staticmethod
|
||||
def _apply_angle_caps(ret: structs.CarParams) -> None:
|
||||
"""Enable capabilities that are safe only with the xnor Extreme angle box."""
|
||||
ret.flags |= RivianFlags.ANGLE_HARNESS.value
|
||||
ret.safetyConfigs[0].safetyParam |= RivianSafetyFlags.ANGLE_CONTROL.value
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.lateralSmoothSeconds = 0.4
|
||||
ret.steerAtStandstill = True
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "rivian"
|
||||
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.rivian)]
|
||||
|
||||
ret.steerActuatorDelay = 0.15
|
||||
# Gen 2 (2025+) does not publish SCCM_WheelTouch on the powertrain bus.
|
||||
if 0x321 not in fingerprint[0]:
|
||||
ret.flags |= RivianFlags.GEN2.value
|
||||
|
||||
angle_harness = 0x1310 in fingerprint[1]
|
||||
longitudinal_harness = 0x131A in fingerprint[1]
|
||||
|
||||
if angle_harness:
|
||||
CarInterface._apply_angle_caps(ret)
|
||||
|
||||
if longitudinal_harness:
|
||||
ret.flags |= RivianFlags.LONGITUDINAL_HARNESS.value
|
||||
|
||||
# A base comma harness and the xnor longitudinal harness are valid torque
|
||||
# configurations. Angle-only caps remain gated to the detected Extreme box.
|
||||
if not angle_harness:
|
||||
ret.steerActuatorDelay = 0.15
|
||||
ret.lateralSmoothSeconds = 0.0
|
||||
ret.steerAtStandstill = False
|
||||
ret.steerLimitTimer = 0.4
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.torque
|
||||
ret.radarUnavailable = True
|
||||
ret.radarUnavailable = not longitudinal_harness
|
||||
ret.enableBsm = longitudinal_harness
|
||||
|
||||
# TODO: pending finding/handling missing set speed and fixing up radar parser
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
if alpha_long:
|
||||
ret.alphaLongitudinalAvailable = longitudinal_harness
|
||||
if alpha_long and ret.alphaLongitudinalAvailable:
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.safetyConfigs[0].safetyParam |= RivianSafetyFlags.LONG_CONTROL.value
|
||||
|
||||
ret.longitudinalActuatorDelay = 0.35
|
||||
# AdventurePilot road data measured roughly 0.26-0.38 s command-to-aEgo
|
||||
# lag. 0.3 s puts the planner near the center of the observed plant delay.
|
||||
ret.longitudinalActuatorDelay = 0.3
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.stopAccel = 0
|
||||
ret.stopAccel = -0.2
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [0.2]
|
||||
|
||||
return ret
|
||||
|
||||
@@ -50,22 +50,26 @@ class RadarInterface(RadarInterfaceBase):
|
||||
for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT):
|
||||
msg = self.rcp.vl[f"RADAR_TRACK_{addr:x}"]
|
||||
|
||||
if addr not in self.pts:
|
||||
self.pts[addr] = structs.RadarData.RadarPoint()
|
||||
self.pts[addr].trackId = self.track_id
|
||||
self.track_id += 1
|
||||
|
||||
valid = msg['STATE'] in (3, 4) and msg['STATE_2'] == 1
|
||||
# STATE: 1=New, 2=New_updated, 3=Updated, 4=Coasting, 7=New_coasting
|
||||
valid = msg['STATE'] in (1, 2, 3, 4, 7)
|
||||
# Ignore short-range-only objects, which include close stationary roadside
|
||||
# objects that can otherwise lead to phantom braking.
|
||||
valid = valid and msg['MODE'] in (2, 3)
|
||||
if valid:
|
||||
if addr not in self.pts or msg['STATE'] in (1, 2, 7):
|
||||
self.pts[addr] = structs.RadarData.RadarPoint()
|
||||
self.pts[addr].trackId = self.track_id
|
||||
self.track_id += 1
|
||||
|
||||
azimuth = math.radians(msg['AZIMUTH'])
|
||||
self.pts[addr].measured = True
|
||||
self.pts[addr].measured = msg['STATE'] in (2, 3)
|
||||
self.pts[addr].dRel = math.cos(azimuth) * msg['LONG_DIST']
|
||||
self.pts[addr].yRel = 0.5 * -math.sin(azimuth) * msg['LONG_DIST']
|
||||
self.pts[addr].yRel = -math.sin(azimuth) * msg['LONG_DIST']
|
||||
self.pts[addr].vRel = msg['REL_SPEED']
|
||||
self.pts[addr].aRel = float('nan')
|
||||
self.pts[addr].yvRel = float('nan')
|
||||
|
||||
else:
|
||||
elif addr in self.pts:
|
||||
del self.pts[addr]
|
||||
|
||||
ret.points = list(self.pts.values())
|
||||
|
||||
@@ -43,6 +43,33 @@ def create_lka_steering(packer, frame, acm_lka_hba_cmd, apply_torque, enabled, a
|
||||
return packer.make_can_msg("ACM_lkaHbaCmd", 0, values)
|
||||
|
||||
|
||||
def create_angle_steering(packer, frame, angle, active):
|
||||
values = {
|
||||
"ACM_SteeringControl_Counter": frame % 15,
|
||||
"ACM_SteeringAngleRequest": angle,
|
||||
"ACM_EacEnabled": active,
|
||||
"ACM_HapticRequired": 0,
|
||||
}
|
||||
|
||||
data = packer.make_can_msg("ACM_SteeringControl", 0, values)[1]
|
||||
values["ACM_SteeringControl_Checksum"] = checksum(data[1:], 0x1D, 0x41)
|
||||
return packer.make_can_msg("ACM_SteeringControl", 0, values)
|
||||
|
||||
|
||||
def create_acm_status(packer, frame, feature_status):
|
||||
values = {
|
||||
"ACM_Status_Counter": frame % 15,
|
||||
"ACM_FeatureStatus": feature_status,
|
||||
"ACM_FaultStatus": 0,
|
||||
"ACM_FaultSupervisorState": 0,
|
||||
"ACM_Unkown1": 0,
|
||||
}
|
||||
|
||||
data = packer.make_can_msg("ACM_Status", 0, values)[1]
|
||||
values["ACM_Status_Checksum"] = checksum(data[1:], 0x1D, 0x5F)
|
||||
return packer.make_can_msg("ACM_Status", 0, values)
|
||||
|
||||
|
||||
def create_wheel_touch(packer, sccm_wheel_touch, enabled):
|
||||
values = {s: sccm_wheel_touch[s] for s in (
|
||||
"SCCM_WheelTouch_Counter",
|
||||
@@ -92,6 +119,8 @@ def create_adas_status(packer, vdm_adas_status, interface_status):
|
||||
)}
|
||||
|
||||
if interface_status is not None:
|
||||
if interface_status == 1:
|
||||
values["VDM_UserAdasRequest"] = 1
|
||||
values["VDM_AdasInterfaceStatus"] = interface_status
|
||||
|
||||
data = packer.make_can_msg("VDM_AdasSts", 2, values)[1]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,179 @@
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
TOI_MAX_ANGLE_DEG = 90
|
||||
TOI_REARM_ANGLE_DEG = 80
|
||||
TOI_MAX_ANGLE_FRAMES = 89
|
||||
TOI_ACK_FRAMES = 2
|
||||
TOI_RECOVERY_TIMEOUT_FRAMES = 50
|
||||
|
||||
|
||||
class ToiState(IntEnum):
|
||||
INACTIVE = 0
|
||||
TORQUE = 1
|
||||
RELEASING = 2
|
||||
REARMING = 3
|
||||
PREARMING = 4
|
||||
ACTIVATING = 5
|
||||
HIGH_ANGLE_LOCKOUT = 6
|
||||
|
||||
|
||||
class ToiController:
|
||||
"""Handshake Rivian's torque-overlay request with the EPAS status feedback."""
|
||||
|
||||
def __init__(self):
|
||||
self.state = ToiState.INACTIVE
|
||||
self.high_angle_frames = 0
|
||||
self.ack_frames = 0
|
||||
self.recovery_frames = 0
|
||||
self.recovery_failed = False
|
||||
|
||||
@property
|
||||
def recovering(self) -> bool:
|
||||
return self.state in (ToiState.RELEASING, ToiState.REARMING, ToiState.HIGH_ANGLE_LOCKOUT)
|
||||
|
||||
def _reset(self) -> None:
|
||||
self.state = ToiState.INACTIVE
|
||||
self.high_angle_frames = 0
|
||||
self.ack_frames = 0
|
||||
self.recovery_frames = 0
|
||||
self.recovery_failed = False
|
||||
|
||||
def _start_release(self) -> None:
|
||||
self.state = ToiState.RELEASING
|
||||
self.high_angle_frames = 0
|
||||
self.ack_frames = 0
|
||||
self.recovery_frames = 0
|
||||
|
||||
def _start_rearm(self) -> None:
|
||||
self.state = ToiState.REARMING
|
||||
self.ack_frames = 0
|
||||
self.recovery_frames = 0
|
||||
|
||||
def _start_prearm(self) -> None:
|
||||
self.state = ToiState.PREARMING
|
||||
self.ack_frames = 0
|
||||
self.recovery_frames = 0
|
||||
|
||||
def _start_activation(self) -> None:
|
||||
self.state = ToiState.ACTIVATING
|
||||
self.ack_frames = 0
|
||||
self.recovery_frames = 0
|
||||
|
||||
def _start_high_angle_lockout(self) -> None:
|
||||
self.state = ToiState.HIGH_ANGLE_LOCKOUT
|
||||
self.ack_frames = 0
|
||||
self.recovery_frames = 0
|
||||
|
||||
def _finish_activation(self) -> None:
|
||||
self.state = ToiState.TORQUE
|
||||
self.high_angle_frames = 0
|
||||
self.ack_frames = 0
|
||||
self.recovery_frames = 0
|
||||
self.recovery_failed = False
|
||||
|
||||
def update(self, requested: bool, high_angle: bool, toi_fault: bool,
|
||||
toi_active: bool, toi_unavailable: bool, prearming: bool = False,
|
||||
high_angle_rearm: bool | None = None, hold_high_angle_release: bool = False) -> tuple[bool, bool]:
|
||||
"""Return the request bit and whether non-zero torque may be sent."""
|
||||
high_angle_rearm = not high_angle if high_angle_rearm is None else high_angle_rearm
|
||||
if not requested:
|
||||
self._reset()
|
||||
return False, False
|
||||
|
||||
# A live angle->torque selection ramps the rate-limited torque command while
|
||||
# EAC is still active. ToiActive cannot acknowledge until angle releases, so
|
||||
# prearm must be distinguished from an ordinary zero-torque rearm.
|
||||
if self.state == ToiState.INACTIVE:
|
||||
if prearming:
|
||||
self._start_prearm()
|
||||
else:
|
||||
self._start_rearm()
|
||||
elif prearming and self.state == ToiState.REARMING:
|
||||
self._start_prearm()
|
||||
elif not prearming and self.state == ToiState.PREARMING:
|
||||
self._start_activation()
|
||||
|
||||
if self.state == ToiState.TORQUE:
|
||||
if toi_fault or toi_unavailable:
|
||||
self._start_release()
|
||||
else:
|
||||
self.high_angle_frames = self.high_angle_frames + 1 if high_angle else 0
|
||||
if self.high_angle_frames > TOI_MAX_ANGLE_FRAMES:
|
||||
self._start_release()
|
||||
|
||||
if self.state == ToiState.RELEASING:
|
||||
self.recovery_frames += 1
|
||||
released = not toi_active and not toi_fault and not toi_unavailable
|
||||
self.ack_frames = self.ack_frames + 1 if released else 0
|
||||
self.recovery_failed |= self.recovery_frames >= TOI_RECOVERY_TIMEOUT_FRAMES
|
||||
if self.ack_frames >= TOI_ACK_FRAMES:
|
||||
if hold_high_angle_release and not high_angle_rearm:
|
||||
self._start_high_angle_lockout()
|
||||
elif prearming:
|
||||
self._start_prearm()
|
||||
else:
|
||||
self._start_rearm()
|
||||
return False, False
|
||||
|
||||
if self.state == ToiState.HIGH_ANGLE_LOCKOUT:
|
||||
if toi_fault or toi_unavailable:
|
||||
self.recovery_frames += 1
|
||||
self.recovery_failed |= self.recovery_frames >= TOI_RECOVERY_TIMEOUT_FRAMES
|
||||
elif not high_angle_rearm:
|
||||
self.recovery_frames = 0
|
||||
elif prearming:
|
||||
self._start_prearm()
|
||||
else:
|
||||
self._start_rearm()
|
||||
return False, False
|
||||
|
||||
if self.state == ToiState.PREARMING:
|
||||
if toi_fault or toi_unavailable:
|
||||
self._start_release()
|
||||
return False, False
|
||||
|
||||
self.high_angle_frames = self.high_angle_frames + 1 if high_angle else 0
|
||||
if self.high_angle_frames > TOI_MAX_ANGLE_FRAMES:
|
||||
self._start_release()
|
||||
return False, False
|
||||
return True, True
|
||||
|
||||
if self.state == ToiState.ACTIVATING:
|
||||
if toi_fault or toi_unavailable:
|
||||
self._start_release()
|
||||
return False, False
|
||||
|
||||
self.high_angle_frames = self.high_angle_frames + 1 if high_angle else 0
|
||||
if self.high_angle_frames > TOI_MAX_ANGLE_FRAMES:
|
||||
self._start_release()
|
||||
return False, False
|
||||
|
||||
self.recovery_frames += 1
|
||||
self.ack_frames = self.ack_frames + 1 if toi_active else 0
|
||||
if self.ack_frames >= TOI_ACK_FRAMES:
|
||||
self._finish_activation()
|
||||
elif self.recovery_frames >= TOI_RECOVERY_TIMEOUT_FRAMES:
|
||||
self.recovery_failed = True
|
||||
self._start_release()
|
||||
return False, False
|
||||
return True, True
|
||||
|
||||
if self.state == ToiState.REARMING:
|
||||
if toi_fault or toi_unavailable:
|
||||
self._start_release()
|
||||
return False, False
|
||||
|
||||
self.recovery_frames += 1
|
||||
self.ack_frames = self.ack_frames + 1 if toi_active else 0
|
||||
if self.ack_frames >= TOI_ACK_FRAMES:
|
||||
self._finish_activation()
|
||||
elif self.recovery_frames >= TOI_RECOVERY_TIMEOUT_FRAMES:
|
||||
# Drop the request before retrying instead of leaving an unacknowledged
|
||||
# torque command asserted indefinitely.
|
||||
self.recovery_failed = True
|
||||
self._start_release()
|
||||
return False, False
|
||||
return True, False
|
||||
|
||||
return True, True
|
||||
@@ -1,7 +1,8 @@
|
||||
from dataclasses import dataclass, field
|
||||
from enum import StrEnum, IntFlag
|
||||
|
||||
from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, structs, uds
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, structs, uds
|
||||
from opendbc.car.lateral import AngleSteeringLimits, ISO_LATERAL_ACCEL
|
||||
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
|
||||
from opendbc.car.vin import Vin
|
||||
@@ -22,6 +23,7 @@ class ModelYear(StrEnum):
|
||||
P_2023 = "P"
|
||||
R_2024 = "R"
|
||||
S_2025 = "S"
|
||||
T_2026 = "T"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -33,23 +35,28 @@ class RivianCarDocs(CarDocs):
|
||||
|
||||
@dataclass
|
||||
class RivianPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: 'rivian_primary_actuator', Bus.radar: 'rivian_mando_front_radar_generated'})
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: 'rivian_primary_actuator', Bus.radar: 'rivian_mando_front_radar_generated',
|
||||
Bus.alt: 'rivian_park_assist_can'})
|
||||
wmis: set[WMI] = field(default_factory=set)
|
||||
lines: set[ModelLine] = field(default_factory=set)
|
||||
years: set[ModelYear] = field(default_factory=set)
|
||||
|
||||
|
||||
class CAR(Platforms):
|
||||
# Retain the historical platform identifier for stored fingerprints and
|
||||
# routes; RivianFlags.GEN2 distinguishes the vehicle generation at runtime.
|
||||
RIVIAN_R1_GEN1 = RivianPlatformConfig(
|
||||
# TODO: verify this
|
||||
[
|
||||
RivianCarDocs("Rivian R1S 2022-24"),
|
||||
RivianCarDocs("Rivian R1S 2025", setup_video=None, car_parts=CarParts.common([CarHarness.rivian_b])),
|
||||
RivianCarDocs("Rivian R1T 2022-24"),
|
||||
RivianCarDocs("Rivian R1T 2025", setup_video=None, car_parts=CarParts.common([CarHarness.rivian_b])),
|
||||
],
|
||||
CarSpecs(mass=3206., wheelbase=3.08, steerRatio=15.2),
|
||||
wmis={WMI.RIVIAN_TRUCK, WMI.RIVIAN_MPV},
|
||||
lines={ModelLine.R1T, ModelLine.R1S},
|
||||
years={ModelYear.N_2022, ModelYear.P_2023, ModelYear.R_2024},
|
||||
years={ModelYear.N_2022, ModelYear.P_2023, ModelYear.R_2024, ModelYear.S_2025},
|
||||
)
|
||||
|
||||
|
||||
@@ -105,18 +112,22 @@ GEAR_MAP = {
|
||||
4: structs.CarState.GearShifter.drive,
|
||||
}
|
||||
|
||||
AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, conservative banked-road allowance for angle safety
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
# The R1T 2023 and R1S 2023 we tested on achieves slightly more lateral acceleration going left vs. right
|
||||
# and lateral acceleration falls linearly as speed decreases from 38 mph to 20 mph. These values are set
|
||||
# conservatively to reach a maximum of 3.0 m/s^2 turning left at 80 mph
|
||||
|
||||
# These refer to turning left:
|
||||
# 250 is ~2.8 m/s^2 above 17 m/s, then linearly ramps to ~1.6 m/s^2 from 17 m/s to 9 m/s
|
||||
# TODO: it is theorized older models have different steering racks and achieve down to half the
|
||||
# lateral acceleration referenced here at all speeds. detect this and ship a torque increase for those models
|
||||
STEER_MAX = 250 # 350 is intended to maintain lateral accel, not increase it
|
||||
STEER_MAX_LOOKUP = [9, 17], [350, 250]
|
||||
# AdventurePilot's road-tested four-point tune preserves the original low-
|
||||
# speed authority, lifts the 29-55 mph saturation band, and keeps the
|
||||
# highway cap conservative.
|
||||
STEER_MAX = 385
|
||||
STEER_MAX_LOOKUP = [9, 13, 25, 27], [385, 350, 295, 275]
|
||||
STEER_STEP = 1
|
||||
STEER_DELTA_UP = 3 # torque increase per refresh
|
||||
STEER_DELTA_DOWN = 5 # torque decrease per refresh
|
||||
@@ -124,15 +135,37 @@ class CarControllerParams:
|
||||
STEER_DRIVER_MULTIPLIER = 2 # weight driver torque
|
||||
STEER_DRIVER_FACTOR = 100
|
||||
|
||||
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
|
||||
500,
|
||||
([], []),
|
||||
([], []),
|
||||
MAX_LATERAL_ACCEL=ISO_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
|
||||
MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
|
||||
MAX_ANGLE_RATE=2.5,
|
||||
)
|
||||
|
||||
ACCEL_MIN = -3.5 # m/s^2
|
||||
ACCEL_MAX = 2.0 # m/s^2
|
||||
|
||||
# Compensate for the Rivian VDM's measured regen/creep drag without adding a
|
||||
# noisy proportional term around wheel-speed-derived aEgo. The offset fades
|
||||
# to zero at standstill so braking hold behavior is unchanged.
|
||||
ACCEL_FF_DRAG_BP = [0.8, 3.0, 8.0, 13.0, 20.0, 30.0]
|
||||
ACCEL_FF_DRAG_V = [0.0, 0.17, 0.17, 0.12, 0.10, 0.08]
|
||||
|
||||
def __init__(self, CP):
|
||||
pass
|
||||
|
||||
|
||||
class RivianSafetyFlags(IntFlag):
|
||||
LONG_CONTROL = 1
|
||||
ANGLE_CONTROL = 2
|
||||
|
||||
|
||||
class RivianFlags(IntFlag):
|
||||
ANGLE_HARNESS = 1
|
||||
LONGITUDINAL_HARNESS = 2
|
||||
GEN2 = 4
|
||||
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
@@ -40,319 +40,414 @@ BO_ 1280 RADAR_TRACK_500: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1281 RADAR_TRACK_501: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1282 RADAR_TRACK_502: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1283 RADAR_TRACK_503: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1284 RADAR_TRACK_504: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1285 RADAR_TRACK_505: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1286 RADAR_TRACK_506: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1287 RADAR_TRACK_507: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1288 RADAR_TRACK_508: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1289 RADAR_TRACK_509: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1290 RADAR_TRACK_50a: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1291 RADAR_TRACK_50b: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1292 RADAR_TRACK_50c: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1293 RADAR_TRACK_50d: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1294 RADAR_TRACK_50e: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1295 RADAR_TRACK_50f: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1296 RADAR_TRACK_510: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1297 RADAR_TRACK_511: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1298 RADAR_TRACK_512: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1299 RADAR_TRACK_513: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1300 RADAR_TRACK_514: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1301 RADAR_TRACK_515: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1302 RADAR_TRACK_516: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1303 RADAR_TRACK_517: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1304 RADAR_TRACK_518: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1305 RADAR_TRACK_519: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1306 RADAR_TRACK_51a: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1307 RADAR_TRACK_51b: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1308 RADAR_TRACK_51c: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1309 RADAR_TRACK_51d: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1310 RADAR_TRACK_51e: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1311 RADAR_TRACK_51f: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
VAL_ 1280 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1280 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1281 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1281 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1282 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1282 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1283 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1283 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1284 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1284 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1285 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1285 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1286 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1286 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1287 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1287 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1288 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1288 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1289 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1289 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1290 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1290 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1291 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1291 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1292 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1292 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1293 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1293 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1294 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1294 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1295 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1295 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1296 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1296 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1297 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1297 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1298 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1298 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1299 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1299 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1300 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1300 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1301 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1301 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1302 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1302 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1303 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1303 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1304 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1304 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1305 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1305 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1306 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1306 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1307 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1307 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1308 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1308 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1309 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1309 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1310 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1310 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1311 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1311 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
@@ -51,9 +51,15 @@ BO_ {a} RADAR_TRACK_{a:x}: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
""")
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
""")
|
||||
|
||||
for a in range(0x500, 0x500 + 32):
|
||||
f.write(f"""
|
||||
VAL_ {a} STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ {a} MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
""")
|
||||
|
||||
@@ -43,319 +43,414 @@ BO_ 1280 RADAR_TRACK_500: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1281 RADAR_TRACK_501: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1282 RADAR_TRACK_502: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1283 RADAR_TRACK_503: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1284 RADAR_TRACK_504: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1285 RADAR_TRACK_505: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1286 RADAR_TRACK_506: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1287 RADAR_TRACK_507: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1288 RADAR_TRACK_508: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1289 RADAR_TRACK_509: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1290 RADAR_TRACK_50a: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1291 RADAR_TRACK_50b: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1292 RADAR_TRACK_50c: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1293 RADAR_TRACK_50d: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1294 RADAR_TRACK_50e: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1295 RADAR_TRACK_50f: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1296 RADAR_TRACK_510: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1297 RADAR_TRACK_511: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1298 RADAR_TRACK_512: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1299 RADAR_TRACK_513: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1300 RADAR_TRACK_514: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1301 RADAR_TRACK_515: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1302 RADAR_TRACK_516: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1303 RADAR_TRACK_517: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1304 RADAR_TRACK_518: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1305 RADAR_TRACK_519: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1306 RADAR_TRACK_51a: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1307 RADAR_TRACK_51b: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1308 RADAR_TRACK_51c: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1309 RADAR_TRACK_51d: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1310 RADAR_TRACK_51e: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
BO_ 1311 RADAR_TRACK_51f: 8 RADAR
|
||||
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ UNKNOWN_1 : 23|8@0- (1,0) [-128|127] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-61.2|62.1] "" XXX
|
||||
SG_ AZIMUTH : 28|10@0- (0.1,0) [-51.2|51.1] "" XXX
|
||||
SG_ STATE : 31|3@0+ (1,0) [0|7] "" XXX
|
||||
SG_ LONG_DIST : 34|11@0+ (0.1,0) [0|204.7] "" XXX
|
||||
SG_ STATE_2 : 55|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "" XXX
|
||||
|
||||
SG_ MODE : 55|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ REL_SPEED : 53|14@0- (0.01,0) [-81.92|81.92] "m/s" XXX
|
||||
|
||||
VAL_ 1280 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1280 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1281 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1281 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1282 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1282 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1283 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1283 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1284 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1284 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1285 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1285 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1286 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1286 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1287 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1287 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1288 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1288 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1289 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1289 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1290 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1290 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1291 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1291 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1292 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1292 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1293 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1293 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1294 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1294 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1295 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1295 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1296 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1296 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1297 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1297 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1298 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1298 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1299 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1299 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1300 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1300 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1301 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1301 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1302 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1302 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1303 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1303 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1304 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1304 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1305 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1305 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1306 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1306 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1307 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1307 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1308 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1308 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1309 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1309 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1310 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1310 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
VAL_ 1311 STATE 0 "Empty" 1 "New" 2 "New_updated" 3 "Updated" 4 "Coasting" 7 "New_coasting" ;
|
||||
VAL_ 1311 MODE 0 "None" 1 "SRR" 2 "LRR" 3 "SRR_and_LRR" ;
|
||||
|
||||
@@ -51,5 +51,23 @@ BO_ 848 BSM_BlindSpotIndicator: 4 XXX
|
||||
SG_ BSM_BlindSpotIndicator_Left : 29|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ BSM_BlindSpotIndicator_Right : 30|2@1+ (1,0) [0|3] "" XXX
|
||||
|
||||
BO_ 4890 WheelButtons_Fwd: 7 XXX
|
||||
SG_ LeftButton_ScrollClick : 19|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LeftButton_RightClick : 21|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LeftButton_LeftClick : 22|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ LeftButton_Scroll : 31|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ RightButton_ScrollClick : 35|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ RightButton_RightClick : 37|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ RightButton_LeftClick : 38|2@1+ (1,0) [0|3] "" XXX
|
||||
SG_ RightButton_Scroll : 47|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 4944 BSM_BlindSpotIndicator_Fwd: 4 XXX
|
||||
SG_ BSM_BlindSpotIndicator_Checksum : 0|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ BSM_BlindSpotIndicator_Counter : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ BSM_BlindSpotIndicator_Left : 29|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ BSM_BlindSpotIndicator_Right : 30|2@1+ (1,0) [0|3] "" XXX
|
||||
|
||||
VAL_ 848 BSM_BlindSpotIndicator_Left 0 "OFF" 1 "OBJECT_DETECTED" 2 "ACTIVE_WARNING" ;
|
||||
VAL_ 848 BSM_BlindSpotIndicator_Right 0 "OFF" 1 "OBJECT_DETECTED" 2 "ACTIVE_WARNING" ;
|
||||
VAL_ 848 BSM_BlindSpotIndicator_Right 0 "OFF" 1 "OBJECT_DETECTED" 2 "ACTIVE_WARNING" ;
|
||||
VAL_ 4944 BSM_BlindSpotIndicator_Left 0 "OFF" 1 "OBJECT_DETECTED" 2 "ACTIVE_WARNING" ;
|
||||
VAL_ 4944 BSM_BlindSpotIndicator_Right 0 "OFF" 1 "OBJECT_DETECTED" 2 "ACTIVE_WARNING" ;
|
||||
|
||||
@@ -343,7 +343,8 @@ BO_ 801 SCCM_WheelTouch: 7 SCCM
|
||||
SG_ SCCM_WheelTouch_Counter : 11|4@0+ (1,0) [0|15] "" XXX
|
||||
SG_ SCCM_WheelTouch_HandsOn : 21|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ SETME_X52 : 31|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ SCCM_WheelTouch_CapacitiveValue : 32|12@1+ (1,0) [0|4095] "" XXX
|
||||
SG_ SCCM_WheelTouch_CapacitiveValue : 32|8@1+ (1,0) [0|4095] "" XXX
|
||||
SG_ SCCM_WheelTouch_CapacitiveValue2 : 47|8@0+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 811 ESP_EpbStatus: 8 ESP
|
||||
SG_ ESP_EpbStatus_Checksum : 7|8@0+ (1,0) [0|255] "" ACM,VDM
|
||||
@@ -881,6 +882,7 @@ VAL_ 354 VDM_AdasFaultStatus 0 "VDM_AdasFlaultStatus_No_Fault" 1 "VDM_AdasFaultS
|
||||
VAL_ 354 VDM_AdasDriverModeStatus 0 "VDM_AdasDriverModeStatus_Human" 1 "VDM_AdasDriverModeStatus_Adas" 2 "VDM_AdasDriverModeStatus_Reserved" 3 "VDM_AdasDriverModeStatus_Sna";
|
||||
VAL_ 354 VDM_AdasInterfaceStatus 0 "VDM_AdasInterfaceStatus_Unavailable" 1 "VDM_AdasInterfaceStatus_Available" 2 "VDM_AdasInterfaceStatus_Enabled" 3 "VDM_AdasInterfaceStatus_Faulted";
|
||||
VAL_ 354 VDM_AdasVehicleHoldStatus 0 "VDM_AdasVehicleHoldStatus_NotHold" 1 "VDM_AdasVehicleHoldStatus_Hold";
|
||||
VAL_ 354 VDM_UserAdasRequest 0 "IDLE" 1 "UP_1" 2 "UP_2" 3 "DOWN_1" 4 "DOWN_2";
|
||||
VAL_ 357 VDM_AdasStalkGapAdjust 0 "VDM_AdasStalkGapAdjust_No_Required" 1 "VDM_AdasStalkGapAdjust_GapDecrement" 2 "VDM_AdasStalkGapAdjust_GapIncrement";
|
||||
VAL_ 357 VDM_AdasStalkAccCancelRes 0 "VDM_AdasStalkAccCancelRes_NoRequired" 1 "VDM_AdasStalkAccCancelRes_Cancel" 2 "VDM_AdasStalkAccCancelRes_Resume";
|
||||
VAL_ 357 VDM_AdasStalkAutonomyButton 0 "VDM_AdasStalkAutonomyButton_No_Required" 1 "VDM_AdasStalkAutonomyButton_Pressed";
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "opendbc/safety/declarations.h"
|
||||
|
||||
static bool rivian_angle_control = false;
|
||||
|
||||
static uint8_t rivian_get_counter(const CANPacket_t *msg) {
|
||||
uint8_t cnt = 0;
|
||||
if ((msg->addr == 0x208U) || (msg->addr == 0x150U)) {
|
||||
@@ -86,6 +88,13 @@ static void rivian_rx_hook(const CANPacket_t *msg) {
|
||||
update_sample(&torque_driver, torque_driver_new);
|
||||
}
|
||||
|
||||
// Measured steering angle from EPAS, stored in 0.1 degrees to match
|
||||
// ACM_SteeringAngleRequest and angle_deg_to_can.
|
||||
if (msg->addr == 0x390U) {
|
||||
int angle_meas_new = ((msg->data[5] << 6) | (msg->data[6] >> 2)) - 8192U;
|
||||
update_sample(&angle_meas, angle_meas_new);
|
||||
}
|
||||
|
||||
// Brake pressed
|
||||
if (msg->addr == 0x38fU) {
|
||||
brake_pressed = (msg->data[2] >> 7) & 1U;
|
||||
@@ -97,20 +106,33 @@ static void rivian_rx_hook(const CANPacket_t *msg) {
|
||||
if (msg->addr == 0x100U) {
|
||||
const int feature_status = msg->data[2] >> 5U;
|
||||
pcm_cruise_check(feature_status == 1);
|
||||
|
||||
acc_main_on = (feature_status == 0) || (feature_status == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool rivian_tx_hook(const CANPacket_t *msg) {
|
||||
// Rivian utilizes more torque at low speed to maintain the same lateral accel
|
||||
const AngleSteeringLimits RIVIAN_ANGLE_STEERING_LIMITS = {
|
||||
.max_angle = 5000,
|
||||
.angle_deg_to_can = 10,
|
||||
.frequency = 100U,
|
||||
};
|
||||
|
||||
const AngleSteeringParams RIVIAN_ANGLE_STEERING_PARAMS = {
|
||||
.slip_factor = -0.0005445721739802007,
|
||||
.steer_ratio = 15.2,
|
||||
.wheelbase = 3.08,
|
||||
};
|
||||
|
||||
const TorqueSteeringLimits RIVIAN_STEERING_LIMITS = {
|
||||
.max_torque = 350,
|
||||
.max_torque = 385,
|
||||
.dynamic_max_torque = true,
|
||||
// Conservative three-point envelope around the software's four-point
|
||||
// [9,13,25,27] -> [385,350,295,275] tune. This curve is never below the
|
||||
// software lookup, so Panda permits every command the controller can emit.
|
||||
.max_torque_lookup = {
|
||||
{9., 17., 17.},
|
||||
{350, 250, 250},
|
||||
{9., 25., 27.},
|
||||
{385, 295, 275},
|
||||
},
|
||||
.max_rate_up = 3,
|
||||
.max_rate_down = 5,
|
||||
@@ -118,6 +140,10 @@ static bool rivian_tx_hook(const CANPacket_t *msg) {
|
||||
.driver_torque_multiplier = 2,
|
||||
.driver_torque_allowance = 100,
|
||||
.type = TorqueDriverLimited,
|
||||
.min_valid_request_frames = 89,
|
||||
.max_invalid_request_frames = 2,
|
||||
.min_valid_request_rt_interval = 810000,
|
||||
.has_steer_req_tolerance = true,
|
||||
};
|
||||
|
||||
const LongitudinalLimits RIVIAN_LONG_LIMITS = {
|
||||
@@ -129,6 +155,18 @@ static bool rivian_tx_hook(const CANPacket_t *msg) {
|
||||
bool tx = true;
|
||||
|
||||
if (msg->bus == 0U) {
|
||||
// Angle steering supplied only by the Extreme harness bridge.
|
||||
if (msg->addr == 0x110U) {
|
||||
int desired_angle = ((msg->data[2] << 7) | (msg->data[3] >> 1)) - 16384U;
|
||||
bool angle_active = GET_BIT(msg, 12U);
|
||||
const bool angle_violation = !rivian_angle_control || steer_angle_cmd_checks_vm(desired_angle, angle_active,
|
||||
RIVIAN_ANGLE_STEERING_LIMITS,
|
||||
RIVIAN_ANGLE_STEERING_PARAMS);
|
||||
if (angle_violation) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Steering control
|
||||
if (msg->addr == 0x120U) {
|
||||
int desired_torque = ((msg->data[2] << 3U) | (msg->data[3] >> 5U)) - 1024U;
|
||||
@@ -156,20 +194,24 @@ static safety_config rivian_init(uint16_t param) {
|
||||
// VDM_AdasSts: for canceling stock ACC
|
||||
// 0x120 = ACM_lkaHbaCmd, 0x321 = SCCM_WheelTouch, 0x162 = VDM_AdasSts
|
||||
static const CanMsg RIVIAN_TX_MSGS[] = {{0x120, 0, 8, .check_relay = true}, {0x321, 2, 7, .check_relay = true}, {0x162, 2, 8, .check_relay = true}};
|
||||
static const CanMsg RIVIAN_ANGLE_TX_MSGS[] = {{0x100, 0, 8, .check_relay = true}, {0x110, 0, 8, .check_relay = true}, {0x120, 0, 8, .check_relay = true}, {0x321, 2, 7, .check_relay = true}, {0x162, 2, 8, .check_relay = true}};
|
||||
// 0x160 = ACM_longitudinalRequest
|
||||
static const CanMsg RIVIAN_LONG_TX_MSGS[] = {{0x120, 0, 8, .check_relay = true}, {0x321, 2, 7, .check_relay = true}, {0x160, 0, 5, .check_relay = true}};
|
||||
static const CanMsg RIVIAN_ANGLE_LONG_TX_MSGS[] = {{0x100, 0, 8, .check_relay = true}, {0x110, 0, 8, .check_relay = true}, {0x120, 0, 8, .check_relay = true}, {0x321, 2, 7, .check_relay = true}, {0x160, 0, 5, .check_relay = true}};
|
||||
|
||||
static RxCheck rivian_rx_checks[] = {
|
||||
{.msg = {{0x208, 0, 8, 50U, .max_counter = 14U}, { 0 }, { 0 }}}, // ESP_Status (speed)
|
||||
{.msg = {{0x150, 0, 7, 50U, .max_counter = 14U}, { 0 }, { 0 }}}, // VDM_PropStatus (gas pedal & 2nd speed)
|
||||
{.msg = {{0x380, 0, 5, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // EPAS_SystemStatus (driver torque)
|
||||
{.msg = {{0x390, 0, 7, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // EPAS_AdasStatus (measured angle)
|
||||
{.msg = {{0x38f, 0, 6, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // iBESP2 (brakes)
|
||||
{.msg = {{0x100, 2, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // ACM_Status (cruise state)
|
||||
};
|
||||
|
||||
bool rivian_longitudinal = false;
|
||||
const int FLAG_RIVIAN_ANGLE_CONTROL = 2;
|
||||
rivian_angle_control = GET_FLAG(param, FLAG_RIVIAN_ANGLE_CONTROL);
|
||||
|
||||
SAFETY_UNUSED(param);
|
||||
#ifdef ALLOW_DEBUG
|
||||
const int FLAG_RIVIAN_LONG_CONTROL = 1;
|
||||
rivian_longitudinal = GET_FLAG(param, FLAG_RIVIAN_LONG_CONTROL);
|
||||
@@ -178,8 +220,17 @@ static safety_config rivian_init(uint16_t param) {
|
||||
// FIXME: cppcheck thinks that rivian_longitudinal is always false. This is not true
|
||||
// if ALLOW_DEBUG is defined but cppcheck is run without ALLOW_DEBUG
|
||||
// cppcheck-suppress knownConditionTrueFalse
|
||||
return rivian_longitudinal ? BUILD_SAFETY_CFG(rivian_rx_checks, RIVIAN_LONG_TX_MSGS) : \
|
||||
BUILD_SAFETY_CFG(rivian_rx_checks, RIVIAN_TX_MSGS);
|
||||
safety_config config;
|
||||
if (rivian_longitudinal && rivian_angle_control) {
|
||||
config = BUILD_SAFETY_CFG(rivian_rx_checks, RIVIAN_ANGLE_LONG_TX_MSGS);
|
||||
} else if (rivian_longitudinal) {
|
||||
config = BUILD_SAFETY_CFG(rivian_rx_checks, RIVIAN_LONG_TX_MSGS);
|
||||
} else if (rivian_angle_control) {
|
||||
config = BUILD_SAFETY_CFG(rivian_rx_checks, RIVIAN_ANGLE_TX_MSGS);
|
||||
} else {
|
||||
config = BUILD_SAFETY_CFG(rivian_rx_checks, RIVIAN_TX_MSGS);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
const safety_hooks rivian_hooks = {
|
||||
|
||||
@@ -987,6 +987,8 @@ class SafetyTest(SafetyTestBase):
|
||||
continue
|
||||
if attr.startswith('TestHyundaiCanfd') and current_test.startswith('TestHyundaiCanfd'):
|
||||
continue
|
||||
if attr.startswith('TestRivian') and current_test.startswith('TestRivian'):
|
||||
continue
|
||||
if attr.startswith('TestHyundaiCanCanfdBlended') and current_test.startswith('TestHyundaiCanCanfdBlended'):
|
||||
continue
|
||||
if {attr, current_test}.issubset({'TestHyundaiLongitudinalSafety', 'TestHyundaiLongitudinalSafetyCameraSCC',
|
||||
@@ -1003,6 +1005,13 @@ class SafetyTest(SafetyTestBase):
|
||||
if attr == 'TestHyundaiCanfdLKASteeringLongEV' and current_test.startswith('TestToyota'):
|
||||
tx = list(filter(lambda m: m[0] not in [0x160, ], tx))
|
||||
|
||||
# Rivian and Hyundai angle-control safety modes intentionally share these CAN address/bus pairs.
|
||||
rivian_angle_tests = {'TestRivianAngleSafety', 'TestRivianAngleLongitudinalSafety'}
|
||||
hyundai_alt_angle_test = 'TestHyundaiCanfdLKASteeringAltAngleLongEV'
|
||||
if ((current_test in rivian_angle_tests and attr == hyundai_alt_angle_test) or
|
||||
(attr in rivian_angle_tests and current_test == hyundai_alt_angle_test)):
|
||||
tx = list(filter(lambda m: [m[0], m[1]] not in ([0x100, 0], [0x110, 0]), tx))
|
||||
|
||||
# Volkswagen MQB longitudinal actuating message overlaps with the Subaru lateral actuating message
|
||||
if attr == 'TestVolkswagenMqbLongSafety' and current_test.startswith('TestSubaru'):
|
||||
tx = list(filter(lambda m: m[0] not in [0x122, ], tx))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from opendbc.car.ford.values import FordSafetyFlags
|
||||
from opendbc.car.hyundai.values import HyundaiSafetyFlags
|
||||
from opendbc.car.rivian.values import RivianSafetyFlags
|
||||
from opendbc.car.subaru.values import SubaruSafetyFlags
|
||||
from opendbc.car.toyota.values import ToyotaSafetyFlags
|
||||
from opendbc.car.structs import CarParams
|
||||
@@ -41,7 +42,7 @@ def is_steering_msg(mode, param, addr):
|
||||
elif mode == CarParams.SafetyModel.nissan:
|
||||
ret = addr == 0x169
|
||||
elif mode == CarParams.SafetyModel.rivian:
|
||||
ret = addr == 0x120
|
||||
ret = addr == (0x110 if param & RivianSafetyFlags.ANGLE_CONTROL else 0x120)
|
||||
elif mode == CarParams.SafetyModel.tesla:
|
||||
ret = addr == 0x488
|
||||
return ret
|
||||
@@ -94,7 +95,10 @@ def get_steer_value(mode, param, msg):
|
||||
angle = (msg.data[0] << 10) | (msg.data[1] << 2) | (msg.data[2] >> 6)
|
||||
angle = -angle + (1310 * 100)
|
||||
elif mode == CarParams.SafetyModel.rivian:
|
||||
torque = ((msg.data[2] << 3) | (msg.data[3] >> 5)) - 1024
|
||||
if param & RivianSafetyFlags.ANGLE_CONTROL:
|
||||
angle = ((msg.data[2] << 7) | (msg.data[3] >> 1)) - 16384
|
||||
else:
|
||||
torque = ((msg.data[2] << 3) | (msg.data[3] >> 5)) - 1024
|
||||
elif mode == CarParams.SafetyModel.tesla:
|
||||
angle = (((msg.data[0] & 0x7F) << 8) | (msg.data[1])) - 16384 # ceil(1638.35/0.1)
|
||||
return torque, angle
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
import unittest
|
||||
import numpy as np
|
||||
|
||||
from opendbc.car import STD_CARGO_KG, scale_rot_inertia, scale_tire_stiffness
|
||||
from opendbc.car.lateral import get_max_angle_delta_vm, get_max_angle_vm
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from opendbc.car.rivian.values import CarControllerParams
|
||||
from opendbc.safety.tests.libsafety import libsafety_py
|
||||
import opendbc.safety.tests.common as common
|
||||
from opendbc.safety.tests.common import CANPackerSafety
|
||||
@@ -9,6 +14,18 @@ from opendbc.car.rivian.values import RivianSafetyFlags
|
||||
from opendbc.car.rivian.riviancan import checksum as _checksum
|
||||
|
||||
|
||||
def get_safety_vm():
|
||||
CP = CarParams()
|
||||
CP.mass = 3206. + STD_CARGO_KG
|
||||
CP.wheelbase = 3.08
|
||||
CP.centerToFront = CP.wheelbase * 0.5
|
||||
CP.steerRatio = 15.2
|
||||
CP.steerRatioRear = 0.
|
||||
CP.rotationalInertia = scale_rot_inertia(CP.mass, CP.wheelbase)
|
||||
CP.tireStiffnessFront, CP.tireStiffnessRear = scale_tire_stiffness(CP.mass, CP.wheelbase, CP.centerToFront, 1.0)
|
||||
return VehicleModel(CP)
|
||||
|
||||
|
||||
def checksum(msg):
|
||||
addr, dat, bus = msg
|
||||
ret = bytearray(dat)
|
||||
@@ -18,18 +35,20 @@ def checksum(msg):
|
||||
ret[0] = _checksum(ret[1:], 0x1D, 0xB1)
|
||||
elif addr == 0x150:
|
||||
ret[0] = _checksum(ret[1:], 0x1D, 0x9A)
|
||||
elif addr == 0x162:
|
||||
ret[0] = _checksum(ret[1:], 0x1D, 0xD1)
|
||||
|
||||
return addr, ret, bus
|
||||
|
||||
|
||||
class TestRivianSafetyBase(common.CarSafetyTest, common.DriverTorqueSteeringSafetyTest, common.LongitudinalAccelSafetyTest,
|
||||
common.VehicleSpeedSafetyTest):
|
||||
class TestRivianSafetyBase(common.CarSafetyTest, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest,
|
||||
common.LongitudinalAccelSafetyTest, common.VehicleSpeedSafetyTest):
|
||||
|
||||
TX_MSGS = [[0x120, 0], [0x321, 2], [0x162, 2]]
|
||||
RELAY_MALFUNCTION_ADDRS = {0: (0x120,), 2: (0x321, 0x162)}
|
||||
FWD_BLACKLISTED_ADDRS = {0: [0x321, 0x162], 2: [0x120]}
|
||||
|
||||
MAX_TORQUE_LOOKUP = [9, 17], [350, 250]
|
||||
MAX_TORQUE_LOOKUP = [9, 25, 27], [385, 295, 275]
|
||||
DYNAMIC_MAX_TORQUE = True
|
||||
MAX_RATE_UP = 3
|
||||
MAX_RATE_DOWN = 5
|
||||
@@ -38,6 +57,10 @@ class TestRivianSafetyBase(common.CarSafetyTest, common.DriverTorqueSteeringSafe
|
||||
|
||||
DRIVER_TORQUE_ALLOWANCE = 100
|
||||
DRIVER_TORQUE_FACTOR = 2
|
||||
MIN_VALID_STEERING_FRAMES = 89
|
||||
MAX_INVALID_STEERING_FRAMES = 2
|
||||
|
||||
ANGLE = False
|
||||
|
||||
cnt_speed = 0
|
||||
cnt_speed_2 = 0
|
||||
@@ -109,6 +132,92 @@ class TestRivianSafetyBase(common.CarSafetyTest, common.DriverTorqueSteeringSafe
|
||||
self.assertFalse(self._rx(msg))
|
||||
self.assertFalse(self.safety.get_controls_allowed())
|
||||
|
||||
def test_angle_messages_require_harness_flag(self):
|
||||
if self.ANGLE:
|
||||
raise unittest.SkipTest("Angle harness safety configuration")
|
||||
self.assertFalse(self._tx(self.packer.make_can_msg_safety("ACM_SteeringControl", 0, {})))
|
||||
self.assertFalse(self._tx(self.packer.make_can_msg_safety("ACM_Status", 0, {})))
|
||||
|
||||
|
||||
class TestRivianAngleSafetyBase(TestRivianSafetyBase, common.AngleSteeringSafetyTest):
|
||||
ANGLE = True
|
||||
LONGITUDINAL = False
|
||||
TX_MSGS = [[0x100, 0], [0x110, 0], [0x120, 0], [0x321, 2], [0x162, 2]]
|
||||
RELAY_MALFUNCTION_ADDRS = {0: (0x100, 0x110, 0x120), 2: (0x321, 0x162)}
|
||||
FWD_BLACKLISTED_ADDRS = {0: [0x321, 0x162], 2: [0x100, 0x110, 0x120]}
|
||||
|
||||
STEER_ANGLE_MAX = 500
|
||||
DEG_TO_CAN = 10
|
||||
ANGLE_RATE_BP = None
|
||||
ANGLE_RATE_UP = None
|
||||
ANGLE_RATE_DOWN = None
|
||||
LATERAL_FREQUENCY = 100
|
||||
cnt_angle_cmd = 0
|
||||
|
||||
def _get_steer_cmd_angle_max(self, speed):
|
||||
return get_max_angle_vm(max(speed, 1), self.VM, CarControllerParams)
|
||||
|
||||
def _angle_cmd_msg(self, angle: float, enabled: bool, increment_timer: bool = True):
|
||||
values = {"ACM_SteeringAngleRequest": angle, "ACM_EacEnabled": enabled}
|
||||
if increment_timer:
|
||||
self.safety.set_timer(self.cnt_angle_cmd * int(1e6 / self.LATERAL_FREQUENCY))
|
||||
self.__class__.cnt_angle_cmd += 1
|
||||
return self.packer.make_can_msg_safety("ACM_SteeringControl", 0, values)
|
||||
|
||||
def _angle_meas_msg(self, angle: float):
|
||||
return self.packer.make_can_msg_safety("EPAS_AdasStatus", 0, {"EPAS_InternalSas": angle})
|
||||
|
||||
def test_angle_cmd_when_enabled(self):
|
||||
# The VM-based lateral acceleration and jerk limits are exercised below.
|
||||
pass
|
||||
|
||||
def _can_to_deg(self, can_value):
|
||||
return can_value / self.DEG_TO_CAN
|
||||
|
||||
@staticmethod
|
||||
def _round_speed(speed):
|
||||
speed_kph_can = round((speed + 1) * 3.6 / 0.01) * 0.01
|
||||
stored = round(speed_kph_can / 3.6 * 1000)
|
||||
return max(stored / 1000.0 - 1.0, 1.0)
|
||||
|
||||
def test_lateral_accel_limit(self):
|
||||
for speed in np.linspace(0, 40, 100):
|
||||
speed = max(self._round_speed(speed), 1)
|
||||
for sign in (-1, 1):
|
||||
self.safety.set_controls_allowed(True)
|
||||
self._reset_speed_measurement(speed + 1)
|
||||
max_angle_can = int(get_max_angle_vm(speed, self.VM, CarControllerParams) * self.DEG_TO_CAN) + 1
|
||||
max_angle_can = min(max_angle_can, self.STEER_ANGLE_MAX * self.DEG_TO_CAN)
|
||||
|
||||
self.safety.set_desired_angle_last(max_angle_can * sign)
|
||||
self.assertTrue(self._tx(self._angle_cmd_msg(self._can_to_deg(max_angle_can) * sign, True)))
|
||||
|
||||
above_can = max_angle_can + 1
|
||||
above_deg = self._can_to_deg(above_can) * sign
|
||||
self._tx(self._angle_cmd_msg(above_deg, True))
|
||||
should_tx = above_can > self.STEER_ANGLE_MAX * self.DEG_TO_CAN
|
||||
self.assertEqual(should_tx, self._tx(self._angle_cmd_msg(above_deg, True)))
|
||||
|
||||
def test_lateral_jerk_limit(self):
|
||||
for speed in np.linspace(0, 40, 100):
|
||||
speed = max(self._round_speed(speed), 1)
|
||||
for sign in (-1, 1):
|
||||
self.safety.set_controls_allowed(True)
|
||||
self._reset_speed_measurement(speed + 1)
|
||||
self._tx(self._angle_cmd_msg(0, True))
|
||||
max_delta_can = int(get_max_angle_delta_vm(speed, self.VM, CarControllerParams) * self.DEG_TO_CAN) + 1
|
||||
|
||||
self.assertTrue(self._tx(self._angle_cmd_msg(self._can_to_deg(max_delta_can) * sign, True)))
|
||||
self.assertTrue(self._tx(self._angle_cmd_msg(self._can_to_deg(max_delta_can) * sign, True)))
|
||||
self.assertTrue(self._tx(self._angle_cmd_msg(0, True)))
|
||||
|
||||
above_can = max_delta_can + 1
|
||||
self.assertFalse(self._tx(self._angle_cmd_msg(self._can_to_deg(above_can) * sign, True)))
|
||||
self.safety.set_desired_angle_last(above_can * sign)
|
||||
self.assertTrue(self._tx(self._angle_cmd_msg(self._can_to_deg(above_can) * sign, True)))
|
||||
self.assertFalse(self._tx(self._angle_cmd_msg(0, True)))
|
||||
self.assertTrue(self._tx(self._angle_cmd_msg(0, True)))
|
||||
|
||||
|
||||
class TestRivianStockSafety(TestRivianSafetyBase):
|
||||
|
||||
@@ -142,5 +251,29 @@ class TestRivianLongitudinalSafety(TestRivianSafetyBase):
|
||||
self.safety.init_tests()
|
||||
|
||||
|
||||
class TestRivianAngleSafety(TestRivianAngleSafetyBase):
|
||||
def setUp(self):
|
||||
self.VM = get_safety_vm()
|
||||
self.packer = CANPackerSafety("rivian_primary_actuator")
|
||||
self.safety = libsafety_py.libsafety
|
||||
self.safety.set_safety_hooks(CarParams.SafetyModel.rivian, RivianSafetyFlags.ANGLE_CONTROL)
|
||||
self.safety.init_tests()
|
||||
|
||||
|
||||
class TestRivianAngleLongitudinalSafety(TestRivianAngleSafetyBase):
|
||||
LONGITUDINAL = True
|
||||
TX_MSGS = [[0x100, 0], [0x110, 0], [0x120, 0], [0x321, 2], [0x160, 0]]
|
||||
RELAY_MALFUNCTION_ADDRS = {0: (0x100, 0x110, 0x120, 0x160), 2: (0x321,)}
|
||||
FWD_BLACKLISTED_ADDRS = {0: [0x321], 2: [0x100, 0x110, 0x120, 0x160]}
|
||||
|
||||
def setUp(self):
|
||||
self.VM = get_safety_vm()
|
||||
self.packer = CANPackerSafety("rivian_primary_actuator")
|
||||
self.safety = libsafety_py.libsafety
|
||||
flags = RivianSafetyFlags.ANGLE_CONTROL | RivianSafetyFlags.LONG_CONTROL
|
||||
self.safety.set_safety_hooks(CarParams.SafetyModel.rivian, flags)
|
||||
self.safety.init_tests()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -4,6 +4,7 @@ from opendbc.car import DT_CTRL, structs
|
||||
from opendbc.car.chrysler.values import RAM_DT
|
||||
from opendbc.car.gm.values import CAR as GM_CAR, GMFlags, SDGM_CAR
|
||||
from opendbc.car.interfaces import MAX_CTRL_SPEED
|
||||
from opendbc.car.rivian.values import RivianFlags
|
||||
|
||||
from openpilot.selfdrive.selfdrived.events import Events
|
||||
|
||||
@@ -65,6 +66,21 @@ class CarSpecificEvents:
|
||||
self.gm_low_speed_alert_shown = False
|
||||
self.no_steer_warning = False
|
||||
self.silent_steer_warning = True
|
||||
self.rivian = self.CP.brand == "rivian"
|
||||
self.rivian_angle_harness = self.rivian and bool(self.CP.flags & RivianFlags.ANGLE_HARNESS)
|
||||
self.rivian_status_frame = 0
|
||||
self.rivian_angle_saturated = False
|
||||
self.rivian_toi_recovery_failed = False
|
||||
self.rivian_status_params = None
|
||||
self.rivian_angle_params = None
|
||||
if self.rivian:
|
||||
try:
|
||||
from openpilot.common.params import Params
|
||||
self.rivian_status_params = Params(memory=True)
|
||||
if self.rivian_angle_harness:
|
||||
self.rivian_angle_params = self.rivian_status_params
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def update(self, CS: car.CarState, CS_prev: car.CarState, CC: car.CarControl):
|
||||
extra_gears = BRAND_EXTRA_GEARS.get(self.CP.brand, None)
|
||||
@@ -186,6 +202,18 @@ class CarSpecificEvents:
|
||||
else:
|
||||
events = self.create_common_events(CS, CS_prev, extra_gears=extra_gears)
|
||||
|
||||
if self.rivian:
|
||||
self.rivian_status_frame += 1
|
||||
if self.rivian_status_params is not None and self.rivian_status_frame % 5 == 0:
|
||||
self.rivian_toi_recovery_failed = self.rivian_status_params.get_bool("RivianToiRecoveryFailed")
|
||||
if self.rivian_angle_harness:
|
||||
self.rivian_angle_saturated = self.rivian_status_params.get_bool("RivianAngleSaturated")
|
||||
if self.rivian_toi_recovery_failed:
|
||||
events.add(EventName.steerTempUnavailable)
|
||||
if self.rivian_angle_harness:
|
||||
if self.rivian_angle_saturated:
|
||||
events.add(EventName.steerSaturated)
|
||||
|
||||
return events
|
||||
|
||||
def create_common_events(self, CS: structs.CarState, CS_prev: car.CarState, extra_gears: list | None = None, pcm_enable=True,
|
||||
|
||||
@@ -220,7 +220,10 @@ class Car:
|
||||
|
||||
self.starpilot_card = StarPilotCard(self.CP, self.FPCP)
|
||||
|
||||
self.sm = self.sm.extend(['starpilotOnroadEvents', 'starpilotPlan', 'starpilotSelfdriveState', 'liveCalibration', 'selfdriveState'])
|
||||
starpilot_services = ['starpilotOnroadEvents', 'starpilotPlan', 'starpilotSelfdriveState', 'liveCalibration', 'selfdriveState']
|
||||
if self.CP.brand == "rivian":
|
||||
starpilot_services.append('liveParameters')
|
||||
self.sm = self.sm.extend(starpilot_services)
|
||||
self.pm = self.pm.extend(['starpilotCarState'])
|
||||
|
||||
def _inject_favorite_virtual_cruise_events(self, CS: car.CarState) -> None:
|
||||
@@ -388,6 +391,10 @@ class Car:
|
||||
now_nanos = self.can_log_mono_time if REPLAY else int(time.monotonic() * 1e9)
|
||||
self._update_redneck_cruise(CS, CC)
|
||||
self._update_openpilot_lead_state(CC)
|
||||
if self.CP.brand == "rivian" and self.sm.all_checks(['liveParameters']) and hasattr(self.CI.CC, 'update_live_params'):
|
||||
live_params = self.sm['liveParameters']
|
||||
self.CI.CC.update_live_params(live_params.roll, live_params.angleOffsetDeg,
|
||||
live_params.stiffnessFactor, live_params.steerRatio)
|
||||
self.last_actuators_output, can_sends = self.CI.apply(CC, now_nanos, self.starpilot_toggles)
|
||||
self.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=CS.canValid))
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from types import ModuleType, SimpleNamespace
|
||||
|
||||
|
||||
MODULE_PATH = Path(__file__).resolve().parents[1] / "car_specific.py"
|
||||
|
||||
|
||||
class FakeEvents:
|
||||
def __init__(self):
|
||||
self.names = []
|
||||
|
||||
def add(self, event):
|
||||
self.names.append(event)
|
||||
|
||||
|
||||
def load_car_specific(monkeypatch):
|
||||
messaging = ModuleType("cereal.messaging")
|
||||
messaging.SubMaster = object
|
||||
monkeypatch.setitem(sys.modules, "cereal.messaging", messaging)
|
||||
|
||||
events = ModuleType("openpilot.selfdrive.selfdrived.events")
|
||||
events.Events = FakeEvents
|
||||
monkeypatch.setitem(sys.modules, "openpilot.selfdrive.selfdrived.events", events)
|
||||
|
||||
spec = importlib.util.spec_from_file_location("rivian_car_specific_under_test", MODULE_PATH)
|
||||
assert spec is not None and spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_angle_saturation_raises_stock_take_control_event(monkeypatch):
|
||||
module = load_car_specific(monkeypatch)
|
||||
handler = module.CarSpecificEvents(SimpleNamespace(brand="rivian", flags=1))
|
||||
handler.rivian_status_params = SimpleNamespace(get_bool=lambda key: key == "RivianAngleSaturated")
|
||||
handler.rivian_angle_params = handler.rivian_status_params
|
||||
handler.create_common_events = lambda *args, **kwargs: FakeEvents()
|
||||
|
||||
events = None
|
||||
for _ in range(5):
|
||||
events = handler.update(SimpleNamespace(), SimpleNamespace(), SimpleNamespace())
|
||||
|
||||
assert module.EventName.steerSaturated in events.names
|
||||
|
||||
|
||||
def test_saturation_bridge_is_inert_without_angle_harness(monkeypatch):
|
||||
module = load_car_specific(monkeypatch)
|
||||
handler = module.CarSpecificEvents(SimpleNamespace(brand="rivian", flags=0))
|
||||
handler.create_common_events = lambda *args, **kwargs: FakeEvents()
|
||||
|
||||
events = handler.update(SimpleNamespace(), SimpleNamespace(), SimpleNamespace())
|
||||
|
||||
assert module.EventName.steerSaturated not in events.names
|
||||
|
||||
|
||||
def test_toi_recovery_timeout_raises_temporary_steering_event(monkeypatch):
|
||||
module = load_car_specific(monkeypatch)
|
||||
handler = module.CarSpecificEvents(SimpleNamespace(brand="rivian", flags=0))
|
||||
handler.rivian_status_params = SimpleNamespace(get_bool=lambda key: key == "RivianToiRecoveryFailed")
|
||||
handler.create_common_events = lambda *args, **kwargs: FakeEvents()
|
||||
|
||||
events = None
|
||||
for _ in range(5):
|
||||
events = handler.update(SimpleNamespace(), SimpleNamespace(), SimpleNamespace())
|
||||
|
||||
assert module.EventName.steerTempUnavailable in events.names
|
||||
@@ -26,7 +26,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
|
||||
)
|
||||
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
|
||||
from openpilot.selfdrive.car.cruise_state import should_cancel_stock_cruise
|
||||
from openpilot.selfdrive.modeld.modeld import LAT_SMOOTH_SECONDS
|
||||
from openpilot.selfdrive.modeld.modeld import LAT_SMOOTH_SECONDS, get_car_lateral_smooth_seconds
|
||||
from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose
|
||||
|
||||
from openpilot.starpilot.common.starpilot_variables import get_starpilot_toggles
|
||||
@@ -35,6 +35,7 @@ from openpilot.starpilot.controls.lib.neural_network_feedforward import LatContr
|
||||
State = log.SelfdriveState.OpenpilotState
|
||||
LaneChangeState = log.LaneChangeState
|
||||
LaneChangeDirection = log.LaneChangeDirection
|
||||
LateralControlMode = car.CarControl.Actuators.LateralControlMode
|
||||
|
||||
ACTUATOR_FIELDS = tuple(car.CarControl.Actuators.schema.fields.keys())
|
||||
|
||||
@@ -216,6 +217,19 @@ def get_plan_reach(model_v2) -> float:
|
||||
return xs[-1] if len(xs) else 0.0
|
||||
|
||||
|
||||
def get_control_lateral_smooth_seconds(brand: str, v_ego: float, vehicle_smooth_seconds: float) -> float:
|
||||
if brand != "rivian":
|
||||
return LAT_SMOOTH_SECONDS
|
||||
return get_car_lateral_smooth_seconds(brand, v_ego, vehicle_smooth_seconds)
|
||||
|
||||
|
||||
def turn_lead_allowed(brand: str, lateral_control_mode: car.CarControl.Actuators.LateralControlMode) -> bool:
|
||||
# Torque steering mechanically damps the turn-lead fade. A direct angle
|
||||
# controller follows the resulting lead/catch-up cycle literally, which can
|
||||
# reverse the wheel command several times during one turn initiation.
|
||||
return brand != "rivian" or lateral_control_mode != LateralControlMode.angle
|
||||
|
||||
|
||||
# Turn-initiation lead. The model's action and the fixed 4/7 m probes are anchored in
|
||||
# METERS, so the seconds of warning they give shrinks with speed — at 12 mph a corner
|
||||
# enters the 7 m window only ~1.3 s out, too late to wind the wheel, which is why every
|
||||
@@ -577,7 +591,9 @@ class Controls:
|
||||
# bend is not a turn. The model-oppose veto is defense-in-depth for the fade-in
|
||||
# edge: a model actively steering against the blinker is correcting something the
|
||||
# lead must not fight (see the constants comment for the 2026-07-19 failures).
|
||||
if (CC.latActive and blinker_dir != 0.0 and
|
||||
lateral_control_mode = self.sm['carOutput'].actuatorsOutput.lateralControlMode
|
||||
if (turn_lead_allowed(self.CP.brand, lateral_control_mode) and
|
||||
CC.latActive and blinker_dir != 0.0 and
|
||||
model_v2.meta.laneChangeState == LaneChangeState.off and
|
||||
TURN_LEAD_MIN_SPEED <= CS.vEgo < TURN_LEAD_MAX_SPEED and
|
||||
new_desired_curvature * blinker_dir > -TURN_LEAD_MODEL_OPPOSE):
|
||||
@@ -650,7 +666,8 @@ class Controls:
|
||||
|
||||
self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll,
|
||||
jerk_factor)
|
||||
lat_delay = self.sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS
|
||||
lat_smooth_seconds = get_control_lateral_smooth_seconds(self.CP.brand, CS.vEgo, self.CP.lateralSmoothSeconds)
|
||||
lat_delay = self.sm["liveDelay"].lateralDelay + lat_smooth_seconds
|
||||
|
||||
actuators.curvature = self.desired_curvature
|
||||
steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from cereal import car
|
||||
|
||||
import pytest
|
||||
|
||||
from openpilot.selfdrive.controls.controlsd import get_control_lateral_smooth_seconds, turn_lead_allowed
|
||||
|
||||
|
||||
LateralControlMode = car.CarControl.Actuators.LateralControlMode
|
||||
|
||||
|
||||
def test_turn_lead_is_suppressed_only_during_applied_angle_control():
|
||||
assert not turn_lead_allowed("rivian", LateralControlMode.angle)
|
||||
assert turn_lead_allowed("rivian", LateralControlMode.torque)
|
||||
assert turn_lead_allowed("rivian", LateralControlMode.torqueRecovering)
|
||||
assert turn_lead_allowed("rivian", LateralControlMode.inactive)
|
||||
assert turn_lead_allowed("ford", LateralControlMode.angle)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("v_ego", [0.0, 5.0, 30.0])
|
||||
def test_non_rivian_control_smoothing_matches_starpilot(v_ego):
|
||||
assert get_control_lateral_smooth_seconds("toyota", v_ego, 0.0) == 0.1
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("v_ego", "expected"), [
|
||||
(0.0, 0.4),
|
||||
(5.0, 0.2),
|
||||
(30.0, 0.0),
|
||||
])
|
||||
def test_rivian_control_smoothing_remains_speed_scheduled(v_ego, expected):
|
||||
assert get_control_lateral_smooth_seconds("rivian", v_ego, 0.4) == pytest.approx(expected)
|
||||
@@ -63,6 +63,17 @@ def _model_smooth_seconds(params, key, default):
|
||||
value = params.get_float(key, return_default=True, default=default)
|
||||
return round(min(max(value, SMOOTH_SECONDS_STEP), 2.0) / SMOOTH_SECONDS_STEP) * SMOOTH_SECONDS_STEP
|
||||
MIN_LAT_CONTROL_SPEED = 0.3
|
||||
LAT_SMOOTH_BP = [2.0, 8.0]
|
||||
|
||||
|
||||
def get_lateral_smooth_seconds(v_ego: float, maximum: float = 0.0) -> float:
|
||||
return float(np.interp(v_ego, LAT_SMOOTH_BP, [maximum, 0.0]))
|
||||
|
||||
|
||||
def get_car_lateral_smooth_seconds(brand: str, v_ego: float, maximum: float) -> float:
|
||||
if brand == "rivian":
|
||||
return get_lateral_smooth_seconds(v_ego, maximum)
|
||||
return maximum
|
||||
|
||||
|
||||
def _get_param_str(params: Params, key: str, default: str = "") -> str:
|
||||
@@ -601,13 +612,15 @@ def main(demo=False):
|
||||
meta_extra = meta_main
|
||||
|
||||
sm.update(0)
|
||||
lat_smooth_seconds = _model_smooth_seconds(params, "LatSmoothSeconds", LAT_SMOOTH_SECONDS)
|
||||
long_smooth_seconds = _model_smooth_seconds(params, "LongSmoothSeconds", LONG_SMOOTH_SECONDS)
|
||||
long_delay = CP.longitudinalActuatorDelay + long_smooth_seconds
|
||||
desire = DH.desire
|
||||
is_rhd = sm["driverMonitoringState"].isRHD
|
||||
frame_id = sm["roadCameraState"].frameId
|
||||
v_ego = max(sm["carState"].vEgo, 0.)
|
||||
lat_smooth_default = CP.lateralSmoothSeconds if CP.brand == "rivian" else LAT_SMOOTH_SECONDS
|
||||
lat_smooth_maximum = _model_smooth_seconds(params, "LatSmoothSeconds", lat_smooth_default)
|
||||
lat_smooth_seconds = get_car_lateral_smooth_seconds(CP.brand, v_ego, lat_smooth_maximum)
|
||||
lat_delay = sm["liveDelay"].lateralDelay + lat_smooth_seconds
|
||||
lateral_control_params = np.array([v_ego, lat_delay], dtype=np.float32)
|
||||
if sm.frame % 60 == 0:
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import pytest
|
||||
|
||||
from openpilot.selfdrive.modeld.modeld import get_car_lateral_smooth_seconds, get_lateral_smooth_seconds
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("v_ego", "expected"), [
|
||||
(0.0, 0.4),
|
||||
(2.0, 0.4),
|
||||
(5.0, 0.2),
|
||||
(8.0, 0.0),
|
||||
(30.0, 0.0),
|
||||
])
|
||||
def test_lateral_smoothing_tapers_with_speed(v_ego, expected):
|
||||
assert get_lateral_smooth_seconds(v_ego, 0.4) == pytest.approx(expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("v_ego", [0.0, 5.0, 30.0])
|
||||
def test_default_lateral_smoothing_is_disabled(v_ego):
|
||||
assert get_lateral_smooth_seconds(v_ego) == 0.0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("v_ego", [0.0, 5.0, 30.0])
|
||||
def test_non_rivian_cars_keep_configured_starpilot_smoothing(v_ego):
|
||||
assert get_car_lateral_smooth_seconds("toyota", v_ego, 0.4) == 0.4
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("v_ego", "maximum", "expected"), [
|
||||
(0.0, 0.4, 0.4),
|
||||
(5.0, 0.4, 0.2),
|
||||
(30.0, 0.4, 0.0),
|
||||
(0.0, 0.0, 0.0),
|
||||
])
|
||||
def test_rivian_uses_configured_smoothing(v_ego, maximum, expected):
|
||||
assert get_car_lateral_smooth_seconds("rivian", v_ego, maximum) == pytest.approx(expected)
|
||||
@@ -11,6 +11,7 @@ from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params, UnknownKeyName
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive.pandad.rivian_long_flasher import prepare_rivian_bridge
|
||||
|
||||
|
||||
def get_selected_firmware_name(app_fn: str, remote_start: bool, hkg_remote_start: bool, ignore_ignition_line: bool) -> str:
|
||||
@@ -159,6 +160,13 @@ def main() -> None:
|
||||
|
||||
cloudlog.info(f"{len(panda_serials)} panda(s) found, connecting - {panda_serials}")
|
||||
|
||||
# Update and reserve the Rivian harness bridge before managing internal Pandas.
|
||||
bridge_serials = prepare_rivian_bridge(panda_serials)
|
||||
panda_serials = [serial for serial in panda_serials if serial not in bridge_serials]
|
||||
if len(panda_serials) == 0:
|
||||
no_internal_panda_count += 1
|
||||
continue
|
||||
|
||||
# Flash pandas
|
||||
pandas: list[Panda] = []
|
||||
remote_start = get_remote_start_boots_comma(params)
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Firmware management for the Rivian Gen 1 harness bridge."""
|
||||
|
||||
import os
|
||||
from itertools import accumulate
|
||||
|
||||
from cereal import car
|
||||
from panda import Panda
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
FW_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "rivian_long_fw.bin.signed")
|
||||
SECTOR_SIZES = [0x4000] * 4 + [0x10000] + [0x20000] * 11
|
||||
|
||||
|
||||
def _is_rivian() -> bool:
|
||||
params = Params()
|
||||
|
||||
for key in ("CarParamsPersistent", "CarParamsCache", "CarParamsPrevRoute"):
|
||||
cp_bytes = params.get(key)
|
||||
if cp_bytes is None:
|
||||
continue
|
||||
try:
|
||||
with car.CarParams.from_bytes(cp_bytes) as CP:
|
||||
if CP.brand == "rivian":
|
||||
return True
|
||||
except Exception:
|
||||
cloudlog.exception(f"Unable to read {key} while identifying Rivian bridge")
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def is_rivian_vehicle() -> bool:
|
||||
return _is_rivian()
|
||||
|
||||
|
||||
def _flash_static(handle, code: bytes) -> None:
|
||||
assert Panda.flasher_present(handle)
|
||||
last_sector = next((i + 1 for i, value in enumerate(accumulate(SECTOR_SIZES[1:])) if value > len(code)), -1)
|
||||
assert 1 <= last_sector < 7, "Invalid Rivian bridge firmware size"
|
||||
|
||||
handle.controlWrite(Panda.REQUEST_IN, 0xB1, 0, 0, b'')
|
||||
for sector in range(1, last_sector + 1):
|
||||
handle.controlWrite(Panda.REQUEST_IN, 0xB2, sector, 0, b'')
|
||||
for offset in range(0, len(code), 0x10):
|
||||
handle.bulkWrite(2, code[offset:offset + 0x10])
|
||||
try:
|
||||
handle.controlWrite(Panda.REQUEST_IN, 0xD8, 0, 0, b'', expect_disconnect=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _flash_panda(panda: Panda) -> None:
|
||||
expected_signature = Panda.get_signature_from_firmware(FW_PATH)
|
||||
if not panda.bootstub and panda.get_signature() == expected_signature:
|
||||
cloudlog.info(f"Rivian bridge {panda.get_usb_serial()} already up to date")
|
||||
return
|
||||
|
||||
cloudlog.info(f"Flashing Rivian Extreme harness bridge {panda.get_usb_serial()}")
|
||||
with open(FW_PATH, "rb") as firmware:
|
||||
code = firmware.read()
|
||||
|
||||
if not panda.bootstub:
|
||||
# Old F4 firmware cannot use Panda.reset(); enter its bootstub directly.
|
||||
try:
|
||||
panda._handle.controlWrite(Panda.REQUEST_IN, 0xD1, 1, 0, b'', timeout=15000, expect_disconnect=True)
|
||||
except Exception:
|
||||
pass
|
||||
panda.close()
|
||||
panda.reconnect()
|
||||
|
||||
_flash_static(panda._handle, code)
|
||||
panda.reconnect()
|
||||
cloudlog.info(f"Successfully flashed Rivian Extreme harness bridge {panda.get_usb_serial()}")
|
||||
|
||||
|
||||
def is_rivian_bridge_panda(panda: Panda, rivian: bool | None = None) -> bool:
|
||||
if panda.is_internal() or panda.get_type() != Panda.HW_TYPE_BLACK:
|
||||
return False
|
||||
if rivian is None:
|
||||
rivian = _is_rivian()
|
||||
if rivian or panda.bootstub:
|
||||
return rivian
|
||||
try:
|
||||
expected_signature = Panda.get_signature_from_firmware(FW_PATH)
|
||||
return panda.get_signature() == expected_signature
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def prepare_rivian_bridge(panda_serials: list[str]) -> set[str]:
|
||||
"""Identify bridge serials which must not be passed to normal Panda management."""
|
||||
firmware_available = os.path.isfile(FW_PATH)
|
||||
if not firmware_available:
|
||||
cloudlog.error(f"Rivian bridge firmware not found at {FW_PATH}")
|
||||
|
||||
rivian = _is_rivian()
|
||||
usb_serials = set(Panda.usb_list())
|
||||
bridge_serials: set[str] = set()
|
||||
|
||||
for serial in panda_serials:
|
||||
if serial not in usb_serials:
|
||||
continue
|
||||
panda = None
|
||||
try:
|
||||
panda = Panda(serial)
|
||||
if panda.is_internal() or panda.get_type() != Panda.HW_TYPE_BLACK:
|
||||
continue
|
||||
|
||||
bridge_confirmed = is_rivian_bridge_panda(panda, rivian)
|
||||
if not bridge_confirmed:
|
||||
continue
|
||||
|
||||
bridge_serials.add(serial)
|
||||
if rivian and firmware_available:
|
||||
_flash_panda(panda)
|
||||
except Exception:
|
||||
cloudlog.exception(f"Failed to prepare Rivian Extreme harness bridge {serial}")
|
||||
finally:
|
||||
if panda is not None:
|
||||
panda.close()
|
||||
|
||||
return bridge_serials
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
prepare_rivian_bridge(Panda.list())
|
||||
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch # noqa: TID251 - mocks are required to guarantee no hardware access
|
||||
|
||||
|
||||
MODULE_PATH = Path(__file__).parents[1] / "rivian_long_flasher.py"
|
||||
MODULE_SPEC = importlib.util.spec_from_file_location("rivian_long_flasher", MODULE_PATH)
|
||||
assert MODULE_SPEC is not None and MODULE_SPEC.loader is not None
|
||||
flasher = importlib.util.module_from_spec(MODULE_SPEC)
|
||||
MODULE_SPEC.loader.exec_module(flasher)
|
||||
|
||||
|
||||
def _external_black_panda(signature: bytes = b"current", bootstub: bool = False):
|
||||
panda = MagicMock()
|
||||
panda.is_internal.return_value = False
|
||||
panda.get_type.return_value = b"\x03"
|
||||
panda.get_signature.return_value = signature
|
||||
panda.bootstub = bootstub
|
||||
return panda
|
||||
|
||||
|
||||
def test_current_rivian_bridge_uses_bridge_flash_path():
|
||||
panda = _external_black_panda(signature=b"expected")
|
||||
with patch.object(flasher, "_is_rivian", return_value=True), \
|
||||
patch.object(flasher.os.path, "isfile", return_value=True), \
|
||||
patch.object(flasher, "Panda", wraps=flasher.Panda) as panda_class, \
|
||||
patch.object(flasher, "_flash_panda") as flash_panda:
|
||||
panda_class.return_value = panda
|
||||
panda_class.HW_TYPE_BLACK = b"\x03"
|
||||
panda_class.usb_list.return_value = ["bridge"]
|
||||
panda_class.get_signature_from_firmware.return_value = b"expected"
|
||||
|
||||
assert flasher.prepare_rivian_bridge(["internal", "bridge"]) == {"bridge"}
|
||||
flash_panda.assert_called_once_with(panda)
|
||||
panda.close.assert_called_once()
|
||||
|
||||
|
||||
def test_outdated_rivian_bridge_uses_bridge_flash_path():
|
||||
panda = _external_black_panda(signature=b"unexpected")
|
||||
with patch.object(flasher, "_is_rivian", return_value=True), \
|
||||
patch.object(flasher.os.path, "isfile", return_value=True), \
|
||||
patch.object(flasher, "Panda", wraps=flasher.Panda) as panda_class, \
|
||||
patch.object(flasher, "_flash_panda") as flash_panda:
|
||||
panda_class.return_value = panda
|
||||
panda_class.HW_TYPE_BLACK = b"\x03"
|
||||
panda_class.usb_list.return_value = ["bridge"]
|
||||
panda_class.get_signature_from_firmware.return_value = b"expected"
|
||||
|
||||
assert flasher.prepare_rivian_bridge(["internal", "bridge"]) == {"bridge"}
|
||||
flash_panda.assert_called_once_with(panda)
|
||||
|
||||
|
||||
def test_non_rivian_matching_bridge_is_reserved_without_flashing():
|
||||
panda = _external_black_panda(signature=b"expected")
|
||||
with patch.object(flasher, "_is_rivian", return_value=False), \
|
||||
patch.object(flasher.os.path, "isfile", return_value=True), \
|
||||
patch.object(flasher, "Panda", wraps=flasher.Panda) as panda_class, \
|
||||
patch.object(flasher, "_flash_panda") as flash_panda:
|
||||
panda_class.return_value = panda
|
||||
panda_class.HW_TYPE_BLACK = b"\x03"
|
||||
panda_class.usb_list.return_value = ["bridge"]
|
||||
panda_class.get_signature_from_firmware.return_value = b"expected"
|
||||
|
||||
assert flasher.prepare_rivian_bridge(["internal", "bridge"]) == {"bridge"}
|
||||
flash_panda.assert_not_called()
|
||||
|
||||
|
||||
def test_non_rivian_external_black_panda_is_not_misidentified():
|
||||
panda = _external_black_panda(signature=b"unexpected")
|
||||
with patch.object(flasher, "_is_rivian", return_value=False), \
|
||||
patch.object(flasher.os.path, "isfile", return_value=True), \
|
||||
patch.object(flasher, "Panda", wraps=flasher.Panda) as panda_class, \
|
||||
patch.object(flasher, "_flash_panda") as flash_panda:
|
||||
panda_class.return_value = panda
|
||||
panda_class.HW_TYPE_BLACK = b"\x03"
|
||||
panda_class.usb_list.return_value = ["external"]
|
||||
panda_class.get_signature_from_firmware.return_value = b"expected"
|
||||
|
||||
assert flasher.prepare_rivian_bridge(["internal", "external"]) == set()
|
||||
flash_panda.assert_not_called()
|
||||
@@ -4,6 +4,7 @@ import pyray as rl
|
||||
from dataclasses import dataclass
|
||||
from openpilot.common.constants import CV
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.torque_bar import TorqueBar
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.rivian_lateral_mode import rivian_lateral_mode
|
||||
from openpilot.selfdrive.ui.mici.onroad.speed_limit_utils import resolve_display_speed_limit_ms
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.navigation_card import NavigationCardRenderer
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
|
||||
@@ -162,6 +163,7 @@ class HudRenderer(Widget):
|
||||
|
||||
self._wheel_alpha_filter = FirstOrderFilter(0, 0.05, 1 / gui_app.target_fps)
|
||||
self._wheel_y_filter = FirstOrderFilter(0, 0.1, 1 / gui_app.target_fps)
|
||||
self._wheel_tint: rl.Color | None = None
|
||||
|
||||
self._set_speed_alpha_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps)
|
||||
self._egpu_alpha_filter = FirstOrderFilter(0.0, 0.1, 1 / gui_app.target_fps)
|
||||
@@ -185,10 +187,13 @@ class HudRenderer(Widget):
|
||||
self.is_cruise_set = False
|
||||
self.set_speed = SET_SPEED_NA
|
||||
self.speed = 0.0
|
||||
self._wheel_tint = None
|
||||
return
|
||||
|
||||
controls_state = sm['controlsState']
|
||||
car_state = sm['carState']
|
||||
rivian_lateral_mode.update()
|
||||
self._wheel_tint = rivian_lateral_mode.wheel_tint
|
||||
|
||||
v_cruise_cluster = car_state.vCruiseCluster
|
||||
set_speed = (
|
||||
@@ -380,7 +385,8 @@ class HudRenderer(Widget):
|
||||
origin = (wheel_txt.width / 2, wheel_txt.height / 2)
|
||||
|
||||
# color and draw
|
||||
color = rl.Color(255, 255, 255, int(self._wheel_alpha_filter.x))
|
||||
base_color = self._wheel_tint if self._wheel_tint is not None and not self._show_wheel_critical else rl.Color(255, 255, 255, 255)
|
||||
color = rl.Color(base_color.r, base_color.g, base_color.b, int(self._wheel_alpha_filter.x))
|
||||
rl.draw_texture_pro(wheel_txt, src_rect, dest_rect, origin, rotation, color)
|
||||
|
||||
if self._show_wheel_critical:
|
||||
|
||||
@@ -25,6 +25,7 @@ class ExpButton(Widget):
|
||||
|
||||
self._white_color: rl.Color = rl.Color(255, 255, 255, 255)
|
||||
self._black_bg: rl.Color = rl.Color(0, 0, 0, 166)
|
||||
self.wheel_tint: rl.Color | None = None
|
||||
self._txt_wheel: rl.Texture = gui_app.texture('icons/chffr_wheel.png', icon_size, icon_size)
|
||||
self._txt_exp: rl.Texture = gui_app.texture('icons/experimental.png', icon_size, icon_size)
|
||||
self._rect = rl.Rectangle(0, 0, button_size, button_size)
|
||||
@@ -97,17 +98,31 @@ class ExpButton(Widget):
|
||||
|
||||
self._white_color.a = 180 if self.is_pressed or not self._engageable else 255
|
||||
|
||||
texture = self._txt_exp if self._held_or_actual_mode() else self._txt_wheel
|
||||
exp_mode = self._held_or_actual_mode()
|
||||
texture = self._txt_exp if exp_mode else self._txt_wheel
|
||||
color = self._white_color
|
||||
tint = None
|
||||
if self.wheel_tint is not None:
|
||||
tint = rl.Color(self.wheel_tint.r, self.wheel_tint.g, self.wheel_tint.b, self._white_color.a)
|
||||
|
||||
rl.draw_circle(center_x, center_y, self._rect.width / 2, self._bg_color)
|
||||
if tint is not None:
|
||||
if exp_mode:
|
||||
# The experimental icon is already colored, so show the lateral mode
|
||||
# around it instead of obscuring the icon with a texture tint.
|
||||
radius = self._rect.width / 2
|
||||
rl.draw_ring(rl.Vector2(center_x, center_y), radius - 8, radius, 0, 360, 0, tint)
|
||||
else:
|
||||
color = tint
|
||||
|
||||
rotating_wheel = ui_state.starpilot_toggles.get("rotating_wheel", False) or self._params.get_bool("RotatingWheel")
|
||||
if texture == self._txt_wheel and rotating_wheel:
|
||||
source_rect = rl.Rectangle(0, 0, texture.width, texture.height)
|
||||
dest_rect = rl.Rectangle(center_x, center_y, texture.width, texture.height)
|
||||
origin = rl.Vector2(texture.width / 2, texture.height / 2)
|
||||
rl.draw_texture_pro(texture, source_rect, dest_rect, origin, -self._steer_angle_filter.x, self._white_color)
|
||||
rl.draw_texture_pro(texture, source_rect, dest_rect, origin, -self._steer_angle_filter.x, color)
|
||||
else:
|
||||
rl.draw_texture_ex(texture, rl.Vector2(center_x - texture.width / 2, center_y - texture.height / 2), 0.0, 1.0, self._white_color)
|
||||
rl.draw_texture_ex(texture, rl.Vector2(center_x - texture.width / 2, center_y - texture.height / 2), 0.0, 1.0, color)
|
||||
|
||||
def _held_or_actual_mode(self):
|
||||
now = time.monotonic()
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import pyray as rl
|
||||
|
||||
from cereal import car
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
ANGLE_COLOR = rl.Color(0x3A, 0xDB, 0x6D, 255)
|
||||
TORQUE_COLOR = rl.Color(0x4D, 0x9D, 0xFF, 255)
|
||||
DRIVER_OVERRIDE_COLOR = rl.Color(255, 255, 255, 255)
|
||||
LateralControlMode = car.CarControl.Actuators.LateralControlMode
|
||||
|
||||
|
||||
class RivianLateralMode:
|
||||
"""Display Rivian's active lateral channel and driver steering input."""
|
||||
|
||||
def __init__(self):
|
||||
self.mode: str | None = None
|
||||
self.driver_override = False
|
||||
self._frame = -1
|
||||
|
||||
def update(self) -> None:
|
||||
sm = ui_state.sm
|
||||
if sm.frame == self._frame:
|
||||
return
|
||||
self._frame = sm.frame
|
||||
|
||||
CP = ui_state.CP
|
||||
rivian = CP is not None and CP.brand == "rivian"
|
||||
car_state_received = sm.recv_frame["carState"] >= ui_state.started_frame
|
||||
car_control_received = sm.recv_frame["carControl"] >= ui_state.started_frame
|
||||
if not rivian or not car_state_received or not car_control_received or not sm["carControl"].latActive:
|
||||
self.mode = None
|
||||
self.driver_override = False
|
||||
return
|
||||
|
||||
self.driver_override = sm["carState"].steeringPressed
|
||||
lateral_mode = sm["carOutput"].actuatorsOutput.lateralControlMode
|
||||
if lateral_mode == LateralControlMode.angle:
|
||||
self.mode = "angle"
|
||||
elif lateral_mode in (LateralControlMode.torque, LateralControlMode.torqueRecovering):
|
||||
self.mode = "torque"
|
||||
else:
|
||||
self.mode = None
|
||||
|
||||
@property
|
||||
def wheel_tint(self) -> "rl.Color | None":
|
||||
if self.driver_override:
|
||||
return DRIVER_OVERRIDE_COLOR
|
||||
if self.mode == "angle":
|
||||
return ANGLE_COLOR
|
||||
if self.mode == "torque":
|
||||
return TORQUE_COLOR
|
||||
return None
|
||||
|
||||
|
||||
rivian_lateral_mode = RivianLateralMode()
|
||||
@@ -5,6 +5,7 @@ from openpilot.selfdrive.ui.onroad.starpilot.starpilot_border import render_behi
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.path import render_adjacent_lanes, render_path_edges
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.torque_bar import TorqueBar
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.rivian_lateral_mode import rivian_lateral_mode
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.widget_layout_manager import WidgetLayoutManager
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.widgets import (
|
||||
SetSpeedWidget, SpeedLimitWidget, PedalIconsWidget,
|
||||
@@ -76,6 +77,10 @@ class StarPilotOnroadView(AugmentedRoadView):
|
||||
self._child(self._driver_monitor_widget)
|
||||
self._child(self._stopped_timer_widget)
|
||||
|
||||
def _update_state(self) -> None:
|
||||
rivian_lateral_mode.update()
|
||||
self._hud_renderer._exp_button.wheel_tint = rivian_lateral_mode.wheel_tint
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
border_width = self._get_border_width()
|
||||
border_color = get_screen_edge_color(ui_state)
|
||||
|
||||
@@ -7,6 +7,7 @@ import numpy as np
|
||||
import pyray as rl
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.selfdrive.ui.lib.starpilot_visuals import blend_colors
|
||||
from openpilot.selfdrive.ui.onroad.starpilot.rivian_lateral_mode import rivian_lateral_mode
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.shader_polygon import draw_polygon, Gradient
|
||||
@@ -162,8 +163,13 @@ class TorqueBar(Widget):
|
||||
if self._demo:
|
||||
return
|
||||
|
||||
# torque line
|
||||
if ui_state.sm['controlsState'].lateralControlState.which() == 'angleState':
|
||||
rivian_lateral_mode.update()
|
||||
# Angle-controlled cars, including Rivian's hybrid controller while its
|
||||
# angle channel is active, use a lateral-acceleration estimate for the bar.
|
||||
# The shared Rivian mode detector keys off the actual CAN torque so the bar
|
||||
# follows live angle/torque handoffs rather than the controller request.
|
||||
if (ui_state.sm['controlsState'].lateralControlState.which() == 'angleState' or
|
||||
rivian_lateral_mode.mode == "angle"):
|
||||
controls_state = ui_state.sm['controlsState']
|
||||
car_state = ui_state.sm['carState']
|
||||
live_parameters = ui_state.sm['liveParameters']
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
import importlib.util
|
||||
from enum import IntFlag
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from types import ModuleType, SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from cereal import car
|
||||
|
||||
|
||||
MODULE_PATH = Path(__file__).resolve().parents[1] / "onroad" / "starpilot" / "rivian_lateral_mode.py"
|
||||
EXP_BUTTON_PATH = Path(__file__).resolve().parents[1] / "onroad" / "exp_button.py"
|
||||
|
||||
|
||||
LateralControlMode = car.CarControl.Actuators.LateralControlMode
|
||||
|
||||
|
||||
class FakeColor:
|
||||
def __init__(self, r, g, b, a):
|
||||
self.r = r
|
||||
self.g = g
|
||||
self.b = b
|
||||
self.a = a
|
||||
|
||||
|
||||
class FakeRectangle:
|
||||
def __init__(self, x, y, width, height):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
|
||||
class FakeTexture:
|
||||
def __init__(self, name, width, height):
|
||||
self.name = name
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
|
||||
class FakeSubMaster(dict):
|
||||
def __init__(self, *, lateral_mode, steering_pressed=False, lat_active=True):
|
||||
super().__init__({
|
||||
"carControl": SimpleNamespace(latActive=lat_active),
|
||||
"carState": SimpleNamespace(steeringPressed=steering_pressed),
|
||||
"carOutput": SimpleNamespace(
|
||||
actuatorsOutput=SimpleNamespace(lateralControlMode=lateral_mode),
|
||||
),
|
||||
})
|
||||
self.frame = 1
|
||||
self.recv_frame = {"carControl": 1, "carState": 1}
|
||||
|
||||
|
||||
def load_lateral_mode(monkeypatch, *, brand="rivian", angle_harness=True, longitudinal_harness=False,
|
||||
steering_pressed=False, lat_active=True, lateral_mode=LateralControlMode.inactive):
|
||||
class RivianFlags(IntFlag):
|
||||
ANGLE_HARNESS = 1
|
||||
LONGITUDINAL_HARNESS = 2
|
||||
|
||||
fake_pyray = ModuleType("pyray")
|
||||
fake_pyray.Color = lambda *args: args
|
||||
monkeypatch.setitem(sys.modules, "pyray", fake_pyray)
|
||||
|
||||
values_module = ModuleType("opendbc.car.rivian.values")
|
||||
values_module.RivianFlags = RivianFlags
|
||||
monkeypatch.setitem(sys.modules, "opendbc.car.rivian.values", values_module)
|
||||
|
||||
flags = RivianFlags(0)
|
||||
if angle_harness:
|
||||
flags |= RivianFlags.ANGLE_HARNESS
|
||||
if longitudinal_harness:
|
||||
flags |= RivianFlags.LONGITUDINAL_HARNESS
|
||||
|
||||
ui_state = SimpleNamespace(
|
||||
CP=SimpleNamespace(brand=brand, flags=flags),
|
||||
sm=FakeSubMaster(lateral_mode=lateral_mode, steering_pressed=steering_pressed, lat_active=lat_active),
|
||||
started_frame=0,
|
||||
)
|
||||
ui_state_module = ModuleType("openpilot.selfdrive.ui.ui_state")
|
||||
ui_state_module.ui_state = ui_state
|
||||
monkeypatch.setitem(sys.modules, "openpilot.selfdrive.ui.ui_state", ui_state_module)
|
||||
|
||||
spec = importlib.util.spec_from_file_location("rivian_lateral_mode_under_test", MODULE_PATH)
|
||||
assert spec is not None and spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def load_exp_button(monkeypatch):
|
||||
draws = {"textures": [], "rings": []}
|
||||
fake_pyray = ModuleType("pyray")
|
||||
fake_pyray.Color = FakeColor
|
||||
fake_pyray.Rectangle = FakeRectangle
|
||||
fake_pyray.Texture = FakeTexture
|
||||
fake_pyray.Vector2 = lambda x, y: SimpleNamespace(x=x, y=y)
|
||||
fake_pyray.draw_circle = lambda *args: None
|
||||
fake_pyray.draw_ring = lambda *args: draws["rings"].append(args)
|
||||
fake_pyray.draw_texture_ex = lambda *args: draws["textures"].append(args)
|
||||
fake_pyray.draw_texture_pro = lambda *args: draws["textures"].append(args)
|
||||
monkeypatch.setitem(sys.modules, "pyray", fake_pyray)
|
||||
|
||||
params_module = ModuleType("openpilot.common.params")
|
||||
params_module.Params = type("Params", (), {"get_bool": lambda self, *args, **kwargs: False})
|
||||
monkeypatch.setitem(sys.modules, "openpilot.common.params", params_module)
|
||||
|
||||
fake_ui_state = SimpleNamespace(
|
||||
ui_params=SimpleNamespace(get_bool=lambda *args, **kwargs: False),
|
||||
sm={
|
||||
"selfdriveState": SimpleNamespace(experimentalMode=False, engageable=True, enabled=False),
|
||||
"carState": SimpleNamespace(steeringAngleDeg=0.0),
|
||||
},
|
||||
starpilot_toggles={},
|
||||
always_on_lateral_active=False,
|
||||
conditional_status=0,
|
||||
switchback_mode_enabled=False,
|
||||
traffic_mode_enabled=False,
|
||||
params_memory=SimpleNamespace(),
|
||||
has_longitudinal_control=False,
|
||||
)
|
||||
ui_state_module = ModuleType("openpilot.selfdrive.ui.ui_state")
|
||||
ui_state_module.ui_state = fake_ui_state
|
||||
monkeypatch.setitem(sys.modules, "openpilot.selfdrive.ui.ui_state", ui_state_module)
|
||||
|
||||
class FakeGuiApp:
|
||||
target_fps = 60
|
||||
|
||||
@staticmethod
|
||||
def texture(name, width, height):
|
||||
return FakeTexture(name, width, height)
|
||||
|
||||
application_module = ModuleType("openpilot.system.ui.lib.application")
|
||||
application_module.gui_app = FakeGuiApp()
|
||||
monkeypatch.setitem(sys.modules, "openpilot.system.ui.lib.application", application_module)
|
||||
|
||||
class FakeWidget:
|
||||
def __init__(self):
|
||||
self.is_pressed = False
|
||||
|
||||
def set_visible(self, visible):
|
||||
self._visible = visible
|
||||
|
||||
def _handle_mouse_release(self, _):
|
||||
pass
|
||||
|
||||
widgets_module = ModuleType("openpilot.system.ui.widgets")
|
||||
widgets_module.Widget = FakeWidget
|
||||
monkeypatch.setitem(sys.modules, "openpilot.system.ui.widgets", widgets_module)
|
||||
|
||||
class FakeFilter:
|
||||
def __init__(self, x, *args):
|
||||
self.x = x
|
||||
|
||||
def update(self, x):
|
||||
self.x = x
|
||||
return x
|
||||
|
||||
filter_module = ModuleType("openpilot.common.filter_simple")
|
||||
filter_module.FirstOrderFilter = FakeFilter
|
||||
monkeypatch.setitem(sys.modules, "openpilot.common.filter_simple", filter_module)
|
||||
|
||||
experimental_module = ModuleType("openpilot.starpilot.common.experimental_state")
|
||||
experimental_module.CEStatus = {"OFF": 0}
|
||||
experimental_module.next_manual_ce_status = lambda *args: 0
|
||||
experimental_module.sync_manual_ce_state = lambda *args: None
|
||||
monkeypatch.setitem(sys.modules, "openpilot.starpilot.common.experimental_state", experimental_module)
|
||||
|
||||
spec = importlib.util.spec_from_file_location("exp_button_under_test", EXP_BUTTON_PATH)
|
||||
assert spec is not None and spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module, draws
|
||||
|
||||
|
||||
def test_angle_mode_uses_controller_report(monkeypatch):
|
||||
module = load_lateral_mode(monkeypatch, lateral_mode=LateralControlMode.angle)
|
||||
mode = module.RivianLateralMode()
|
||||
|
||||
mode.update()
|
||||
|
||||
assert mode.mode == "angle"
|
||||
assert mode.wheel_tint == module.ANGLE_COLOR
|
||||
|
||||
|
||||
def test_zero_torque_at_standstill_stays_in_reported_torque_mode(monkeypatch):
|
||||
module = load_lateral_mode(monkeypatch, lateral_mode=LateralControlMode.torque)
|
||||
mode = module.RivianLateralMode()
|
||||
|
||||
mode.update()
|
||||
|
||||
assert mode.mode == "torque"
|
||||
assert mode.wheel_tint == module.TORQUE_COLOR
|
||||
|
||||
|
||||
def test_torque_recovery_stays_blue(monkeypatch):
|
||||
module = load_lateral_mode(monkeypatch, lateral_mode=LateralControlMode.torqueRecovering)
|
||||
mode = module.RivianLateralMode()
|
||||
|
||||
mode.update()
|
||||
|
||||
assert mode.mode == "torque"
|
||||
assert mode.wheel_tint == module.TORQUE_COLOR
|
||||
|
||||
|
||||
def test_basic_harness_rivian_uses_torque_color(monkeypatch):
|
||||
module = load_lateral_mode(monkeypatch, angle_harness=False, lateral_mode=LateralControlMode.torque)
|
||||
mode = module.RivianLateralMode()
|
||||
|
||||
mode.update()
|
||||
|
||||
assert mode.mode == "torque"
|
||||
assert mode.wheel_tint == module.TORQUE_COLOR
|
||||
|
||||
|
||||
def test_longitudinal_harness_rivian_uses_torque_color(monkeypatch):
|
||||
module = load_lateral_mode(monkeypatch, angle_harness=False, longitudinal_harness=True,
|
||||
lateral_mode=LateralControlMode.torque)
|
||||
mode = module.RivianLateralMode()
|
||||
|
||||
mode.update()
|
||||
|
||||
assert mode.mode == "torque"
|
||||
assert mode.wheel_tint == module.TORQUE_COLOR
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("angle_harness", "longitudinal_harness", "lateral_mode"), [
|
||||
(False, False, LateralControlMode.torque),
|
||||
(False, True, LateralControlMode.torque),
|
||||
(True, True, LateralControlMode.angle),
|
||||
(True, True, LateralControlMode.torque),
|
||||
(True, True, LateralControlMode.torqueRecovering),
|
||||
])
|
||||
def test_driver_steering_is_white_in_every_configuration(monkeypatch, angle_harness, longitudinal_harness, lateral_mode):
|
||||
module = load_lateral_mode(monkeypatch, angle_harness=angle_harness, longitudinal_harness=longitudinal_harness,
|
||||
steering_pressed=True, lateral_mode=lateral_mode)
|
||||
mode = module.RivianLateralMode()
|
||||
|
||||
mode.update()
|
||||
|
||||
expected_mode = "angle" if lateral_mode == LateralControlMode.angle else "torque"
|
||||
assert mode.mode == expected_mode
|
||||
assert mode.driver_override
|
||||
assert mode.wheel_tint == module.DRIVER_OVERRIDE_COLOR
|
||||
|
||||
|
||||
def test_releasing_wheel_restores_active_mode_color(monkeypatch):
|
||||
module = load_lateral_mode(monkeypatch, steering_pressed=True, lateral_mode=LateralControlMode.angle)
|
||||
mode = module.RivianLateralMode()
|
||||
mode.update()
|
||||
assert mode.wheel_tint == module.DRIVER_OVERRIDE_COLOR
|
||||
|
||||
module.ui_state.sm["carState"].steeringPressed = False
|
||||
module.ui_state.sm.frame += 1
|
||||
mode.update()
|
||||
|
||||
assert not mode.driver_override
|
||||
assert mode.wheel_tint == module.ANGLE_COLOR
|
||||
|
||||
|
||||
def test_non_rivian_is_not_classified(monkeypatch):
|
||||
module = load_lateral_mode(monkeypatch, brand="toyota", steering_pressed=True,
|
||||
lateral_mode=LateralControlMode.torque)
|
||||
mode = module.RivianLateralMode()
|
||||
|
||||
mode.update()
|
||||
|
||||
assert mode.mode is None
|
||||
assert not mode.driver_override
|
||||
assert mode.wheel_tint is None
|
||||
|
||||
|
||||
def test_inactive_lateral_is_not_classified(monkeypatch):
|
||||
module = load_lateral_mode(monkeypatch, steering_pressed=True, lat_active=False,
|
||||
lateral_mode=LateralControlMode.torque)
|
||||
mode = module.RivianLateralMode()
|
||||
|
||||
mode.update()
|
||||
|
||||
assert mode.mode is None
|
||||
assert not mode.driver_override
|
||||
assert mode.wheel_tint is None
|
||||
|
||||
|
||||
def test_non_mici_wheel_icon_uses_rivian_tint(monkeypatch):
|
||||
module, draws = load_exp_button(monkeypatch)
|
||||
button = module.ExpButton(192, 144)
|
||||
button.wheel_tint = FakeColor(0x4D, 0x9D, 0xFF, 255)
|
||||
button._update_state()
|
||||
|
||||
button._render(FakeRectangle(0, 0, 192, 192))
|
||||
|
||||
assert len(draws["textures"]) == 1
|
||||
texture_color = draws["textures"][0][-1]
|
||||
assert (texture_color.r, texture_color.g, texture_color.b, texture_color.a) == (0x4D, 0x9D, 0xFF, 255)
|
||||
@@ -189,6 +189,8 @@ def get_selected_panda_firmware_name(app_fn, remote_start, hkg_remote_start, ign
|
||||
|
||||
|
||||
def flash_panda(params_memory):
|
||||
from openpilot.selfdrive.pandad.rivian_long_flasher import is_rivian_bridge_panda, is_rivian_vehicle
|
||||
|
||||
params = Params()
|
||||
try:
|
||||
remote_start = params.get_bool("RemoteStartBootsComma")
|
||||
@@ -203,9 +205,14 @@ def flash_panda(params_memory):
|
||||
except Exception:
|
||||
ignore_ignition_line = False
|
||||
|
||||
rivian = is_rivian_vehicle()
|
||||
usb_serials = set(Panda.usb_list())
|
||||
for serial in Panda.list():
|
||||
try:
|
||||
with Panda(serial=serial) as panda:
|
||||
if serial in usb_serials and is_rivian_bridge_panda(panda, rivian):
|
||||
print(f"Skipping Rivian harness bridge {serial}")
|
||||
continue
|
||||
print(f"Flashing Panda {serial}")
|
||||
flash_fn = None
|
||||
app_fn = panda.get_mcu_type().config.app_fn
|
||||
|
||||
@@ -330,6 +330,9 @@ def get_starpilot_toggles(sm=messaging.SubMaster(["starpilotPlan"]), *, read_per
|
||||
# Controller selection happens before the first live StarPilot broadcast. Do
|
||||
# not let a cached CarParams/controller type hide the persisted user request.
|
||||
toggles.force_torque_controller = get_starpilot_toggles._params.get_bool("ForceTorqueController")
|
||||
# Controller selection happens before the first live StarPilot broadcast.
|
||||
# Realtime callers use the serialized value to avoid blocking reads.
|
||||
toggles.rivian_angle_control = get_starpilot_toggles._params.get_bool("RivianAngleControl")
|
||||
return toggles
|
||||
|
||||
@cache
|
||||
@@ -563,6 +566,7 @@ class StarPilotVariables:
|
||||
toggle = self.starpilot_toggles
|
||||
# CarParams uses this value to select the matching Panda safety configuration.
|
||||
toggle.tesla_cooperative_steering = self.params.get_bool("TeslaCoopSteering")
|
||||
toggle.rivian_angle_control = self.params.get_bool("RivianAngleControl")
|
||||
|
||||
fallback_platform = GM_CAR.CHEVROLET_BOLT_ACC_2022_2023 if HARDWARE.get_device_type() == "pc" else MOCK.MOCK
|
||||
|
||||
@@ -1421,6 +1425,7 @@ class StarPilotVariables:
|
||||
"TeslaCoopSteering",
|
||||
condition=toggle.car_make == "tesla" and toggle.car_model == TESLA_CAR.TESLA_MODEL_3,
|
||||
)
|
||||
toggle.rivian_angle_control = self.get_value("RivianAngleControl", condition=toggle.car_make == "rivian")
|
||||
|
||||
toggle.tethering_config = self.get_value("TetheringEnabled", cast=float)
|
||||
|
||||
|
||||
@@ -64,6 +64,19 @@ def test_get_starpilot_toggles_realtime_path_does_not_read_persisted_force_param
|
||||
assert toggles.force_torque_controller is False
|
||||
|
||||
|
||||
def test_get_starpilot_toggles_uses_live_rivian_angle_request(monkeypatch):
|
||||
params = SimpleNamespace(get_bool=lambda key: key == "RivianAngleControl")
|
||||
monkeypatch.setattr(spv.get_starpilot_toggles, "_params", params, raising=False)
|
||||
|
||||
payload = '{"rivian_angle_control": false}'
|
||||
toggles = spv.get_starpilot_toggles(
|
||||
{"starpilotPlan": SimpleNamespace(starpilotToggles=payload)},
|
||||
read_persisted_force_params=True,
|
||||
)
|
||||
|
||||
assert toggles.rivian_angle_control is True
|
||||
|
||||
|
||||
class _FakeParams:
|
||||
def __init__(self, floats=None, ints=None, bools=None):
|
||||
self.floats = dict(floats or {})
|
||||
|
||||
@@ -15,6 +15,7 @@ const HIDDEN_SETTING_KEYS = new Set(["HumanAcceleration", "ReverseCruise"])
|
||||
const GM_MAKES = ["Buick", "Cadillac", "Chevrolet", "GMC", "Holden"]
|
||||
const HKG_MAKES = ["Genesis", "Hyundai", "Kia"]
|
||||
const VEHICLE_SETTING_MAKES = {
|
||||
RivianAngleControl: ["Rivian"],
|
||||
TeslaCoopSteering: ["Tesla"],
|
||||
NAPRadarEnabled: ["Tesla"],
|
||||
NAPRadarBehindNosecone: ["Tesla"],
|
||||
@@ -105,6 +106,7 @@ function isVehicleSettingVisible(section, param) {
|
||||
function isSettingVisible(section, param) {
|
||||
// This policy controls Galaxy rendering only; hidden params retain their stored values.
|
||||
if (HIDDEN_SETTING_KEYS.has(param.key) || !isVehicleSettingVisible(section, param)) return false
|
||||
if (param.requires_capability && !state.values[param.requires_capability]) return false
|
||||
if (RADAR_REQUIRED_KEYS.has(param.key) && !state.values.HasRadar) return false
|
||||
if (param.key === "AlphaLongitudinalEnabled" && !state.values.AlphaLongitudinalAvailable) return false
|
||||
if (state.values[GALAXY_DEVELOPER_MODE_KEY]) return true
|
||||
|
||||
@@ -2737,6 +2737,16 @@
|
||||
"options_endpoint": "/api/fingerprints/models?make={CarMake}",
|
||||
"settings_tier": "simple"
|
||||
},
|
||||
{
|
||||
"key": "RivianAngleControl",
|
||||
"label": "Rivian Angle Steering",
|
||||
"description": "Use the Extreme harness angle channel with cooperative torque fallback.",
|
||||
"data_type": "bool",
|
||||
"ui_type": "toggle",
|
||||
"favorite_eligible": true,
|
||||
"requires_capability": "HasRivianAngleHarness",
|
||||
"settings_tier": "simple"
|
||||
},
|
||||
{
|
||||
"key": "ForceFingerprint",
|
||||
"label": "Disable Automatic Fingerprint Detection",
|
||||
|
||||
@@ -188,6 +188,17 @@ def test_human_acceleration_param_is_removed():
|
||||
assert '{"HumanAcceleration",' not in params_source
|
||||
|
||||
|
||||
def test_rivian_angle_control_is_live_favorite_and_harness_gated():
|
||||
sections = _params_by_section(_layout())
|
||||
setting = sections["Vehicle"]["RivianAngleControl"]
|
||||
|
||||
assert setting["ui_type"] == "toggle"
|
||||
assert setting["favorite_eligible"] is True
|
||||
assert setting["requires_capability"] == "HasRivianAngleHarness"
|
||||
assert "reboot" not in setting["description"].lower()
|
||||
assert _declared_default("RivianAngleControl") == "0"
|
||||
|
||||
|
||||
def test_vasm_is_default_off_and_configured_only_in_galaxy():
|
||||
sections = _params_by_section(_layout())
|
||||
lateral = sections["Lateral (Steering)"]
|
||||
|
||||
@@ -251,6 +251,23 @@ def test_favorite_slot_options_include_virtual_cruise_actions(monkeypatch):
|
||||
assert "__starpilot_favorite_action__:distance_increase" in option_keys
|
||||
|
||||
|
||||
def test_rivian_angle_favorite_requires_detected_extreme_harness(monkeypatch):
|
||||
options = [
|
||||
{"key": "RivianAngleControl", "requiresCapability": "HasRivianAngleHarness"},
|
||||
{"key": "NonGatedFavorite", "requiresCapability": ""},
|
||||
]
|
||||
monkeypatch.setattr(the_galaxy, "_get_favorite_slot_options", lambda: options)
|
||||
|
||||
monkeypatch.setattr(the_galaxy, "_get_has_rivian_angle_harness", lambda: False)
|
||||
assert [option["key"] for option in the_galaxy._get_available_favorite_slot_options()] == ["NonGatedFavorite"]
|
||||
|
||||
monkeypatch.setattr(the_galaxy, "_get_has_rivian_angle_harness", lambda: True)
|
||||
assert [option["key"] for option in the_galaxy._get_available_favorite_slot_options()] == [
|
||||
"RivianAngleControl",
|
||||
"NonGatedFavorite",
|
||||
]
|
||||
|
||||
|
||||
def test_favorite_action_endpoint_increments_virtual_button_counter(monkeypatch):
|
||||
client, _ = _params_client(monkeypatch, {}, "tici")
|
||||
fake_memory = WritableFakeParams()
|
||||
|
||||
@@ -92,6 +92,8 @@ from openpilot.starpilot.system.the_galaxy import flm_workspace, utilities
|
||||
from openpilot.starpilot.system.the_galaxy.update_recovery import inspect_interrupted_update, public_recovery_status, recover_interrupted_update
|
||||
|
||||
DISCORD_WEBHOOK_URL = os.getenv("DISCORD_WEBHOOK_URL")
|
||||
# Keep Galaxy independent of opendbc's generated car bindings while matching RivianFlags.ANGLE_HARNESS.
|
||||
RIVIAN_ANGLE_HARNESS_FLAG = 1 << 0
|
||||
|
||||
GITLAB_API = "https://gitlab.com/api/v4"
|
||||
GITLAB_SUBMISSIONS_PROJECT_ID = "71992109"
|
||||
@@ -2509,6 +2511,7 @@ def _get_favorite_slot_options():
|
||||
"label": str(param_data.get("label") or key),
|
||||
"description": str(param_data.get("description") or ""),
|
||||
"section": section_name,
|
||||
"requiresCapability": str(param_data.get("requires_capability") or ""),
|
||||
})
|
||||
except Exception:
|
||||
options = []
|
||||
@@ -2517,6 +2520,15 @@ def _get_favorite_slot_options():
|
||||
_favorite_slot_options = options
|
||||
return _favorite_slot_options
|
||||
|
||||
def _get_available_favorite_slot_options():
|
||||
capabilities = {
|
||||
"HasRivianAngleHarness": _get_has_rivian_angle_harness(),
|
||||
}
|
||||
return [
|
||||
option for option in _get_favorite_slot_options()
|
||||
if not option.get("requiresCapability") or capabilities.get(option["requiresCapability"], False)
|
||||
]
|
||||
|
||||
def _favorite_slot_values(options):
|
||||
return {
|
||||
option["key"]: _safe_params_get_bool(option["key"])
|
||||
@@ -3322,6 +3334,17 @@ def _get_alpha_longitudinal_available():
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _get_has_rivian_angle_harness():
|
||||
cp_bytes = _safe_params_get_live_raw("CarParamsPersistent")
|
||||
if not cp_bytes:
|
||||
return False
|
||||
|
||||
try:
|
||||
with car.CarParams.from_bytes(cp_bytes) as cp:
|
||||
return cp.brand == "rivian" and bool(int(getattr(cp, "flags", 0)) & RIVIAN_ANGLE_HARNESS_FLAG)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _get_hardware_snapshot_items():
|
||||
starpilot_toggles = _get_starpilot_toggles_snapshot()
|
||||
|
||||
@@ -4456,7 +4479,7 @@ def setup(app):
|
||||
|
||||
@app.route("/api/favorites/slots", methods=["GET", "PUT"])
|
||||
def favorite_slots():
|
||||
options = _get_favorite_slot_options()
|
||||
options = _get_available_favorite_slot_options()
|
||||
option_by_key = {option["key"]: option for option in options}
|
||||
eligible_keys = set(option_by_key)
|
||||
|
||||
@@ -4506,7 +4529,7 @@ def setup(app):
|
||||
|
||||
@app.route("/api/favorites/values", methods=["GET"])
|
||||
def favorite_values():
|
||||
options = _get_favorite_slot_options()
|
||||
options = _get_available_favorite_slot_options()
|
||||
eligible_keys = {option["key"] for option in options}
|
||||
slots = normalize_favorite_slots(params.get(FAVORITE_SLOTS_PARAM), params=params, eligible_keys=eligible_keys)
|
||||
return jsonify({"values": _configured_favorite_slot_values(slots)}), 200
|
||||
@@ -4537,7 +4560,7 @@ def setup(app):
|
||||
if not isinstance(raw_slots, list):
|
||||
return jsonify({"error": "Favorite slots must be configured with the Favorites editor."}), 400
|
||||
|
||||
options = _get_favorite_slot_options()
|
||||
options = _get_available_favorite_slot_options()
|
||||
option_by_key = {option["key"]: option for option in options}
|
||||
eligible_keys = set(option_by_key)
|
||||
slots = normalize_favorite_slots(raw_slots, params=params, eligible_keys=eligible_keys)
|
||||
@@ -4918,6 +4941,8 @@ def setup(app):
|
||||
update_starpilot_toggles()
|
||||
|
||||
response = {"message": f"Parameter '{key}' updated successfully."}
|
||||
if key == "RivianAngleControl":
|
||||
response["message"] = "Rivian steering mode updated. The safe channel handoff is in progress."
|
||||
updated = {}
|
||||
if key in PANDA_FIRMWARE_TOGGLE_KEYS:
|
||||
threading.Thread(target=_flash_panda_then_reboot, daemon=True).start()
|
||||
@@ -5001,6 +5026,7 @@ def setup(app):
|
||||
result["HasRadar"] = _get_has_radar()
|
||||
result["VehicleParked"] = _get_vehicle_parked()
|
||||
result["AlphaLongitudinalAvailable"] = _get_alpha_longitudinal_available()
|
||||
result["HasRivianAngleHarness"] = _get_has_rivian_angle_harness()
|
||||
|
||||
return jsonify(_sanitize_json_value(result)), 200
|
||||
|
||||
|
||||
Reference in New Issue
Block a user