mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-21 00:03:45 +08:00
Merge PR #90: Rivian Angle Support
Original PR by TonyJOM (Anthony Orta). Co-authored-by: TonyJOM <anthonyorta20@icloud.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -1422,6 +1426,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
|
||||
|
||||
@@ -2703,6 +2703,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",
|
||||
|
||||
@@ -172,6 +172,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)"]
|
||||
|
||||
@@ -249,6 +249,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()
|
||||
|
||||
@@ -99,6 +99,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"
|
||||
@@ -2721,6 +2723,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 = []
|
||||
@@ -2729,6 +2732,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"])
|
||||
@@ -3517,6 +3529,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()
|
||||
|
||||
@@ -4655,7 +4678,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)
|
||||
|
||||
@@ -4705,7 +4728,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
|
||||
@@ -4736,7 +4759,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)
|
||||
@@ -5099,6 +5122,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()
|
||||
@@ -5178,6 +5203,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