Compare commits

...

94 Commits

Author SHA1 Message Date
Rick Lan 459665ca60 Pre-build - 2025-06-09 2025-06-09 17:04:43 +08:00
Rick Lan c3273aaa07 pandad: fix premature USB panda recovery https://github.com/commaai/openpilot/pull/3528 2025-06-09 17:03:56 +08:00
Rick Lan 8f1967a74d Fix tethering connection disabling internet
https://github.com/commaai/openpilot/pull/35075
2025-06-09 17:03:56 +08:00
Rick Lan f91dac60cd Fix possible CAN ignition overlap due to incorrect counter tracking
https://github.com/commaai/openpilot/pull/35019
2025-06-09 17:03:56 +08:00
Rick Lan 7ab310e401 radard: use a filter for aLeadTau
https://github.com/commaai/openpilot/pull/34883/commits
2025-06-09 17:03:56 +08:00
Rick Lan 53430b4334 Merge branch 'min-feat/lon/aem' into pre 2025-06-09 17:03:03 +08:00
Rick Lan a0f46a5556 Merge branch 'min-feat/dev/alert-mode' into pre 2025-06-09 17:01:29 +08:00
Rick Lan 84d7670880 Adaptive Experimental Mode (AEM) - 2025/05/27 2025-06-04 19:49:56 +08:00
Rick Lan c37ea89539 Audiable Alert Mode - 2025-05-28 2025-05-28 14:20:47 +08:00
Rick Lan 90e8695072 Merge branch 'min-feat/dev/alert-mode' into pre 2025-05-28 14:13:39 +08:00
Rick Lan 037bf886b8 Audiable Alert Mode - 2025-05-28 2025-05-28 14:11:36 +08:00
Rick Lan 8203454d57 Merge branch 'min-feat/dev/fileserv' into pre 2025-05-28 13:53:16 +08:00
Rick Lan 38878135b8 fileserv - 2025-05-28 2025-05-28 13:52:40 +08:00
Rick Lan 723ba7aad3 Merge branch 'min-feat/dev/fileserv' into pre 2025-05-28 13:36:43 +08:00
Rick Lan 6f50e36afc fileserv - 2025-05-28 2025-05-28 13:36:11 +08:00
Rick Lan 7f1fbe1bac Merge branch 'min-feat/dev/fileserv' into pre 2025-05-28 13:34:49 +08:00
Rick Lan 0e9b423d8b fileserv - 2025-05-28 2025-05-28 13:34:02 +08:00
Rick Lan 369fd2352a Merge branch 'min-feat/dev/fileserv' into pre 2025-05-28 13:32:29 +08:00
Rick Lan 15634c7342 fileserv - 2025-05-28 2025-05-28 13:31:36 +08:00
Rick Lan 53d2fc5c3b Merge branch 'min-feat/dev/fileserv' into pre 2025-05-28 12:54:21 +08:00
Rick Lan 4d91288053 fileserv - 2025-05-28 2025-05-28 12:49:35 +08:00
Rick Lan 19c4658ecb Merge branch 'min-feat/ui/border-indicator' into pre 2025-05-27 20:05:50 +08:00
Rick Lan aec4fa3814 Border Indicator - 2025-05-27 2025-05-27 20:04:44 +08:00
Rick Lan 559e657ee8 Merge branch 'pre-hkg' into pre 2025-05-27 15:37:24 +08:00
Rick Lan fc621376ba Merge branch 'pre-vag' into pre 2025-05-27 15:37:20 +08:00
Rick Lan 1be247b35d Merge branch 'brand/toyota/stock-lon' into pre-toyota 2025-05-27 15:36:13 +08:00
Rick Lan 70a05dfa88 Merge branch 'brand/toyota/zss' into pre-toyota 2025-05-27 15:35:38 +08:00
Rick Lan 0a214f01fd Merge branch 'brand/toyota/sdsu' into pre-toyota 2025-05-27 15:35:14 +08:00
Rick Lan 3e6ac60b93 Merge branch 'brand/toyota/radar-filter' into pre-toyota 2025-05-27 15:35:01 +08:00
Rick Lan ad7ea05d76 Merge branch 'brand/toyota/long-filter-common' into pre-toyota 2025-05-27 15:33:32 +08:00
Rick Lan 69fb27bac2 Merge branch 'brand/toyota/tss1-sng' into pre-toyota 2025-05-27 15:33:27 +08:00
Rick Lan f91696153b Toyota SDSU - 2025/05/27 2025-05-27 15:31:41 +08:00
Rick Lan 8a0a8ff4d0 Toyota Door Auto Lock/Unlock - 2025/05/27 2025-05-27 15:18:31 +08:00
Rick Lan e8caa20aee Toyota Long Filter - 2025/05/27 2025-05-27 13:57:33 +08:00
Rick Lan e8edc24252 Merge branch 'brand/vag/avoid-eps-lockout' into pre-vag 2025-05-27 12:56:13 +08:00
Rick Lan 987efffacb Merge branch 'brand/vag/pq-no-dashcam' into pre-vag 2025-05-27 12:55:10 +08:00
Rick Lan 7227c90e7b Merge branch 'brand/vag/pq-steering-patch' into pre-vag 2025-05-27 12:55:00 +08:00
Rick Lan e03c5f3857 HKG SMDPS - init 2025-05-27 12:45:14 +08:00
Rick Lan 49c5184e73 VAG PQ Steering Patch - init 2025-05-27 12:44:51 +08:00
Rick Lan c957d58186 PQ No Dashcam - init 2025-05-27 12:44:40 +08:00
Rick Lan e99675400e VAG Avoid EPS Lockout - 2025/05/17 2025-05-27 12:44:26 +08:00
Rick Lan 1b06d3975d VAG A0 SnG - init 2025-05-27 12:44:14 +08:00
Rick Lan c0119c6bf7 Toyota Stock Lon Mode - init 2025-05-27 12:43:43 +08:00
Rick Lan aa0a5e440b Toyota ZSS - init 2025-05-27 12:43:33 +08:00
Rick Lan 9b588590f7 Toyota Radar Filter - 2025/05/27 2025-05-27 12:43:12 +08:00
Rick Lan f5795947b3 Toyota TSS1 SnG - init 2025-05-27 12:42:34 +08:00
Rick Lan 87972fb990 Merge remote-tracking branch 'dev/min-feat/ui-radar-tracks' into full 2025-05-27 12:38:48 +08:00
Rick Lan 19d643610a Merge remote-tracking branch 'dev/min-feat/auto-shutdown' into full 2025-05-27 12:35:49 +08:00
Rick Lan f1e185736c Merge remote-tracking branch 'dev/min-feat/no-gas-gating' into full 2025-05-27 12:35:23 +08:00
Rick Lan 201f65419d Merge remote-tracking branch 'dev/min-feat/max-speed' into full 2025-05-27 12:34:14 +08:00
Rick Lan 75213dfa8c Merge remote-tracking branch 'dev/min-feat/alert-mode' into full 2025-05-27 12:34:05 +08:00
Rick Lan 8b747872f1 Merge remote-tracking branch 'dev/min-feat/aem' into full 2025-05-27 12:33:45 +08:00
Rick Lan ab8014e3df Merge remote-tracking branch 'dev/min-feat/acm' into full 2025-05-27 12:31:57 +08:00
Rick Lan e8a2e5098f Merge remote-tracking branch 'dev/min-feat/rainbow-path' into full 2025-05-27 12:30:10 +08:00
Rick Lan 7596ebfae0 Merge branch 'min-feat/road-edge-detection' into full 2025-05-27 12:28:48 +08:00
Rick Lan a9aa186b8b Merge branch 'min-feat/ext-radar' into full 2025-05-27 12:26:58 +08:00
Rick Lan 5ad4cd34f5 Merge branch 'min-feat/hide-hud' into full 2025-05-27 12:26:28 +08:00
Rick Lan 757e2bd9cf Merge branch 'min-feat/on-off-road' into full 2025-05-27 12:23:54 +08:00
Rick Lan 1c6a0b8b61 Merge branch 'min-feat/lca' into full 2025-05-27 12:23:40 +08:00
Rick Lan 7aa1a5f91e Merge branch 'min-feat/model-selector' into full 2025-05-27 12:23:13 +08:00
Rick Lan 13f67f331f Merge branch 'min-feat/display-mode' into full 2025-05-27 12:22:46 +08:00
Rick Lan ed18bb8ecd Merge branch 'min-feat/alka' into full 2025-05-27 12:22:01 +08:00
Rick Lan e826a31e7d Audiable Alert Mode - 2025/05/27 2025-05-27 12:18:24 +08:00
Rick Lan f9a9dcc912 Adaptive Experimental Mode (AEM) - 2025/05/27 2025-05-27 11:20:15 +08:00
Rick Lan a6e9c34a02 Ext Radar - 2025/05/27 2025-05-27 10:48:12 +08:00
Rick Lan 6871bfdb99 Adaptive Coasting Mode (ACM) - 2025/05/20 2025-05-20 13:26:45 +08:00
Rick Lan d1899e3403 O3 - 2025/05/17 2025-05-17 22:36:02 +08:00
Rick Lan df4614e585 Auto Shutdown - 2025/05/15 2025-05-15 10:14:00 +08:00
Rick Lan 8c23fdde2b No Gas-Gating (NoGG) - init 2025-05-14 22:16:34 +08:00
Rick Lan f08abf48c5 Max Speed - init 2025-05-14 22:16:28 +08:00
Rick Lan 396576cd01 Rainbow Path - init 2025-05-14 22:16:04 +08:00
Rick Lan 4e11fbde11 Road Edge Detection - init 2025-05-14 22:15:58 +08:00
Rick Lan 07c7f009cd Hide HUD - init 2025-05-14 22:15:45 +08:00
Rick Lan bd09c5be36 On/Off Road - init 2025-05-14 22:15:38 +08:00
Rick Lan 39643f9ea8 LCA - init 2025-05-14 22:15:32 +08:00
Rick Lan 81727ec2fc model selector - init 2025-05-14 22:15:25 +08:00
Rick Lan 139b80e2ef display mode - init 2025-05-14 22:15:17 +08:00
Rick Lan 0545016dde alka - init 2025-05-14 22:15:11 +08:00
Rick Lan f4f1beb861 Radar Track UI - init 2025-05-13 12:03:40 +08:00
Rick Lan c7b91221ff spinbox - init 2025-05-12 15:51:14 +08:00
Rick Lan e2cc9945df Panel - init 2025-05-12 15:50:22 +08:00
Rick Lan 461109b4f6 param - init 2025-05-12 15:49:38 +08:00
Rick Lan f3b6fa9bd7 Core - reduce Exp Mode init speed 2025-05-12 15:49:16 +08:00
Rick Lan 2fa36a0088 Core - dev config 2025-05-06 20:40:08 +08:00
Rick Lan f31977e79a Core - add custom folder 2025-04-25 10:39:40 +08:00
Rick Lan 0151a26082 Core - resize logo 2025-04-11 17:54:30 +08:00
Rick Lan 86f1618f75 Core - Disable branch selection 2025-04-08 17:17:42 +08:00
Rick Lan 4326d334cb Core - Last Errors Btn 2025-04-08 17:15:59 +08:00
Rick Lan 888d0788f6 Core - Disable Updates Toggle 2025-04-08 17:15:59 +08:00
Rick Lan ef4702c077 Core - LICENSE 2025-04-08 17:15:59 +08:00
Rick Lan c11b51a9cc Core - allow toggles change while onroad 2025-04-08 17:15:46 +08:00
Rick Lan 28e1822ad1 Core - allow disable logging 2025-04-08 17:15:46 +08:00
Rick Lan e9fdbaa779 Core - branding 2025-04-08 17:15:46 +08:00
Adeeb Shihadeh a3f1354f27 0.9.8 hotfixes (#34988)
* agnos 11.13 (#34980)

* agnos 11.12

* new build

* camerad: don't gate first frame on FSIN (#34972)

don't wait

* Remove duplicate radar fault event (#34917)

🤦

---------

Co-authored-by: Shane Smiskol <shane@smiskol.com>
2025-04-07 09:55:31 -07:00
126 changed files with 6427 additions and 228 deletions
+3
View File
@@ -4,5 +4,8 @@
"ms-vscode.cpptools",
"elagil.pre-commit-helper",
"charliermarsh.ruff",
"JamiTech.simply-blame",
"k--kato.intellij-idea-keybindings",
"trinm1709.dracula-theme-from-intellij"
]
}
+16 -1
View File
@@ -3,10 +3,25 @@
"editor.insertSpaces": true,
"editor.renderWhitespace": "trailing",
"files.trimTrailingWhitespace": true,
"terminal.integrated.defaultProfile.linux": "dragonpilot",
"terminal.integrated.profiles.linux": {
"dragonpilot": {
"path": "bash",
"args": ["-c", "distrobox enter dp"]
}
},
"search.exclude": {
"**/.git": true,
"**/.venv": true,
"**/__pycache__": true
"**/__pycache__": true,
"msgq_repo/": true,
"opendbc/": true,
"rednose/": true,
"rednose_repo/": true,
"openpilot/": true,
"teleoprtc_repo/": true,
"tinygrad/": true,
"tinygrad_repo/": true
},
"files.exclude": {
"**/.git": true,
+77
View File
@@ -0,0 +1,77 @@
```mermaid
flowchart TD
B000["upstream-tracking"] ---> CORE["core"]
CORE ---> CORE_001["core-feat/params"]
CORE_001 ---> CORE_002["core-feat/panel"]
CORE_002 ---> CORE_003["core-feat/spin-box-ctrl"]
CORE_003 ---> MIN["min"]
MIN ---> MIN_001["min-feat/dev/o3"]
MIN ---> MIN_002["min-feat/lat/alka"]
MIN ---> MIN_003["min-feat/ui/display-mode"]
MIN ---> MIN_004["min-feat/dev/model-selector"]
MIN ---> MIN_005["min-feat/lat/lca"]
MIN ---> MIN_006["min-feat/dev/on-off-road"]
MIN ---> MIN_007["min-feat/ui/hide-hud"]
MIN ---> MIN_008["min-feat/lon/ext-radar"]
MIN ---> MIN_009["min-feat/lat/road-edge-detection"]
MIN ---> MIN_010["min-feat/ui/rainbow-path"]
MIN ---> MIN_011["min-feat/lon/acm"]
MIN ---> MIN_012["min-feat/lon/aem"]
MIN ---> MIN_013["min-feat/dev/alert-mode"]
MIN ---> MIN_014["min-feat/lon/max-speed"]
MIN ---> MIN_015["min-feat/lon/no-gas-gating"]
MIN ---> MIN_016["min-feat/dev/auto-shutdown"]
MIN ---> MIN_017["min-feat/ui/radar-tracks"]
MIN ---> MIN_018["min-feat/ui/border-indicators"]
MIN ---> MIN_019["min-feat/dev/fileserv"]
MIN_001 ---> FULL["full"]
MIN_002 ---> FULL
MIN_003 ---> FULL
MIN_004 ---> FULL
MIN_005 ---> FULL
MIN_006 ---> FULL
MIN_007 ---> FULL
MIN_008 ---> FULL
MIN_009 ---> FULL
MIN_010 ---> FULL
MIN_011 ---> FULL
MIN_012 ---> FULL
MIN_013 ---> FULL
MIN_014 ---> FULL
MIN_015 ---> FULL
MIN_016 ---> FULL
MIN_017 ---> FULL
MIN_018 ---> FULL
MIN_019 ---> FULL
FULL ---> TOYOTA_001[brand/toyota/door-auto-lock-unlock]
FULL ---> TOYOTA_002[brand/toyota/tss1-sng]
FULL ---> TOYOTA_003[brand/toyota/long-filter-common]
FULL ---> TOYOTA_004[brand/toyota/radar-filter]
FULL ---> TOYOTA_005[brand/toyota/sdsu]
FULL ---> TOYOTA_006[brand/toyota/zss]
FULL ---> TOYOTA_007[brand/toyota/stock-lon]
FULL ---> VAG_001[brand/vag/a0-sng]
FULL ---> VAG_002[brand/vag/pq-steering-patch]
FULL ---> VAG_003[brand/vag/pq-no-dashcam]
FULL ---> VAG_004[brand/vag/avoid-eps-lockout]
FULL ---> HKG_001[brand/hkg/smdps]
TOYOTA_001 ---> TOYOTA[pre-toyota]
TOYOTA_002 ---> TOYOTA
TOYOTA_003 ---> TOYOTA
TOYOTA_004 ---> TOYOTA
TOYOTA_005 ---> TOYOTA
TOYOTA_006 ---> TOYOTA
TOYOTA_007 ---> TOYOTA
VAG_001 ---> VAG[pre-vag]
VAG_002 ---> VAG
VAG_003 ---> VAG
VAG_004 ---> VAG
HKG_001 ---> HKG[pre-hkg]
TOYOTA ---> PRE[pre]
VAG ---> PRE
HKG ---> PRE
PRE ---> PRE_PATCH[pre-patch]
PRE_PATCH ---> PRE_001[pre-build]
PRE_001 ---> PRERELEASE[pre-release]
PRERELEASE ---> RELEASE[x.x.x]
```
+1470
View File
File diff suppressed because it is too large Load Diff
Vendored
+2
View File
@@ -79,9 +79,11 @@ def deviceStage(String stageName, String deviceType, List extra_env, def steps)
return
}
/*
if (isReplay()) {
error("REPLAYING TESTS IS NOT ALLOWED. FIX THEM INSTEAD.")
}
*/
def extra = extra_env.collect { "export ${it}" }.join('\n');
def branch = env.BRANCH_NAME ?: 'master';
+20
View File
@@ -0,0 +1,20 @@
MIT Non-Commercial License
Copyright (c) 2019, dragonpilot
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
- Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
Copyright (c) 2018, Comma.ai, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+2 -1
View File
@@ -10,7 +10,8 @@ $Cxx.namespace("cereal");
# DO rename the structs
# DON'T change the identifier (e.g. @0x81c2f05a394cf4af)
struct CustomReserved0 @0x81c2f05a394cf4af {
struct DpControlsState @0x81c2f05a394cf4af {
alkaActive @0 :Bool;
}
struct CustomReserved1 @0xaedffd8f31e7b55d {
+1 -1
View File
@@ -2576,7 +2576,7 @@ struct Event {
# DO change the name of the field and struct
# DON'T change the ID (e.g. @107)
# DON'T change which struct it points to
customReserved0 @107 :Custom.CustomReserved0;
dpControlsState @107 :Custom.DpControlsState;
customReserved1 @108 :Custom.CustomReserved1;
customReserved2 @109 :Custom.CustomReserved2;
customReserved3 @110 :Custom.CustomReserved3;
+1
View File
@@ -91,6 +91,7 @@ _services: dict[str, tuple] = {
"customReservedRawData0": (True, 0.),
"customReservedRawData1": (True, 0.),
"customReservedRawData2": (True, 0.),
"dpControlsState": (False, 100., 10),
}
SERVICE_LIST = {name: Service(*vals) for
idx, (name, vals) in enumerate(_services.items())}
+30 -1
View File
@@ -26,7 +26,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"CurrentBootlog", PERSISTENT},
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
{"DisableLogging", PERSISTENT},
{"DisablePowerDown", PERSISTENT},
{"DisableUpdates", PERSISTENT},
{"DisengageOnAccelerator", PERSISTENT},
@@ -117,4 +117,33 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
{"UpdaterLastFetchTime", PERSISTENT},
{"Version", PERSISTENT},
{"dp_device_last_log", CLEAR_ON_MANAGER_START},
{"dp_device_reset_conf", CLEAR_ON_MANAGER_START},
{"dp_device_is_rhd", PERSISTENT},
{"dp_device_monitoring_disabled", PERSISTENT},
{"dp_device_beep", PERSISTENT},
{"dp_lat_alka", PERSISTENT},
{"dp_ui_display_mode", PERSISTENT},
{"dp_device_model_selected", PERSISTENT},
{"dp_device_model_list", PERSISTENT},
{"dp_lat_lca_speed", PERSISTENT},
{"dp_lat_lca_auto_sec", PERSISTENT},
{"dp_device_go_off_road", CLEAR_ON_MANAGER_START},
{"dp_ui_hide_hud_speed_kph", PERSISTENT},
{"dp_lon_ext_radar", PERSISTENT},
{"dp_lat_road_edge_detection", PERSISTENT},
{"dp_ui_rainbow", PERSISTENT},
{"dp_lon_acm", PERSISTENT},
{"dp_lon_acm_downhill", PERSISTENT},
{"dp_lon_aem", PERSISTENT},
{"dp_device_audible_alert_mode", PERSISTENT},
{"dp_lon_no_gas_gating", PERSISTENT},
{"dp_device_auto_shutdown_in", PERSISTENT},
{"dp_ui_radar_tracks", PERSISTENT},
{"dp_toyota_door_auto_lock_unlock", PERSISTENT},
{"dp_toyota_tss1_sng", PERSISTENT},
{"dp_toyota_stock_lon", PERSISTENT},
{"dp_vag_a0_sng", PERSISTENT},
{"dp_vag_pq_steering_patch", PERSISTENT},
{"dp_vag_avoid_eps_lockout", PERSISTENT},
};
View File
+84
View File
@@ -0,0 +1,84 @@
'''
MIT Non-Commercial License
Copyright (c) 2019, dragonpilot
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
- Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Downhill Coasting allows the vehicle to maintain or slightly increase speed on downhill slopes without braking.
'''
import numpy as np
SLOPE = -0.04
RATIO = 0.9
TTC = 5.
TTC_BP = [3.5, 2.5]
MIN_BRAKE_ALLOW_VALS = [0., -0.5]
class ACM:
def __init__(self):
self.enabled = False
self.downhill_only = False
self._is_downhill = False
self._is_speed_over_cruise = False
self._has_lead = False
self._active_prev = False
self.active = False
self.just_disabled = False
self.allowed_brake_val = 0.
def update_states(self, cs, rs, user_ctrl_lon, v_ego, v_cruise):
self.lead_ttc = float('inf') # Default if no lead
if not self.enabled:
self.active = False
return
if len(cs.orientationNED) != 3:
self.active = False
return
pitch_rad = cs.orientationNED[1]
self._is_downhill = np.sin(pitch_rad) < SLOPE
self._is_speed_over_cruise = v_ego > (v_cruise * RATIO)
lead = rs.leadOne
if lead and lead.status:
self.lead_ttc = lead.dRel / v_ego if v_ego > 0 else float('inf')
self.allowed_brake_val = np.interp(self.lead_ttc, TTC_BP, MIN_BRAKE_ALLOW_VALS)
self._has_lead = self.lead_ttc < TTC
else:
self._has_lead = False
self.active = not user_ctrl_lon and not self._has_lead and self._is_speed_over_cruise and (self._is_downhill if self.downhill_only else True)
self.just_disabled = self._active_prev and not self.active
self._active_prev = self.active
def update_a_desired_trajectory(self, a_desired_trajectory):
if not self.active:
return a_desired_trajectory
# Suppress all braking to allow smooth coasting
for i in range(len(a_desired_trajectory)):
if a_desired_trajectory[i] < 0 and a_desired_trajectory[i] > self.allowed_brake_val:
a_desired_trajectory[i] = 0.0
return a_desired_trajectory
def update_output_a_target(self, output_a_target):
if not self.active:
return output_a_target
# Suppress braking
if output_a_target < 0 and output_a_target > self.allowed_brake_val:
output_a_target = 0.0
return output_a_target
+387
View File
@@ -0,0 +1,387 @@
'''
MIT Non-Commercial License
Copyright (c) 2019, dragonpilot
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
- Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Adaptive Experimental Mode (AEM) switches between ACC and Blended based on driving context.
'''
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.realtime import DT_MDL
import numpy as np
from cereal import log
class AEM:
# --- Configuration Constants ---
# Speed thresholds (m/s)
SPEED_THRESHOLD_HIGHWAY = 22.23 # m/s (approx. 80 kph)
SPEED_THRESHOLD_CITY = 15.27 # m/s (approx. 55 kph)
SPEED_THRESHOLD_LOW = 5.56 # m/s (approx. 20 kph)
SPEED_THRESHOLD_CREEP = 2.23 # m/s (approx. 8 kph)
# Lead related thresholds
LEAD_TTC_CRITICAL = 1.75 # seconds, time to collision
LEAD_TTC_CAUTION = 3.0
LEAD_DIST_VERY_CLOSE = 10.0 # meters
LEAD_DIST_FAR_HIGHWAY = 85.0 # meters, for considering lead far enough on highway
LEAD_DIST_DEFAULT_NO_LEAD = 150.0 # Default distance for EMA when no lead
LEAD_ACCEL_HARD_BRAKE = -3.0 # m/s^2
LEAD_ACCEL_MILD_BRAKE = -2.0 # m/s^2
LEAD_ACCEL_PULLING_AWAY = 0.5 # m/s^2
# Steering thresholds
STEERING_ANGLE_ABS_HIGH_CURVATURE = 45.0 # degrees (EMA value)
# Hysteresis & Timers
HYSTERESIS_FRAMES_TO_SWITCH = 10 # Approx 0.5s at 20Hz
LEAD_LOST_FRAMES_TO_FALLBACK_BASE = 40 # Approx 2s at 20Hz
# EMA filter time constants (seconds) - THESE ARE DESIGN PARAMETERS
EMA_TC_V_EGO = 1.0
EMA_TC_LEAD_DREL = 0.5
EMA_TC_LEAD_V_ABS = 0.5 # Filters absolute lead speed
EMA_TC_LEAD_ALEAD = 0.5
EMA_TC_STEERING_ANGLE_ABS = 0.8
EMA_TC_V_MODEL_ERROR = 1.0
# Model & Planner Related Thresholds
MODEL_VEL_ERROR_THRESHOLD = 2.0 # m/s (EMA value)
MIN_VISION_LEAD_PROB_ACTION = 0.5 # Min modelProb for acting on vision-only leads
# Other
REL_SPEED_SIGNIFICANT_DIFFERENCE = 2.5 # m/s
def __init__(self, debug=False):
self.enabled = False
self._current_mpc_mode = 'acc' # Default to ACC
self._target_mode_suggestion = None
self._mode_switch_counter = 0
self.debug = debug
self._lead_id_prev = -1
self._lead_absence_frames = 0
self.personality = log.LongitudinalPersonality.standard
# --- Calculate alpha values from time constants (tau) ---
def get_alpha(tau, dt):
"""Calculates EMA alpha from time constant and time step."""
# Ensure tau > 0 to avoid division by zero if dt=0 somehow
# Also handle potential case where dt is zero
return dt / (tau + dt) if tau > 1e-5 and dt > 1e-5 else 1.0 # Default to alpha=1 (no filtering) if tau or dt is invalid/zero
alpha_v_ego = get_alpha(AEM.EMA_TC_V_EGO, DT_MDL)
alpha_lead_drel = get_alpha(AEM.EMA_TC_LEAD_DREL, DT_MDL)
alpha_lead_v_abs = get_alpha(AEM.EMA_TC_LEAD_V_ABS, DT_MDL)
alpha_lead_alead = get_alpha(AEM.EMA_TC_LEAD_ALEAD, DT_MDL)
alpha_steering_angle_abs = get_alpha(AEM.EMA_TC_STEERING_ANGLE_ABS, DT_MDL)
alpha_v_model_error = get_alpha(AEM.EMA_TC_V_MODEL_ERROR, DT_MDL)
# --- Initialize EMA Filters using alpha ---
# Ensure FirstOrderFilter takes 'alpha' as keyword arg
try:
self._v_ego_ema = FirstOrderFilter(0.0, alpha=alpha_v_ego, dt=DT_MDL)
self._lead_drel_ema = FirstOrderFilter(AEM.LEAD_DIST_DEFAULT_NO_LEAD, alpha=alpha_lead_drel, dt=DT_MDL)
self._lead_v_ema = FirstOrderFilter(0.0, alpha=alpha_lead_v_abs, dt=DT_MDL) # Filters absolute V_lead
self._lead_alead_ema = FirstOrderFilter(0.0, alpha=alpha_lead_alead, dt=DT_MDL)
self._steering_angle_abs_ema = FirstOrderFilter(0.0, alpha=alpha_steering_angle_abs, dt=DT_MDL)
self._v_model_error_ema = FirstOrderFilter(0.0, alpha=alpha_v_model_error, dt=DT_MDL)
except TypeError:
# Fallback if the specific FirstOrderFilter expects positional args differently
print("Warning: FirstOrderFilter init failed with keyword 'alpha', attempting positional.")
self._v_ego_ema = FirstOrderFilter(0.0, alpha_v_ego, DT_MDL)
self._lead_drel_ema = FirstOrderFilter(AEM.LEAD_DIST_DEFAULT_NO_LEAD, alpha_lead_drel, DT_MDL)
self._lead_v_ema = FirstOrderFilter(0.0, alpha_lead_v_abs, DT_MDL)
self._lead_alead_ema = FirstOrderFilter(0.0, alpha_lead_alead, DT_MDL)
self._steering_angle_abs_ema = FirstOrderFilter(0.0, alpha_steering_angle_abs, DT_MDL)
self._v_model_error_ema = FirstOrderFilter(0.0, alpha_v_model_error, DT_MDL)
self._log(f"AEM Initialized. Alphas: v_ego={alpha_v_ego:.3f}, dRel={alpha_lead_drel:.3f}, vLead={alpha_lead_v_abs:.3f}, aLead={alpha_lead_alead:.3f}, steer={alpha_steering_angle_abs:.3f}, v_err={alpha_v_model_error:.3f}")
def _log(self, msg: str):
"""Logs debug messages if debug is enabled."""
if self.debug:
# Consider using cloudlog for persistent logs during testing
# from openpilot.common.swaglog import cloudlog
# cloudlog.debug(f"[AEM]: {msg}")
print(f"[AEM]: {msg}")
def _calculate_ttc(self, dist: float, ego_speed: float, lead_speed: float) -> float:
"""Calculates Time To Collision (TTC), returns infinity if collision is not imminent."""
relative_speed = ego_speed - lead_speed
if dist > 0.1 and relative_speed > 0.3: # Thresholds to avoid noise and division issues
# Avoid division by zero or very small numbers
return max(0.0, dist / relative_speed) # Return non-negative TTC
return float('inf') # Return infinity if no collision course or relative speed is too low
def _reset_lead_emas(self, d_lead_raw, v_lead_raw, a_lead_raw):
"""Helper to reset/initialize lead EMAs' state."""
# This assumes direct state access/setting via '.x' is the correct method
# for the FirstOrderFilter implementation being used.
try:
self._lead_drel_ema.x = float(d_lead_raw)
self._lead_v_ema.x = float(v_lead_raw)
self._lead_alead_ema.x = float(a_lead_raw)
# Ensure filter knows it's 'initialized' if it uses that flag internally
self._lead_drel_ema.initialized = True
self._lead_v_ema.initialized = True
self._lead_alead_ema.initialized = True
self._log(f"Reset lead EMAs with raw values: d={d_lead_raw:.2f}, v={v_lead_raw:.2f}, a={a_lead_raw:.2f}")
except AttributeError:
self._log("Warning: Could not directly set '.x' on FirstOrderFilter. Reset may not be immediate.")
# If direct access fails, update might be the only way, causing a slight delay in reset
self._lead_drel_ema.update(d_lead_raw)
self._lead_v_ema.update(v_lead_raw)
self._lead_alead_ema.update(a_lead_raw)
def set_personality(self, v_ego, personality):
self.personality = personality
if self.enabled:
self.personality = log.LongitudinalPersonality.aggressive if v_ego > 16.67 else self.personality
return self.personality
# Note: Removed 'model_predicted_max_curvature_upcoming_raw' from signature
def get_mode(self, v_ego_raw: float, lead_one_data_raw, steering_angle_deg_raw: float, standstill_raw: bool,
long_personality: int, v_model_error_raw: float, allow_throttle_planner: bool,
model_path_plan_raw: dict, a_target_from_prev_cycle: float, model_predicts_stop_prev: bool,
fcw_active_prev: bool, mpc_source_prev: str) -> str:
"""
Determines the appropriate MPC mode ('acc' or 'blended') based on current conditions.
ACC is primary, Blended is assist.
"""
if not self.enabled:
# Should not be called if not enabled, but return primary mode as safeguard
return 'acc'
# 0. Initialize suggested mode with AEM's current internal state
suggested_mode = self._current_mpc_mode
# 1. Update common EMA filters
self._v_ego_ema.update(v_ego_raw)
self._steering_angle_abs_ema.update(abs(steering_angle_deg_raw))
# Use absolute value of model error for thresholding
self._v_model_error_ema.update(abs(v_model_error_raw))
# 2. Process Lead Data & Update Lead EMAs
lead_status = lead_one_data_raw.status
current_lead_id = lead_one_data_raw.radarTrackId if lead_status else -1
d_lead_raw = lead_one_data_raw.dRel if lead_status else AEM.LEAD_DIST_DEFAULT_NO_LEAD
v_lead_raw = lead_one_data_raw.vLead if lead_status else self._v_ego_ema.x # Default to filtered ego if no lead
a_lead_raw = lead_one_data_raw.aLeadK if lead_status else 0.0
model_prob_lead = lead_one_data_raw.modelProb if lead_status else 0.0
if lead_status:
# Reset EMA if lead ID changes significantly (from a valid previous ID)
if current_lead_id != self._lead_id_prev and self._lead_id_prev != -1:
self._log(f"Lead ID changed: {self._lead_id_prev} -> {current_lead_id}.")
self._reset_lead_emas(d_lead_raw, v_lead_raw, a_lead_raw)
# Reset EMA if acquiring lead from no-lead state
elif self._lead_id_prev == -1 and current_lead_id != -1:
self._log(f"Lead acquired: ID {current_lead_id}.")
self._reset_lead_emas(d_lead_raw, v_lead_raw, a_lead_raw)
# Update filters with current raw data
self._lead_drel_ema.update(d_lead_raw)
self._lead_v_ema.update(v_lead_raw)
self._lead_alead_ema.update(a_lead_raw)
self._lead_absence_frames = 0
else: # No lead currently
self._lead_absence_frames += 1
# If lead was just lost, reset EMAs to default values
if self._lead_id_prev != -1:
self._log(f"Lead lost (Prev ID: {self._lead_id_prev}). Resetting lead EMAs to defaults.")
# Reset using the helper function ensures consistency
self._reset_lead_emas(AEM.LEAD_DIST_DEFAULT_NO_LEAD, self._v_ego_ema.x, 0.0)
else: # Still no lead, continue updating towards defaults
self._lead_drel_ema.update(AEM.LEAD_DIST_DEFAULT_NO_LEAD)
self._lead_v_ema.update(self._v_ego_ema.x) # Update towards filtered ego speed
self._lead_alead_ema.update(0.0) # Update towards zero accel
# Update previous lead ID for next cycle comparison *after* using it
self._lead_id_prev = current_lead_id
# 3. Get Filtered Values for decision making
v_ego = self._v_ego_ema.x
d_lead = self._lead_drel_ema.x
v_lead = self._lead_v_ema.x # Filtered absolute lead speed
a_lead = self._lead_alead_ema.x
steering_angle_abs = self._steering_angle_abs_ema.x
v_model_error = self._v_model_error_ema.x
ttc = self._calculate_ttc(d_lead, v_ego, v_lead)
is_lead_one_vision_only = lead_status and current_lead_id == -1
# 4. Infer Current Model Intentions from raw plan
raw_model_stop_intention_current_cycle = False
if model_path_plan_raw and 'v' in model_path_plan_raw:
model_v_traj = model_path_plan_raw['v']
if len(model_v_traj) >= 5:
avg_final_model_v = np.mean(model_v_traj[-5:])
final_model_v = model_v_traj[-1]
# Consider stop if avg end speed is near creep and final point is very low
raw_model_stop_intention_current_cycle = avg_final_model_v < AEM.SPEED_THRESHOLD_CREEP and \
final_model_v < AEM.SPEED_THRESHOLD_CREEP * 0.7
elif len(model_v_traj) > 0: # Fallback for shorter trajectories
raw_model_stop_intention_current_cycle = np.isclose(model_v_traj[-1], 0.0, atol=0.3)
# --- 5. Mode Selection Logic ---
min_prob_for_action = AEM.MIN_VISION_LEAD_PROB_ACTION if is_lead_one_vision_only else 0.0
# A. Evaluate conditions to switch TO BLENDED (from ACC)
if self._current_mpc_mode == 'acc':
needs_blended_assist = False
reason = "None" # Default reason
# Scenario 1: Emergency/Dangerous Lead Situation
if lead_status and ttc < AEM.LEAD_TTC_CRITICAL and v_ego > AEM.SPEED_THRESHOLD_LOW and model_prob_lead >= min_prob_for_action:
needs_blended_assist, reason = True, f"Critical TTC ({ttc:.2f}s)"
elif lead_status and a_lead < AEM.LEAD_ACCEL_HARD_BRAKE and d_lead < (v_ego * 2.5) and model_prob_lead >= min_prob_for_action:
needs_blended_assist, reason = True, f"Hard lead brake ({a_lead:.2f}), d={d_lead:.1f}"
elif fcw_active_prev:
needs_blended_assist, reason = True, "FCW previously active"
# Scenario 2: Sudden Lead Cut-in/Appearance
# Check only if not already triggered by emergency
if not needs_blended_assist and lead_status and current_lead_id != self._lead_id_prev and self._lead_id_prev != -1 and \
ttc < AEM.LEAD_TTC_CAUTION and d_lead < (AEM.LEAD_DIST_VERY_CLOSE * 2.5) and model_prob_lead >= min_prob_for_action:
needs_blended_assist, reason = True, f"Sudden cut-in (TTC={ttc:.2f}, d={d_lead:.1f})"
# Scenario 3: Low-Speed/Urban/Congestion (Lead)
if not needs_blended_assist and lead_status and v_ego < AEM.SPEED_THRESHOLD_LOW and \
d_lead < (AEM.LEAD_DIST_VERY_CLOSE * 1.8) and model_prob_lead >= min_prob_for_action:
needs_blended_assist, reason = True, f"Low speed ({v_ego:.1f}) close lead ({d_lead:.1f})"
# Scenario 4: Model Predicts Stop
if not needs_blended_assist and \
(raw_model_stop_intention_current_cycle or model_predicts_stop_prev) and \
v_ego > AEM.SPEED_THRESHOLD_CREEP:
needs_blended_assist, reason = True, f"Model predicts stop (curr={raw_model_stop_intention_current_cycle}, prev={model_predicts_stop_prev})"
# Scenario 5: High Curvature/Urban Turns (Curvature input removed)
if not needs_blended_assist and \
steering_angle_abs > AEM.STEERING_ANGLE_ABS_HIGH_CURVATURE and \
v_ego < AEM.SPEED_THRESHOLD_CITY:
needs_blended_assist, reason = True, f"High steering angle ({steering_angle_abs:.1f})"
# Scenario 6: Planner Already Braking (Prev Cycle)
if not needs_blended_assist and \
a_target_from_prev_cycle < AEM.LEAD_ACCEL_MILD_BRAKE and \
not standstill_raw and v_ego > AEM.SPEED_THRESHOLD_CREEP:
needs_blended_assist, reason = True, f"Planner prev brake ({a_target_from_prev_cycle:.2f})"
# Scenario 7: MPC Favored E2E (Prev Cycle) & still complex
if not needs_blended_assist and mpc_source_prev == 'e2e':
is_complex = (v_ego < AEM.SPEED_THRESHOLD_CITY or \
(lead_status and ttc < AEM.LEAD_TTC_CAUTION and model_prob_lead >= min_prob_for_action) or \
(steering_angle_abs > AEM.STEERING_ANGLE_ABS_HIGH_CURVATURE * 0.6)) # Removed curvature check
if is_complex:
needs_blended_assist, reason = True, "Prev E2E source & ongoing complexity"
# Scenario 8: High Gas Disengage Prob. (Model)
if not needs_blended_assist and not allow_throttle_planner and \
(not lead_status or (lead_status and v_ego < (v_lead + AEM.REL_SPEED_SIGNIFICANT_DIFFERENCE * 0.5))):
needs_blended_assist, reason = True, "Model advises against throttle"
if needs_blended_assist:
suggested_mode = 'blended'
self._log(f"ACC->BLENDED Trigger: {reason}")
# B. Evaluate conditions to switch TO ACC (from Blended)
elif self._current_mpc_mode == 'blended':
# Assume staying in blended unless conditions strongly favor returning to ACC
suggested_mode = 'blended'
# Check if any critical Blended-triggering condition is still fundamentally active
blended_condition_still_active = False
active_blended_reason = "None"
if lead_status and ttc < AEM.LEAD_TTC_CRITICAL and v_ego > AEM.SPEED_THRESHOLD_LOW and model_prob_lead >= min_prob_for_action: blended_condition_still_active, active_blended_reason = True, "Critical TTC"
elif lead_status and a_lead < AEM.LEAD_ACCEL_HARD_BRAKE and model_prob_lead >= min_prob_for_action: blended_condition_still_active, active_blended_reason = True, "Hard Lead Brake"
elif fcw_active_prev: blended_condition_still_active, active_blended_reason = True, "Prev FCW"
elif (raw_model_stop_intention_current_cycle or model_predicts_stop_prev) and v_ego > AEM.SPEED_THRESHOLD_CREEP: blended_condition_still_active, active_blended_reason = True, "Model Predicts Stop"
elif steering_angle_abs > AEM.STEERING_ANGLE_ABS_HIGH_CURVATURE and v_ego < AEM.SPEED_THRESHOLD_CITY: blended_condition_still_active, active_blended_reason = True, "High Steering Angle"
elif a_target_from_prev_cycle < AEM.LEAD_ACCEL_MILD_BRAKE and not standstill_raw and v_ego > AEM.SPEED_THRESHOLD_CREEP: blended_condition_still_active, active_blended_reason = True, "Prev Planner Brake"
elif not allow_throttle_planner and (not lead_status or (lead_status and v_ego < (v_lead + AEM.REL_SPEED_SIGNIFICANT_DIFFERENCE * 0.5))): blended_condition_still_active, active_blended_reason = True, "Gas Disengage Prob"
elif lead_status and v_ego < AEM.SPEED_THRESHOLD_LOW and \
d_lead < (AEM.LEAD_DIST_VERY_CLOSE * 1.8) and \
model_prob_lead >= min_prob_for_action: # Using filtered values (v_ego, d_lead) as in other checks
blended_condition_still_active, active_blended_reason = True, f"Persisting Low speed ({v_ego:.1f}) close lead ({d_lead:.1f})"
# Low speed congestion is implicitly handled by other checks usually involving TTC or stops
if not blended_condition_still_active:
safe_to_return_to_acc = False
reason = ""
# Scenario 10: Highway Cruising - Excellent Conditions
if v_ego > AEM.SPEED_THRESHOLD_HIGHWAY and \
steering_angle_abs < (AEM.STEERING_ANGLE_ABS_HIGH_CURVATURE * 0.3) and \
(not lead_status or d_lead > (AEM.LEAD_DIST_FAR_HIGHWAY * 0.8) or ttc > (AEM.LEAD_TTC_CAUTION * 1.5)):
safe_to_return_to_acc, reason = True, "Highway cruise, clear path"
# Scenario 11: Stable Following - Safe Distance Achieved
elif lead_status and v_ego > AEM.SPEED_THRESHOLD_LOW and \
ttc > AEM.LEAD_TTC_CAUTION and \
d_lead > (AEM.LEAD_DIST_VERY_CLOSE * 2.0) and \
abs(a_lead) < (AEM.LEAD_ACCEL_PULLING_AWAY * 0.8) and \
abs(v_ego - v_lead) < (AEM.REL_SPEED_SIGNIFICANT_DIFFERENCE * 0.75) and \
steering_angle_abs < (AEM.STEERING_ANGLE_ABS_HIGH_CURVATURE * 0.5):
safe_to_return_to_acc, reason = True, f"Stable following (TTC={ttc:.2f}, d={d_lead:.1f})"
# Scenario 12: Prolonged Lead Absence
elif not lead_status:
personality_factor = 1.3 if long_personality == 0 else (0.7 if long_personality == 2 else 1.0)
fallback_frames = AEM.LEAD_LOST_FRAMES_TO_FALLBACK_BASE * personality_factor
if self._lead_absence_frames > fallback_frames and v_ego > AEM.SPEED_THRESHOLD_LOW:
safe_to_return_to_acc, reason = True, f"Prolonged lead absence ({self._lead_absence_frames} frames)"
# Scenario 13: Persistent High Model Velocity Error
elif v_model_error > AEM.MODEL_VEL_ERROR_THRESHOLD:
safe_to_return_to_acc, reason = True, f"High model vel error ({v_model_error:.2f})"
# Default fallback if no active blended condition and no specific ACC condition met
elif not safe_to_return_to_acc:
safe_to_return_to_acc, reason = True, "No active Blended condition found"
if safe_to_return_to_acc:
suggested_mode = 'acc'
self._log(f"BLENDED->ACC Trigger: {reason}")
else:
self._log(f"Staying BLENDED due to active condition: {active_blended_reason}")
# --- 6. Apply Hysteresis ---
if suggested_mode != self._current_mpc_mode:
# If target mode changes, reset counter
if self._target_mode_suggestion != suggested_mode:
self._target_mode_suggestion = suggested_mode
self._mode_switch_counter = 1
self._log(f"Target mode suggestion: {self._target_mode_suggestion}. Counter: {self._mode_switch_counter}")
# If target mode remains the same, increment counter
else:
self._mode_switch_counter += 1
# Log counter increment for debugging flapping
# self._log(f"Target mode {self._target_mode_suggestion} persists. Counter: {self._mode_switch_counter}")
# Check if threshold is met to execute switch
if self._mode_switch_counter >= AEM.HYSTERESIS_FRAMES_TO_SWITCH:
self._log(f"\n-----------------------\nExecuting switch from {self._current_mpc_mode} to {self._target_mode_suggestion}\n-----------------------")
self._current_mpc_mode = self._target_mode_suggestion
# Reset hysteresis state after switch
self._mode_switch_counter = 0
self._target_mode_suggestion = None
# else: remain in _current_mpc_mode until counter threshold met
else: # Suggested mode is the same as current mode
# If there was a pending switch suggestion, cancel it
if self._target_mode_suggestion is not None:
self._log(f"Mode suggestion {suggested_mode} matches current mode {self._current_mpc_mode}, cancelling pending switch to {self._target_mode_suggestion}")
# Reset hysteresis state
self._target_mode_suggestion = None
self._mode_switch_counter = 0
# Return the potentially updated current mode
return self._current_mpc_mode
+260
View File
@@ -0,0 +1,260 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
MIT Non-Commercial License
Copyright (c) 2019, dragonpilot
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
- Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'''
import os
import signal
import threading
import socket
import math
from datetime import datetime
from urllib.parse import urlparse, parse_qs, quote
HOST = '0.0.0.0'
PORT = 5000
DEFAULT_DIR = os.path.realpath('/data/media/0/realdata')
def format_file_size(size_bytes):
"""Formats a file size in bytes to a human-readable string (B, KB, MB, GB)."""
if size_bytes == 0:
return "0 B"
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 1)
return f"{s} {size_name[i]}"
def get_safe_path(requested_path):
"""Joins the requested path with the base directory and resolves it, ensuring it's safe."""
combined_path = os.path.join(DEFAULT_DIR, requested_path.lstrip('/'))
safe_path = os.path.realpath(combined_path)
if os.path.commonprefix((safe_path, DEFAULT_DIR)) == DEFAULT_DIR:
return safe_path
return None
def list_directory(client_socket, path):
try:
if not os.path.isdir(path):
response = "HTTP/1.1 404 Not Found\r\n\r\n"
client_socket.sendall(response.encode())
return
items = []
for entry in os.listdir(path):
full_path = os.path.join(path, entry)
try:
stat = os.stat(full_path)
is_dir = os.path.isdir(full_path)
items.append({
'name': entry,
'is_dir': is_dir,
'mtime': datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'),
'size': stat.st_size if not is_dir else 0
})
except FileNotFoundError:
continue
items.sort(key=lambda x: (not x['is_dir'], x['name'].lower()))
# --- HTML and CSS for the page ---
content = """
<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta charset="UTF-8">
<title>FileServ by dragonpilot</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; margin: 0; background-color: #f8f9fa; color: #212529; }
.container { max-width: 960px; margin: 20px auto; padding: 0 10px; }
h3 { border-bottom: 2px solid #dee2e6; padding-bottom: 10px; margin-top: 0; font-weight: 500; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #dee2e6; white-space: nowrap; }
th { background-color: #f1f3f5; font-weight: 600; }
tr:hover { background-color: #f1f3f5; }
a { text-decoration: none; color: #007bff; font-weight: 500; }
a:hover { text-decoration: underline; }
.play-link { font-weight: bold; color: #28a745; font-size: 1.5em; text-align: center; display: block; }
th.actions-col, td.actions-col { width: 1%; padding-right: 5px; padding-left: 5px; }
.name-col { word-break: break-all; white-space: normal; }
td.size { text-align: right; font-family: "Menlo", "Consolas", "Courier New", monospace; color: #495057; }
.path-links { margin-bottom: 20px; font-size: 1.1em; word-wrap: break-word; }
.path-links a { color: #495057; }
/* Responsive styles for mobile */
@media screen and (max-width: 600px) {
.container { margin: 0; padding: 0; }
h3, .path-links { padding-left: 10px; padding-right: 10px; }
th, td { padding: 10px 8px; }
.hide-on-mobile { display: none; }
}
</style>
</head>
<body>
<div class="container">
"""
# Breadcrumb navigation
content += "<h3>FileServ by dragonpilot</h3>"
content += "<div class='path-links'><strong>Path:</strong> "
relative_path_from_root = os.path.relpath(path, DEFAULT_DIR)
content += "<a href='/'>/</a>"
if relative_path_from_root != ".":
current_href = ""
for part in relative_path_from_root.split(os.sep):
current_href = os.path.join(current_href, part)
content += f" <a href='/{current_href}/'>{part}</a> /"
content += "</div>"
# Table header - Swapped 'hide-on-mobile' from Last Modified to Size
content += "<table><thead><tr><th class='actions-col'></th><th>Name</th><th>Last Modified</th><th class='size hide-on-mobile'>Size</th></tr></thead><tbody>"
# Parent directory link
if path != DEFAULT_DIR:
content += "<tr><td class='actions-col'></td><td class='name-col'><a href='..'>.. (Parent Directory)</a></td><td></td><td class='size hide-on-mobile'>-</td></tr>"
# Table rows for directories and files
for item in items:
relative_item_path = os.path.relpath(os.path.join(path, item['name']), DEFAULT_DIR)
encoded_path = quote(relative_item_path)
size_str = format_file_size(item['size']) if not item['is_dir'] else '-'
actions_cell = ""
if not item['is_dir'] and item['name'].endswith('.ts'):
actions_cell = f"<a href='/play?file={encoded_path}' class='play-link' target='_blank'>▶</a>"
# Assemble the row with the 'hide-on-mobile' class on the correct cell
content += f"<tr>"
content += f"<td class='actions-col'>{actions_cell}</td>"
content += f"<td class='name-col'><a href='/{encoded_path}{'/' if item['is_dir'] else ''}'>{item['name']}</a></td>"
content += f"<td>{item['mtime']}</td>"
content += f"<td class='size hide-on-mobile'>{size_str}</td>"
content += f"</tr>"
content += "</tbody></table></div></body></html>"
body = content.encode("utf-8")
response_headers = f"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {len(body)}\r\n\r\n"
client_socket.sendall(response_headers.encode())
client_socket.sendall(body)
except Exception as e:
response = f"HTTP/1.1 500 Internal Server Error\r\n\r\n<h3>500 Internal Server Error</h3><p>{e}</p>"
client_socket.sendall(response.encode())
def serve_player(client_socket, file_path):
encoded_path = quote(file_path)
html = f"""
<!DOCTYPE html><html><head><title>HLS Player</title><style>body,html{{margin:0;padding:0;height:100%;background-color:#000}}#video{{width:100%;height:100%}}</style><script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script></head><body><video id="video" controls autoplay></video><script>var v=document.getElementById('video'),s='/manifest.m3u8?file={encoded_path}';if(Hls.isSupported()){{var h=new Hls();h.loadSource(s);h.attachMedia(v)}}else if(v.canPlayType('application/vnd.apple.mpegurl')){{v.src=s}}</script></body></html>"""
body = html.encode('utf-8')
headers = f"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {len(body)}\r\n\r\n"
client_socket.sendall(headers.encode())
client_socket.sendall(body)
def serve_manifest(client_socket, file_path):
encoded_path = quote(file_path)
manifest = f"""#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:60\n#EXT-X-PLAYLIST-TYPE:VOD\n#EXTINF:60.0,\n/{encoded_path}\n#EXT-X-ENDLIST\n"""
body = manifest.encode('utf-8')
headers = f"HTTP/1.1 200 OK\r\nContent-Type: application/vnd.apple.mpegurl\r\nContent-Length: {len(body)}\r\n\r\n"
client_socket.sendall(headers.encode())
client_socket.sendall(body)
def handle_request(client_socket):
try:
request = client_socket.recv(1024).decode('utf-8')
if not request: return
first_line = request.split('\r\n')[0]
parts = first_line.split()
if len(parts) < 2: return
method, full_path = parts[0], parts[1]
url = urlparse(full_path)
path_qs = parse_qs(url.query)
file_param = path_qs.get('file', [None])[0]
if url.path == '/play' and file_param:
serve_player(client_socket, file_param)
return
if url.path == '/manifest.m3u8' and file_param:
serve_manifest(client_socket, file_param)
return
safe_path = get_safe_path(url.path)
if not safe_path:
client_socket.sendall(b"HTTP/1.1 403 Forbidden\r\n\r\nAccess denied.")
return
if os.path.isfile(safe_path):
file_extension = os.path.splitext(safe_path)[1].lower()
content_type = "application/octet-stream"
if file_extension == '.ts':
content_type = "video/mp2t"
file_size = os.path.getsize(safe_path)
response = f"HTTP/1.1 200 OK\r\nContent-Type: {content_type}\r\nContent-Length: {file_size}\r\n\r\n"
client_socket.sendall(response.encode())
with open(safe_path, 'rb') as f:
while chunk := f.read(8192):
try:
client_socket.sendall(chunk)
except BrokenPipeError:
break
elif os.path.isdir(safe_path):
list_directory(client_socket, safe_path)
else:
client_socket.sendall(b"HTTP/1.1 404 Not Found\r\n\r\nNot Found.")
except Exception:
pass
finally:
client_socket.close()
def start_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
while True:
try:
client_socket, _ = server_socket.accept()
threading.Thread(target=handle_request, args=(client_socket,)).start()
except KeyboardInterrupt:
break
server_socket.close()
def main():
print(f"Server starting on http://{HOST}:{PORT}")
server_thread = threading.Thread(target=start_server)
server_thread.daemon = True
server_thread.start()
signal.signal(signal.SIGINT, lambda s, f: os._exit(0))
signal.signal(signal.SIGTERM, lambda s, f: os._exit(0))
server_thread.join()
if __name__ == "__main__":
main()
+308
View File
@@ -0,0 +1,308 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
MIT Non-Commercial License
Copyright (c) 2019, dragonpilot
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
- Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'''
import os
import signal
import threading
import socket
import math
from datetime import datetime
from urllib.parse import urlparse, parse_qs, quote
HOST = '0.0.0.0'
PORT = 5000
# --- Dynamic Path Configuration ---
DEFAULT_DIR = os.path.realpath('/data/media/0/realdata')
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
STATIC_DIR = os.path.join(SCRIPT_DIR, 'static')
def get_safe_path(base_dir, requested_path):
"""Joins the requested path with a base directory and ensures it's safe."""
normalized_req_path = os.path.normpath(requested_path.lstrip('/'))
combined_path = os.path.join(base_dir, normalized_req_path)
safe_path = os.path.realpath(combined_path)
if safe_path.startswith(os.path.realpath(base_dir)):
return safe_path
return None
def format_file_size(size_bytes):
"""Formats a file size in bytes to a human-readable string (B, KB, MB, GB)."""
if size_bytes == 0:
return "0 B"
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 1)
return f"{s} {size_name[i]}"
def list_directory(client_socket, path):
try:
if not os.path.isdir(path):
response = "HTTP/1.1 404 Not Found\r\n\r\n"
client_socket.sendall(response.encode())
return
items = []
for entry in os.listdir(path):
full_path = os.path.join(path, entry)
try:
stat = os.stat(full_path)
is_dir = os.path.isdir(full_path)
items.append({
'name': entry,
'is_dir': is_dir,
'mtime': datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S'),
'size': stat.st_size if not is_dir else 0
})
except FileNotFoundError:
continue
items.sort(key=lambda x: (not x['is_dir'], x['name'].lower()))
# --- HTML and CSS for the page ---
content = """
<!DOCTYPE html>
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta charset="UTF-8">
<title>dragonpilot FileServ</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; margin: 0; background-color: #f8f9fa; color: #212529; }
.container { max-width: 960px; margin: 20px auto; padding: 0 10px; }
h3 { border-bottom: 2px solid #dee2e6; padding-bottom: 10px; margin-top: 0; font-weight: 500; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #dee2e6; white-space: nowrap; }
th { background-color: #f1f3f5; font-weight: 600; }
tr:hover { background-color: #f1f3f5; }
a { text-decoration: none; color: #007bff; font-weight: 500; }
a:hover { text-decoration: underline; }
.play-link { font-weight: bold; color: #28a745; font-size: 1.5em; text-align: center; display: block; }
th.actions-col, td.actions-col { width: 1%; padding-right: 5px; padding-left: 5px; }
.name-col { word-break: break-all; white-space: normal; }
td.size { text-align: right; font-family: "Menlo", "Consolas", "Courier New", monospace; color: #495057; }
.path-links { margin-bottom: 20px; font-size: 1.1em; word-wrap: break-word; }
.path-links a { color: #495057; }
/* Responsive styles for mobile */
@media screen and (max-width: 600px) {
.container { margin: 0; padding: 0; }
h3, .path-links { padding-left: 10px; padding-right: 10px; }
th, td { padding: 10px 8px; }
.hide-on-mobile { display: none; }
}
</style>
</head>
<body>
<div class="container">
"""
# Breadcrumb navigation
content += "<h3>Fileserv by dragonpilot</h3>"
content += "<div class='path-links'><strong>Path:</strong> "
relative_path_from_root = os.path.relpath(path, DEFAULT_DIR)
content += "<a href='/'>/</a>"
if relative_path_from_root != ".":
current_href = ""
for part in relative_path_from_root.split(os.sep):
current_href = os.path.join(current_href, part)
content += f" <a href='/{current_href}/'>{part}</a> /"
content += "</div>"
# Table header
content += "<table><thead><tr><th class='actions-col'></th><th>Name</th><th>Last Modified</th><th class='size hide-on-mobile'>Size</th></tr></thead><tbody>"
# Parent directory link
if path != DEFAULT_DIR:
content += "<tr><td class='actions-col'></td><td class='name-col'><a href='..'>.. (Parent Directory)</a></td><td></td><td class='size hide-on-mobile'>-</td></tr>"
# Table rows for directories and files
for item in items:
relative_item_path = os.path.relpath(os.path.join(path, item['name']), DEFAULT_DIR)
encoded_path = quote(relative_item_path)
size_str = format_file_size(item['size']) if not item['is_dir'] else '-'
actions_cell = ""
if not item['is_dir'] and item['name'].endswith('.ts'):
actions_cell = f"<a href='/play?file={encoded_path}' class='play-link' target='_blank'>▶</a>"
content += f"<tr>"
content += f"<td class='actions-col'>{actions_cell}</td>"
content += f"<td class='name-col'><a href='/{encoded_path}{'/' if item['is_dir'] else ''}'>{item['name']}</a></td>"
content += f"<td>{item['mtime']}</td>"
content += f"<td class='size hide-on-mobile'>{size_str}</td>"
content += f"</tr>"
content += "</tbody></table></div></body></html>"
body = content.encode("utf-8")
response_headers = f"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {len(body)}\r\n\r\n"
client_socket.sendall(response_headers.encode())
client_socket.sendall(body)
except Exception as e:
response = f"HTTP/1.1 500 Internal Server Error\r\n\r\n<h3>500 Internal Server Error</h3><p>{e}</p>"
client_socket.sendall(response.encode())
def serve_static_file(client_socket, file_path):
"""Serves a static file from the filesystem."""
mime_types = {
'.js': 'application/javascript; charset=utf-8',
'.css': 'text/css; charset=utf-8',
}
ext = os.path.splitext(file_path)[1].lower()
content_type = mime_types.get(ext, 'application/octet-stream')
try:
with open(file_path, 'rb') as f:
body = f.read()
headers = f"HTTP/1.1 200 OK\r\nContent-Type: {content_type}\r\nContent-Length: {len(body)}\r\n\r\n"
client_socket.sendall(headers.encode())
client_socket.sendall(body)
except FileNotFoundError:
client_socket.sendall(b"HTTP/1.1 404 Not Found\r\n\r\nFile Not Found")
except Exception:
client_socket.sendall(b"HTTP/1.1 500 Internal Server Error\r\n\r\nError reading file")
def serve_player(client_socket, file_path):
"""Serves an HTML page with an HLS video player for .ts files."""
encoded_path = quote(file_path)
# The <script> src now points to our local static file
html = f"""
<!DOCTYPE html><html><head><title>HLS Player</title><style>body,html{{margin:0;padding:0;height:100%;background-color:#000}}#video{{width:100%;height:100%}}</style><script src="/static/hls.min.js"></script></head><body><video id="video" controls autoplay></video><script>var v=document.getElementById('video'),s='/manifest.m3u8?file={encoded_path}';if(Hls.isSupported()){{var h=new Hls();h.loadSource(s);h.attachMedia(v)}}else if(v.canPlayType('application/vnd.apple.mpegurl')){{v.src=s}}</script></body></html>"""
body = html.encode('utf-8')
headers = f"HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: {len(body)}\r\n\r\n"
client_socket.sendall(headers.encode())
client_socket.sendall(body)
def serve_manifest(client_socket, file_path):
"""Serves a dynamic .m3u8 manifest for a single .ts file."""
encoded_path = quote(file_path)
manifest = f"""#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:60\n#EXT-X-PLAYLIST-TYPE:VOD\n#EXTINF:60.0,\n/{encoded_path}\n#EXT-X-ENDLIST\n"""
body = manifest.encode('utf-8')
headers = f"HTTP/1.1 200 OK\r\nContent-Type: application/vnd.apple.mpegurl\r\nContent-Length: {len(body)}\r\n\r\n"
client_socket.sendall(headers.encode())
client_socket.sendall(body)
def handle_request(client_socket):
try:
request = client_socket.recv(1024).decode('utf-8')
if not request: return
first_line = request.split('\r\n')[0]
parts = first_line.split()
if len(parts) < 2: return
method, full_path = parts[0], parts[1]
url = urlparse(full_path)
# --- Route requests ---
# Route for static assets
if url.path.startswith('/static/'):
# **FIX:** The path passed to get_safe_path should be relative to STATIC_DIR.
# We strip the '/static/' prefix from the URL path to get that relative path.
relative_path = url.path[len('/static/'):]
safe_static_path = get_safe_path(STATIC_DIR, relative_path)
if not safe_static_path:
client_socket.sendall(b"HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied.")
elif os.path.isfile(safe_static_path):
serve_static_file(client_socket, safe_static_path)
elif os.path.isdir(safe_static_path):
client_socket.sendall(b"HTTP/1.1 403 Forbidden\r\n\r\nDirectory listing is not allowed.")
else:
client_socket.sendall(b"HTTP/1.1 404 Not Found\r\n\r\nNot Found.")
return
path_qs = parse_qs(url.query)
file_param = path_qs.get('file', [None])[0]
if url.path == '/play' and file_param:
serve_player(client_socket, file_param)
return
if url.path == '/manifest.m3u8' and file_param:
serve_manifest(client_socket, file_param)
return
# --- Handle file and directory requests from the main data directory ---
safe_data_path = get_safe_path(DEFAULT_DIR, url.path)
if not safe_data_path:
client_socket.sendall(b"HTTP/1.1 403 Forbidden\r\n\r\nAccess denied.")
return
if os.path.isfile(safe_data_path):
file_extension = os.path.splitext(safe_data_path)[1].lower()
content_type = "application/octet-stream"
if file_extension == '.ts':
content_type = "video/mp2t"
file_size = os.path.getsize(safe_data_path)
response = f"HTTP/1.1 200 OK\r\nContent-Type: {content_type}\r\nContent-Length: {file_size}\r\n\r\n"
client_socket.sendall(response.encode())
with open(safe_data_path, 'rb') as f:
while chunk := f.read(8192):
try:
client_socket.sendall(chunk)
except BrokenPipeError:
break
elif os.path.isdir(safe_data_path):
list_directory(client_socket, safe_data_path)
else:
client_socket.sendall(b"HTTP/1.1 404 Not Found\r\n\r\nNot Found.")
except Exception:
pass
finally:
client_socket.close()
def start_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
while True:
try:
client_socket, _ = server_socket.accept()
threading.Thread(target=handle_request, args=(client_socket,)).start()
except KeyboardInterrupt:
break
server_socket.close()
def main():
print(f"Server starting on http://{HOST}:{PORT}")
print(f"Serving data from: {DEFAULT_DIR}")
print(f"Serving static assets from: {STATIC_DIR}")
server_thread = threading.Thread(target=start_server)
server_thread.daemon = True
server_thread.start()
signal.signal(signal.SIGINT, lambda s, f: os._exit(0))
signal.signal(signal.SIGTERM, lambda s, f: os._exit(0))
server_thread.join()
if __name__ == "__main__":
main()
File diff suppressed because one or more lines are too long
View File
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
'''
MIT Non-Commercial License
Copyright (c) 2019, dragonpilot
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
- Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
beepd is a soundd replacement that makes beep alerts via GPIO
'''
import subprocess
import time
from cereal import car, messaging
from openpilot.common.realtime import Ratekeeper
import threading
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
class Beepd:
def __init__(self):
self.current_alert = AudibleAlert.none
self.enable_gpio()
def enable_gpio(self):
try:
subprocess.run("echo 42 | sudo tee /sys/class/gpio/export",
shell=True,
stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
encoding='utf8')
except Exception:
pass
subprocess.run("echo \"out\" | sudo tee /sys/class/gpio/gpio42/direction",
shell=True,
stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
encoding='utf8')
def _beep(self, on):
val = "1" if on else "0"
subprocess.run(f"echo \"{val}\" | sudo tee /sys/class/gpio/gpio42/value",
shell=True,
stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
encoding='utf8')
def engage(self):
self._beep(True)
time.sleep(0.05)
self._beep(False)
def disengage(self):
for _ in range(2):
self._beep(True)
time.sleep(0.01)
self._beep(False)
time.sleep(0.01)
def warning(self):
for _ in range(3):
self._beep(True)
time.sleep(0.01)
self._beep(False)
time.sleep(0.01)
def warning_immediate(self):
for _ in range(5):
self._beep(True)
time.sleep(0.01)
self._beep(False)
time.sleep(0.01)
def dispatch_beep(self, func):
threading.Thread(target=func, daemon=True).start()
def update_alert(self, new_alert):
current_alert_played_once = self.current_alert == AudibleAlert.none
if self.current_alert != new_alert and (new_alert != AudibleAlert.none or current_alert_played_once):
self.current_alert = new_alert
# if new_alert == AudibleAlert.engage:
# self.dispatch_beep(self.engage)
# if new_alert == AudibleAlert.disengage:
# self.dispatch_beep(self.disengage)
if new_alert == AudibleAlert.warningImmediate:
self.dispatch_beep(self.warning_immediate)
def get_audible_alert(self, sm):
if sm.updated['selfdriveState']:
new_alert = sm['selfdriveState'].alertSound.raw
self.update_alert(new_alert)
def test_beepd_thread(self):
frame = 0
rk = Ratekeeper(20)
pm = messaging.PubMaster(['selfdriveState'])
while True:
cs = messaging.new_message('selfdriveState')
if frame == 20:
cs.selfdriveState.alertSound = AudibleAlert.engage
if frame == 40:
cs.selfdriveState.alertSound = AudibleAlert.disengage
if frame == 60:
cs.selfdriveState.alertSound = AudibleAlert.prompt
if frame == 80:
cs.selfdriveState.alertSound = AudibleAlert.disengage
if frame == 85:
cs.selfdriveState.alertSound = AudibleAlert.prompt
pm.send("selfdriveState", cs)
frame += 1
rk.keep_time()
def beepd_thread(self, test=False):
if test:
threading.Thread(target=self.test_beepd_thread, daemon=True).start()
sm = messaging.SubMaster(['selfdriveState'])
rk = Ratekeeper(20)
while True:
sm.update(0)
self.get_audible_alert(sm)
rk.keep_time()
def main():
s = Beepd()
s.beepd_thread(test=False)
if __name__ == "__main__":
main()
+19
View File
@@ -27,6 +27,24 @@ function agnos_init {
fi
}
no_amp() {
output=$(i2cget -y 0 0x10 0x00 2>/dev/null)
if [ -z "$output" ]; then
return 0
else
return 1
fi
}
function check_device_mode {
if no_amp; then
echo "O3 Lite Mode"
export DISABLE_DRIVER=1
else
echo "C3 Mode"
fi
}
function launch {
# Remove orphaned git lock if it exists on boot
[ -f "$DIR/.git/index.lock" ] && rm -f $DIR/.git/index.lock
@@ -71,6 +89,7 @@ function launch {
# hardware specific init
if [ -f /AGNOS ]; then
check_device_mode
agnos_init
fi
+4 -1
View File
@@ -5,9 +5,12 @@ export MKL_NUM_THREADS=1
export NUMEXPR_NUM_THREADS=1
export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -s /data/params/d/dp_device_model_selected ]; then
export FINGERPRINT="$(cat /data/params/d/dp_device_model_selected)"
fi
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="11.11"
export AGNOS_VERSION="11.13"
fi
export STAGING_ROOT="/data/safe_staging"
+1
View File
@@ -97,6 +97,7 @@ class Bus(StrEnum):
party = auto()
ap_party = auto()
zss = auto()
def apply_driver_steer_torque_limits(apply_torque, apply_torque_last, driver_torque, LIMITS):
+1 -1
View File
@@ -11,7 +11,7 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.notCar = True
ret.brand = "body"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.body)]
+2 -2
View File
@@ -149,7 +149,7 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, experimental_long_allowed: bool,
num_pandas: int = 1, cached_params: CarParamsT | None = None):
num_pandas: int = 1, dp_params: int = 0, cached_params: CarParamsT | None = None):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(can_recv, can_send, set_obd_multiplexing, num_pandas, cached_params)
if candidate is None:
@@ -157,7 +157,7 @@ def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multip
candidate = "MOCK"
CarInterface = interfaces[candidate]
CP: CarParams = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, docs=False)
CP: CarParams = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, dp_params, docs=False)
CP.carVin = vin
CP.carFw = car_fw
CP.fingerprintSource = source
@@ -13,7 +13,7 @@ class CarInterface(CarInterfaceBase):
RadarInterface = RadarInterface
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "chrysler"
ret.dashcamOnly = candidate in RAM_HD
+1 -1
View File
@@ -32,7 +32,7 @@ def get_params_for_docs(platform) -> CarParams:
cp_platform = platform if platform in interfaces else MOCK.MOCK
CP: CarParams = interfaces[cp_platform].get_params(cp_platform, fingerprint=gen_empty_fingerprint(),
car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)],
experimental_long=True, docs=True)
experimental_long=True, dp_params=0, docs=True)
return CP
+1 -1
View File
@@ -26,7 +26,7 @@ class CarInterface(CarInterfaceBase):
return CarControllerParams.ACCEL_MIN, np.interp(current_speed, ACCEL_MAX_BP, ACCEL_MAX_VALS)
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "ford"
ret.radarUnavailable = Bus.radar not in DBC[candidate]
+1 -1
View File
@@ -85,7 +85,7 @@ class CarInterface(CarInterfaceBase):
return self.torque_from_lateral_accel_linear
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "gm"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
ret.autoResumeSng = False
+1 -1
View File
@@ -31,7 +31,7 @@ class CarInterface(CarInterfaceBase):
return CarControllerParams.NIDEC_ACCEL_MIN, np.interp(current_speed, ACCEL_MAX_BP, ACCEL_MAX_VALS)
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "honda"
CAN = CanBus(ret, fingerprint)
@@ -23,7 +23,7 @@ class CarInterface(CarInterfaceBase):
RadarInterface = RadarInterface
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "hyundai"
cam_can = CanBus(None, fingerprint).CAM
@@ -147,6 +147,13 @@ class CarInterface(CarInterfaceBase):
# TODO: Optima Hybrid 2017 uses a different SCC12 checksum
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, }
# w/ SMDPS, allow steering to 0
if 0x2AA in fingerprint[0]:
ret.minSteerSpeed = 0.
print("----------------------------------------------")
print("dragonpilot: SMDPS detected!")
print("----------------------------------------------")
return ret
@staticmethod
@@ -51,7 +51,7 @@ class TestHyundaiFingerprint:
if lka_steering:
cam_can = CanBus(None, fingerprint).CAM
fingerprint[cam_can] = [0x50, 0x110] # LKA steering messages
CP = CarInterface.get_params(CAR.KIA_EV6, fingerprint, [], False, False)
CP = CarInterface.get_params(CAR.KIA_EV6, fingerprint, [], False, 0, False)
assert bool(CP.flags & HyundaiFlags.CANFD_LKA_STEERING) == lka_steering
# radar available
@@ -59,14 +59,14 @@ class TestHyundaiFingerprint:
fingerprint = gen_empty_fingerprint()
if radar:
fingerprint[1][RADAR_START_ADDR] = 8
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA, fingerprint, [], False, False)
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA, fingerprint, [], False, 0, False)
assert CP.radarUnavailable != radar
def test_alternate_limits(self):
# Alternate lateral control limits, for high torque cars, verify Panda safety mode flag is set
fingerprint = gen_empty_fingerprint()
for car_model in CAR:
CP = CarInterface.get_params(car_model, fingerprint, [], False, False)
CP = CarInterface.get_params(car_model, fingerprint, [], False, 0, False)
assert bool(CP.flags & HyundaiFlags.ALT_LIMITS) == bool(CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.ALT_LIMITS)
def test_can_features(self):
+5 -5
View File
@@ -21,7 +21,7 @@ from opendbc.can.parser import CANParser
GearShifter = structs.CarState.GearShifter
ButtonType = structs.CarState.ButtonEvent.Type
V_CRUISE_MAX = 145
V_CRUISE_MAX = 160
MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
ACCEL_MAX = 2.0
ACCEL_MIN = -3.5
@@ -131,11 +131,11 @@ class CarInterfaceBase(ABC):
"""
Parameters essential to controlling the car may be incomplete or wrong without FW versions or fingerprints.
"""
return cls.get_params(candidate, gen_empty_fingerprint(), list(), False, False)
return cls.get_params(candidate, gen_empty_fingerprint(), list(), False, 0, False)
@classmethod
def get_params(cls, candidate: str, fingerprint: dict[int, dict[int, int]], car_fw: list[structs.CarParams.CarFw],
experimental_long: bool, docs: bool) -> structs.CarParams:
experimental_long: bool, dp_params: int, docs: bool) -> structs.CarParams:
ret = CarInterfaceBase.get_std_params(candidate)
platform = PLATFORMS[candidate]
@@ -148,7 +148,7 @@ class CarInterfaceBase(ABC):
ret.tireStiffnessFactor = platform.config.specs.tireStiffnessFactor
ret.flags |= int(platform.config.flags)
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs)
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, dp_params, docs)
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
if not ret.notCar:
@@ -163,7 +163,7 @@ class CarInterfaceBase(ABC):
@staticmethod
@abstractmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint: dict[int, dict[int, int]],
car_fw: list[structs.CarParams.CarFw], experimental_long: bool, docs: bool) -> structs.CarParams:
car_fw: list[structs.CarParams.CarFw], experimental_long: bool, dp_params: int, docs: bool) -> structs.CarParams:
raise NotImplementedError
@staticmethod
+1 -1
View File
@@ -12,7 +12,7 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "mazda"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.mazda)]
ret.radarUnavailable = True
+1 -1
View File
@@ -11,7 +11,7 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "mock"
ret.mass = 1700.
ret.wheelbase = 2.70
@@ -60,7 +60,7 @@ class CarController(CarControllerBase):
# Below are the HUD messages. We copy the stock message and modify
if self.CP.carFingerprint != CAR.NISSAN_ALTIMA:
if self.frame % 2 == 0:
can_sends.append(nissancan.create_lkas_hud_msg(self.packer, CS.lkas_hud_msg, CC.enabled, hud_control.leftLaneVisible, hud_control.rightLaneVisible,
can_sends.append(nissancan.create_lkas_hud_msg(self.packer, CS.lkas_hud_msg, CC.latActive, hud_control.leftLaneVisible, hud_control.rightLaneVisible,
hud_control.leftLaneDepart, hud_control.rightLaneDepart))
if self.frame % 50 == 0:
+1 -1
View File
@@ -10,7 +10,7 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "nissan"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.nissan)]
ret.autoResumeSng = False
+181
View File
@@ -0,0 +1,181 @@
'''
MIT Non-Commercial License
Copyright (c) 2025 Rick Lan
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from Rick Lan. Contact ricklan@gmail.com for inquiries.
- Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from Rick Lan and is licensed under a custom license requiring permission for use."
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'''
from opendbc.car.interfaces import RadarInterfaceBase
from opendbc.can.parser import CANParser
from opendbc.car.structs import RadarData
from typing import List, Tuple
DREL_OFFSET = -1.2 # car head to radar
MAX_OBJECTS = 50
MAX_LAT_DIST = 5. # lat distance
RCS_MIN = 0.
RCS_MAX = 30.
STATIONARY_OBJ_VREL = -20.
STATIONARY_OBJ_LAT_DIST = 3.
# Constants for static point filtering
# 33hz * 5 secs for decay
INITIAL_SCORE = 33*5
def _create_radar_parser():
messages = [("ObjectData", 0)]
messages += [(f"ObjectData_{i}", 0) for i in range(MAX_OBJECTS)]
return CANParser('u_radar', messages, 1)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.updated_messages = set()
self.objs: dict[int, dict] = {}
self.rcp = _create_radar_parser()
self._radar_points = {}
self._radar_point_properties = {}
def _create_parsable_object_can_strings(self, can_strings: List[Tuple]) -> Tuple[List[Tuple], int]:
"""Optimized object string parsing with minimal allocations."""
if not can_strings or not isinstance(can_strings[0], tuple) or len(can_strings[0]) < 2:
return [], 0
# Pre-allocate list with known maximum size
new_list = []
new_list_append = new_list.append # Local reference for faster access
records = can_strings[0][1]
id_num = 1
for record in records:
if id_num > MAX_OBJECTS:
break
if record[0] == 0x60B:
new_list_append((id_num + 383, record[1], record[2]))
id_num += 1
return [(can_strings[0][0], new_list)], len(new_list)
# called by card.py, 100hz
def update(self, can_strings):
self.frame += 1
if self.rcp is None:
return super().update(None)
vls = self.rcp.update_strings(can_strings)
self.updated_messages.update(vls)
# update radar points
if 0x60B in self.updated_messages:
# parse objects, stored into objs array
parsable_can_string, size = self._create_parsable_object_can_strings(can_strings)
self.rcp.update_strings(parsable_can_string)
# Batch update objects
for i in range(size):
cpt = self.rcp.vl[f'ObjectData_{i}']
track_id = int(cpt['ID'])
prev_track = self._radar_points[track_id] if track_id in self._radar_points else None
if prev_track is None:
self._radar_points[track_id] = RadarData.RadarPoint()
self._radar_points[track_id].trackId = track_id
if track_id not in self._radar_point_properties:
self._radar_point_properties[track_id] = {
'class': 0,
'rcs': 0.,
'movement': 0,
'score': INITIAL_SCORE,
}
# radarPoint
self._radar_points[track_id].yvRel = float(cpt['VRelLat'])
self._radar_points[track_id].dRel = float(cpt['DistLong']) + DREL_OFFSET
self._radar_points[track_id].yRel = -float(cpt['DistLat'])
self._radar_points[track_id].vRel = float(cpt['VRelLong'])
self._radar_points[track_id].aRel = float('nan')
self._radar_points[track_id].measured = True
# other properties
self._radar_point_properties[track_id] = {
'class': int(cpt['Class']),
'rcs': float(cpt['RCS']),
'movement': int(cpt['DynProp']),
}
# reset score when:
# 1. it was not previously exists
# 2. object has moved
if prev_track is None or \
'score' not in self._radar_point_properties[track_id] or \
(
prev_track.dRel != self._radar_points[track_id].dRel or \
prev_track.yRel != self._radar_points[track_id].yRel or \
prev_track.vRel != self._radar_point_properties[track_id].vRel or \
prev_track.yvRel != self._radar_point_properties[track_id].yvRel
):
self._radar_point_properties[track_id]['score'] = INITIAL_SCORE
self.updated_messages.clear()
# published to liveTracks via card.py
# liveTracks is 20hz
# radard.py subscribed liveTracks, and is running at 20hz
# publish at 33 hz
if self.frame % 3 == 0:
for track_id in list(self._radar_points.keys()):
should_remove = False
if track_id in self._radar_point_properties:
# decay
self._radar_point_properties[track_id]['score'] -= 1
# idle too long
if not should_remove and self._radar_point_properties[track_id]['score'] < 0:
should_remove = True
# on coming
if not should_remove and self._radar_point_properties[track_id]['movement'] == 2:
should_remove = True
# ignore stationary object on the left/right when driving fast
if not should_remove and self._radar_points[track_id].vRel < STATIONARY_OBJ_VREL and abs(self._radar_points[track_id].yRel) > STATIONARY_OBJ_LAT_DIST:
should_remove = True
# RCS (radar cross section) not within the range
if not should_remove and not (RCS_MIN <= self._radar_point_properties[track_id]['rcs'] <= RCS_MAX):
should_remove = True
# object is outside the lat dist (too far left or too far right)
if not should_remove and abs(self._radar_points[track_id].yRel) > MAX_LAT_DIST:
should_remove = True
# we don't need to remove elements in self._radar_points_properties (not necessary)
if should_remove and track_id in self.pts:
del self._radar_points[track_id]
# update pts array
self.pts = self._radar_points
ret = RadarData()
ret.points = list(self.pts.values()) if self.pts else []
return ret
# return None to card.py so it will not try to publish to liveTracks
return None
+1 -1
View File
@@ -11,7 +11,7 @@ class CarInterface(CarInterfaceBase):
RadarInterface = RadarInterface
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "rivian"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.rivian)]
+11
View File
@@ -18,3 +18,14 @@ CarStateT = capnp.lib.capnp._StructModule
RadarDataT = capnp.lib.capnp._StructModule
CarControlT = capnp.lib.capnp._StructModule
CarParamsT = capnp.lib.capnp._StructModule
class DPFlags:
LateralALKA = 1
ExtRadar = 2
ToyotaDoorAutoLockUnlock = 2 ** 2
ToyotaTSS1SnG = 2 ** 3
ToyotaStockLon = 2 ** 4
VagA0SnG = 2 ** 5
VAGPQSteeringPatch = 2 ** 6
VagAvoidEPSLockout = 2 ** 7
pass
+1 -1
View File
@@ -11,7 +11,7 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
@staticmethod
def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, experimental_long, dp_prams, docs) -> structs.CarParams:
ret.brand = "subaru"
ret.radarUnavailable = True
# for HYBRID CARS to be upstreamed, we need:
+1 -1
View File
@@ -10,7 +10,7 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "tesla"
ret.dashcamOnly = True
@@ -59,7 +59,7 @@ class TestCarInterfaces:
args = get_fuzzy_car_interface_args(data.draw)
car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'],
experimental_long=args['experimental_long'], docs=False)
experimental_long=args['experimental_long'], dp_params=0, docs=False)
car_interface = CarInterface(car_params)
assert car_params
assert car_interface
@@ -33,6 +33,17 @@ MAX_STEER_RATE_FRAMES = 18 # tx control frames needed before torque can be cut
# EPS allows user torque above threshold for 50 frames before permanently faulting
MAX_USER_TORQUE = 500
# Lock / unlock door commands - Credit goes to AlexandreSato!
from common.conversions import Conversions as CV
LOCK_SPEED = 20 * CV.KPH_TO_MS
LOCK_UNLOCK_CAN_ID = 0x750
UNLOCK_CMD = b'\x40\x05\x30\x11\x00\x40\x00\x00'
LOCK_CMD = b'\x40\x05\x30\x11\x00\x80\x00\x00'
from cereal import car
PARK = car.CarState.GearShifter.park
DRIVE = car.CarState.GearShifter.drive
def get_long_tune(CP, params):
if CP.carFingerprint in TSS2_CAR:
@@ -75,6 +86,8 @@ class CarController(CarControllerBase):
self.secoc_lta_message_counter = 0
self.secoc_prev_reset_counter = 0
self.doors_locked = False
def update(self, CC, CS, now_nanos):
actuators = CC.actuators
stopping = actuators.longControlState == LongCtrlState.stopping
@@ -172,6 +185,9 @@ class CarController(CarControllerBase):
# pcm entered standstill or it's disabled
self.standstill_req = False
if (self.CP.flags & ToyotaFlags.TSS1_SNG.value) and CS.out.standstill and not self.last_standstill:
self.standstill_req = False
self.last_standstill = CS.out.standstill
# handle UI messages
@@ -266,9 +282,9 @@ class CarController(CarControllerBase):
if self.frame % 20 == 0 or send_ui:
can_sends.append(toyotacan.create_ui_command(self.packer, steer_alert, pcm_cancel_cmd, hud_control.leftLaneVisible,
hud_control.rightLaneVisible, hud_control.leftLaneDepart,
hud_control.rightLaneDepart, CC.enabled, CS.lkas_hud))
hud_control.rightLaneDepart, lat_active, CS.lkas_hud))
if (self.frame % 100 == 0 or send_ui) and (self.CP.enableDsu or self.CP.flags & ToyotaFlags.DISABLE_RADAR.value):
if not self.CP.flags & ToyotaFlags.RADAR_FILTER.value and (self.frame % 100 == 0 or send_ui) and (self.CP.enableDsu or self.CP.flags & ToyotaFlags.DISABLE_RADAR.value):
can_sends.append(toyotacan.create_fcw_command(self.packer, fcw_alert))
# *** static msgs ***
@@ -277,7 +293,7 @@ class CarController(CarControllerBase):
can_sends.append(CanData(addr, vl, bus))
# keep radar disabled
if self.frame % 20 == 0 and self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
if not self.CP.flags & ToyotaFlags.RADAR_FILTER.value and self.frame % 20 == 0 and self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
can_sends.append(make_tester_present_msg(0x750, 0, 0xF))
new_actuators = actuators.as_builder()
@@ -286,5 +302,13 @@ class CarController(CarControllerBase):
new_actuators.steeringAngleDeg = self.last_angle
new_actuators.accel = self.accel
if self.CP.flags & ToyotaFlags.DOOR_AUTO_LOCK_UNLOCK.value:
if not self.doors_locked and CS.out.gearShifter == DRIVE and CS.out.vEgo >= LOCK_SPEED:
can_sends.append(CanData(LOCK_UNLOCK_CAN_ID, LOCK_CMD, 0))
self.doors_locked = True
elif self.doors_locked and CS.out.gearShifter == PARK:
can_sends.append(CanData(LOCK_UNLOCK_CAN_ID, UNLOCK_CMD, 0))
self.doors_locked = False
self.frame += 1
return new_actuators, can_sends
+24 -3
View File
@@ -53,6 +53,9 @@ class CarState(CarStateBase):
self.gvc = 0.0
self.secoc_synchronization = None
from opendbc.car.toyota.zss import ZSS
self.zss = ZSS(CP.flags)
def update(self, can_parsers) -> structs.CarState:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
@@ -100,6 +103,14 @@ class CarState(CarStateBase):
ret.steeringRateDeg = cp.vl["STEER_ANGLE_SENSOR"]["STEER_RATE"]
torque_sensor_angle_deg = cp.vl["STEER_TORQUE_SENSOR"]["STEER_ANGLE"]
if self.zss.enabled:
self.zss.set_values(can_parsers[Bus.zss])
if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR:
main_on = cp.vl["DSU_CRUISE"]["MAIN_ON"] != 0
else:
main_on = cp.vl["PCM_CRUISE_2"]["MAIN_ON"] != 0
ret.steeringAngleDeg = self.zss.get_steering_angle_deg(main_on, cp.vl["PCM_CRUISE"]["CRUISE_ACTIVE"], ret.steeringAngleDeg)
# On some cars, the angle measurement is non-zero while initializing
if abs(torque_sensor_angle_deg) > 1e-3 and not bool(cp.vl["STEER_TORQUE_SENSOR"]["STEER_ANGLE_INITIALIZING"]):
self.accurate_steer_angle_seen = True
@@ -152,7 +163,8 @@ class CarState(CarStateBase):
ret.cruiseState.speedCluster = cluster_set_speed * conversion_factor
if self.CP.carFingerprint in TSS2_CAR and not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
self.acc_type = cp_acc.vl["ACC_CONTROL"]["ACC_TYPE"]
if not (self.CP.flags & ToyotaFlags.SDSU.value):
self.acc_type = cp_acc.vl["ACC_CONTROL"]["ACC_TYPE"]
ret.stockFcw = bool(cp_acc.vl["PCS_HUD"]["FCW"])
# some TSS2 cars have low speed lockout permanently set, so ignore on those cars
@@ -237,8 +249,12 @@ class CarState(CarStateBase):
if CP.carFingerprint in RADAR_ACC_CAR and not CP.flags & ToyotaFlags.DISABLE_RADAR.value:
pt_messages += [
("PCS_HUD", 1),
("ACC_CONTROL", 33),
# ("ACC_CONTROL", 33),
]
if not CP.flags & ToyotaFlags.SDSU.value:
pt_messages += [
("ACC_CONTROL", 33),
]
if CP.carFingerprint not in (TSS2_CAR - RADAR_ACC_CAR) and not CP.enableDsu and not CP.flags & ToyotaFlags.DISABLE_RADAR.value:
pt_messages += [
@@ -263,7 +279,12 @@ class CarState(CarStateBase):
("PRE_COLLISION", 33),
]
return {
parsers = {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, 2),
}
if CP.flags & ToyotaFlags.ZSS:
parsers[Bus.zss] = CANParser("toyota_zss", [("SECONDARY_STEER_ANGLE", 0)], 0)
return parsers
+49 -5
View File
@@ -21,7 +21,7 @@ class CarInterface(CarInterfaceBase):
return CarControllerParams(CP).ACCEL_MIN, CarControllerParams(CP).ACCEL_MAX
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "toyota"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.toyota)]
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
@@ -57,13 +57,22 @@ class CarInterface(CarInterfaceBase):
if Ecu.hybrid in found_ecus:
ret.flags |= ToyotaFlags.HYBRID.value
if 0x23 in fingerprint[0]:
print("----------------------------------------------")
print("dragonpilot: ZSS detected!")
print("----------------------------------------------")
ret.flags |= ToyotaFlags.ZSS.value
if candidate == CAR.TOYOTA_PRIUS:
stop_and_go = True
# Only give steer angle deadzone to for bad angle sensor prius
for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
ret.steerActuatorDelay = 0.25
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.2)
if ret.flags & ToyotaFlags.ZSS.value:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
else:
ret.steerActuatorDelay = 0.25
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.2)
elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2):
stop_and_go = True
@@ -118,6 +127,30 @@ class CarInterface(CarInterfaceBase):
if experimental_long and candidate in RADAR_ACC_CAR:
ret.flags |= ToyotaFlags.DISABLE_RADAR.value
# RADAR_ACC_CAR = CHR TSS2 / RAV4 TSS2
# NO_DSU_CAR = CAMRY / CHR
if 0x2FF in fingerprint[0] or 0x2AA in fingerprint[0]:
print("----------------------------------------------")
print("dragonpilot: RADAR_FILTER detected!")
print("----------------------------------------------")
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.LONG_FILTER.value
ret.experimentalLongitudinalAvailable = False
ret.flags |= ToyotaFlags.RADAR_FILTER.value | ToyotaFlags.DISABLE_RADAR.value
sdsu_active = False
if not (candidate in (RADAR_ACC_CAR | NO_DSU_CAR)) and 0x2FF in fingerprint[0]:
print("----------------------------------------------")
print("dragonpilot: SDSU detected!")
print("----------------------------------------------")
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.LONG_FILTER.value
ret.enableDsu = False
sdsu_active = True
stop_and_go = True
ret.flags |= ToyotaFlags.SDSU.value
ret.experimentalLongitudinalAvailable = False
# openpilot longitudinal enabled by default:
# - cars w/ DSU disconnected
# - TSS2 cars with camera sending ACC_CONTROL where we can block it
@@ -129,7 +162,12 @@ class CarInterface(CarInterfaceBase):
else:
ret.openpilotLongitudinalControl = ret.enableDsu or \
candidate in (TSS2_CAR - RADAR_ACC_CAR) or \
bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value)
bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value) or \
sdsu_active
if dp_params & structs.DPFlags.ToyotaStockLon:
ret.openpilotLongitudinalControl = False
ret.experimentalLongitudinalAvailable = False
ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR
@@ -151,11 +189,17 @@ class CarInterface(CarInterfaceBase):
if ret.flags & ToyotaFlags.HYBRID.value:
ret.longitudinalActuatorDelay = 0.05
if dp_params & structs.DPFlags.ToyotaDoorAutoLockUnlock:
ret.flags |= ToyotaFlags.DOOR_AUTO_LOCK_UNLOCK.value
if dp_params & structs.DPFlags.ToyotaTSS1SnG:
ret.flags |= ToyotaFlags.TSS1_SNG.value
return ret
@staticmethod
def init(CP, can_recv, can_send):
# disable radar if alpha longitudinal toggled on radar-ACC car
if CP.flags & ToyotaFlags.DISABLE_RADAR.value:
if not CP.flags & ToyotaFlags.RADAR_FILTER.value and CP.flags & ToyotaFlags.DISABLE_RADAR.value:
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, uds.CONTROL_TYPE.ENABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL])
disable_ecu(can_recv, can_send, bus=0, addr=0x750, sub_addr=0xf, com_cont_req=communication_control)
@@ -55,11 +55,14 @@ class ToyotaSafetyFlags(IntFlag):
STOCK_LONGITUDINAL = (2 << 8)
LTA = (4 << 8)
SECOC = (8 << 8)
LONG_FILTER = (16 << 8)
class ToyotaFlags(IntFlag):
# Detected flags
HYBRID = 1
# use legacy id
SDSU = 2
DISABLE_RADAR = 4
# Static flags
@@ -76,6 +79,12 @@ class ToyotaFlags(IntFlag):
RAISED_ACCEL_LIMIT = 1024
SECOC = 2048
ALKA = 2 ** 12
DOOR_AUTO_LOCK_UNLOCK = 2 ** 13
TSS1_SNG = 2 ** 14
RADAR_FILTER = 2 ** 15
ZSS = 2 ** 16
class Footnote(Enum):
CAMRY = CarFootnote(
"openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.",
+79
View File
@@ -0,0 +1,79 @@
from opendbc.car.toyota.values import ToyotaFlags
from opendbc.can.parser import CANParser
ANGLE_DIFF_THRESHOLD = 4.0
THRESHOLD_COUNT = 10
class ZSS:
def __init__(self, flags: int):
self.enabled = flags & ToyotaFlags.ZSS.value
self._alka_enabled = flags & ToyotaFlags.ALKA.value
self._offset_compute_once = True
self._alka_active_prev = False
self._cruise_active_prev = False
self._offset = 0.
self._threshold_count = 0
self._zss_value = 0.
self._print_allow = True
def set_values(self, cp: CANParser):
self._zss_value = cp.vl["SECONDARY_STEER_ANGLE"]["ZORRO_STEER"]
def get_enabled(self):
return self.enabled
def get_steering_angle_deg(self, main_on, cruise_active, stock_steering_angle_deg):
# off, fall back to stock
if not self.enabled:
return stock_steering_angle_deg
# when lka control is off, use stock
alka_active = self._is_alka_active(main_on)
if not cruise_active and not alka_active:
return stock_steering_angle_deg
# lka just activated
if not self._offset_compute_once and ((alka_active and not self._alka_active_prev) or (cruise_active and not self._cruise_active_prev)):
self._threshold_count = 0
self._offset_compute_once = True
self._print_allow = True
self._alka_active_prev = alka_active
self._cruise_active_prev = cruise_active
# compute offset when required
if self._offset_compute_once:
self._offset_compute_once = not self._compute_offset(stock_steering_angle_deg)
# error checking
if self._threshold_count >= THRESHOLD_COUNT:
if self._print_allow:
print("ZSS: Too many large diff, fallback to stock.")
self._print_allow = False
return stock_steering_angle_deg
if self._offset_compute_once:
print("ZSS: Compute offset required, fallback to stock.")
return stock_steering_angle_deg
zss_steering_angle_deg = self._zss_value - self._offset
angle_diff = abs(stock_steering_angle_deg - zss_steering_angle_deg)
if angle_diff > ANGLE_DIFF_THRESHOLD:
print(f"ZSS: Diff too large ({angle_diff}), fallback to stock. ")
if self._is_alka_active(main_on) or cruise_active:
self._threshold_count += 1
return stock_steering_angle_deg
return zss_steering_angle_deg
def _is_alka_active(self, main_on):
return self._alka_enabled and main_on != 0
def _compute_offset(self, steering_angle_deg):
if abs(steering_angle_deg) > 1e-3 and abs(self._zss_value) > 1e-3:
self._offset = self._zss_value - steering_angle_deg
print(f"ZSS: offset computed: {self._offset}")
return True
return False
@@ -13,7 +13,7 @@ LongCtrlState = structs.CarControl.Actuators.LongControlState
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
self.CCP = CarControllerParams(CP)
self.CCP = CarControllerParams(CP, CP.flags & VolkswagenFlags.AVOID_EPS_LOCKOUT)
self.CCS = pqcan if CP.flags & VolkswagenFlags.PQ else mqbcan
self.packer_pt = CANPacker(dbc_names[Bus.pt])
self.ext_bus = CANBUS.pt if CP.networkLocation == structs.CarParams.NetworkLocation.fwdCamera else CANBUS.cam
@@ -24,6 +24,7 @@ class CarController(CarControllerBase):
self.eps_timer_soft_disable_alert = False
self.hca_frame_timer_running = 0
self.hca_frame_same_torque = 0
self.dp_vag_pq_steering_patch = 7 if CP.flags & VolkswagenFlags.PQSteeringPatch else 5
def update(self, CC, CS, now_nanos):
actuators = CC.actuators
@@ -63,7 +64,7 @@ class CarController(CarControllerBase):
self.eps_timer_soft_disable_alert = self.hca_frame_timer_running > self.CCP.STEER_TIME_ALERT / DT_CTRL
self.apply_torque_last = apply_torque
can_sends.append(self.CCS.create_steering_control(self.packer_pt, CANBUS.pt, apply_torque, hca_enabled))
can_sends.append(self.CCS.create_steering_control(self.packer_pt, CANBUS.pt, apply_torque, hca_enabled, self.dp_vag_pq_steering_patch))
if self.CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
# Pacify VW Emergency Assist driver inactivity detection by changing its view of driver steering input torque
@@ -111,11 +112,22 @@ class CarController(CarControllerBase):
lead_distance, hud_control.leadDistanceBars))
# **** Stock ACC Button Controls **************************************** #
if self.CP.flags & VolkswagenFlags.A0SnG:
if self.CP.pcmCruise and CS.gra_stock_values["COUNTER"] != self.gra_acc_counter_last:
standing_resume_spam = CS.out.standstill
spam_window = self.frame % 50 < 15
gra_send_ready = self.CP.pcmCruise and CS.gra_stock_values["COUNTER"] != self.gra_acc_counter_last
if gra_send_ready and (CC.cruiseControl.cancel or CC.cruiseControl.resume):
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values,
cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume))
send_cancel = CC.cruiseControl.cancel
send_resume = CC.cruiseControl.resume or (standing_resume_spam and spam_window)
if send_cancel or send_resume:
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values,
cancel=send_cancel, resume=send_resume))
else:
gra_send_ready = self.CP.pcmCruise and CS.gra_stock_values["COUNTER"] != self.gra_acc_counter_last
if gra_send_ready and (CC.cruiseControl.cancel or CC.cruiseControl.resume):
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values,
cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume))
new_actuators = actuators.as_builder()
new_actuators.torque = self.apply_torque_last / self.CCP.STEER_MAX
@@ -10,7 +10,7 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
@staticmethod
def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, experimental_long, docs) -> structs.CarParams:
def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, experimental_long, dp_params, docs) -> structs.CarParams:
ret.brand = "volkswagen"
ret.radarUnavailable = True
@@ -35,7 +35,7 @@ class CarInterface(CarInterfaceBase):
# It is documented in a four-part blog series:
# https://blog.willemmelching.nl/carhacking/2022/01/02/vw-part1/
# Panda ALLOW_DEBUG firmware required.
ret.dashcamOnly = True
# ret.dashcamOnly = True
else:
# Set global MQB parameters
@@ -87,4 +87,13 @@ class CarInterface(CarInterfaceBase):
ret.vEgoStopping = 0.5
ret.autoResumeSng = ret.minEnableSpeed == -1
if dp_params & structs.DPFlags.VagA0SnG:
ret.flags |= VolkswagenFlags.A0SnG.value
if ret.flags & VolkswagenFlags.PQ and dp_params & structs.DPFlags.VAGPQSteeringPatch:
ret.flags |= VolkswagenFlags.PQSteeringPatch.value
if dp_params & structs.DPFlags.VagAvoidEPSLockout:
ret.flags |= VolkswagenFlags.AVOID_EPS_LOCKOUT.value
return ret
@@ -1,4 +1,4 @@
def create_steering_control(packer, bus, apply_torque, lkas_enabled):
def create_steering_control(packer, bus, apply_torque, lkas_enabled, dp_vag_pq_steering_patch):
values = {
"HCA_01_Status_HCA": 5 if lkas_enabled else 3,
"HCA_01_LM_Offset": abs(apply_torque),
+2 -2
View File
@@ -1,8 +1,8 @@
def create_steering_control(packer, bus, apply_torque, lkas_enabled):
def create_steering_control(packer, bus, apply_torque, lkas_enabled, dp_vag_pq_steering_patch = 5):
values = {
"LM_Offset": abs(apply_torque),
"LM_OffSign": 1 if apply_torque < 0 else 0,
"HCA_Status": 5 if (lkas_enabled and apply_torque != 0) else 3,
"HCA_Status": dp_vag_pq_steering_patch if (lkas_enabled and apply_torque != 0) else 3,
"Vib_Freq": 16,
}
@@ -27,7 +27,8 @@ class CarControllerParams:
# MQB vs PQ maximums are shared, but rate-of-change limited differently
# based on safety requirements driven by lateral accel testing.
STEER_MAX = 300 # Max heading control assist torque 3.00 Nm
# rick - move to init so we can overwrite it with avoid eps lockout
# STEER_MAX = 300 # Max heading control assist torque 3.00 Nm
STEER_DRIVER_MULTIPLIER = 3 # weight driver torque heavily
STEER_DRIVER_FACTOR = 1 # from dbc
@@ -40,8 +41,9 @@ class CarControllerParams:
ACCEL_MAX = 2.0 # 2.0 m/s max acceleration
ACCEL_MIN = -3.5 # 3.5 m/s max deceleration
def __init__(self, CP):
def __init__(self, CP, avoid_eps_lockout = False):
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
self.STEER_MAX = 300 if not avoid_eps_lockout else 288
if CP.flags & VolkswagenFlags.PQ:
self.LDW_STEP = 5 # LDW_1 message frequency 20Hz
@@ -144,6 +146,10 @@ class VolkswagenFlags(IntFlag):
# Static flags
PQ = 2
A0SnG = 2 ** 2
PQSteeringPatch = 2 ** 3
AVOID_EPS_LOCKOUT = 2 ** 4
@dataclass
class VolkswagenMQBPlatformConfig(PlatformConfig):
+4
View File
@@ -0,0 +1,4 @@
BO_ 35 SECONDARY_STEER_ANGLE: 8 XXX
SG_ ZORRO_STEER : 7|24@0- (0.004901594652,0) [-500|500] "" XXX
CM_ "BO_ SECONDARY_STEER_ANGLE: ZSS is a high-precision steering angle sensor that can replace the lower resolution sensor in most TSS1 Toyotas. Learn more: https://github.com/commaai/openpilot/wiki/Toyota-Lexus#zorro-steering-sensor-zss";
+786
View File
@@ -0,0 +1,786 @@
VERSION ""
NS_ :
NS_DESC_
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_DEF_DEF_REL_
BU_SG_REL_
BU_EV_REL_
BU_BO_REL_
SG_MUL_VAL_
BS_:
BU_: RADAR
BO_ 513 RadarState: 8 XXX
SG_ NVMReadStatus : 6|1@0+ (1,0) [0|1] "" XXX
SG_ NVMWriteStatus : 7|1@0+ (1,0) [0|1] "" XXX
SG_ MaxDistanceCfg : 15|10@0+ (2,0) [0|2046] "m" XXX
SG_ RadarPowerCfg : 25|3@0+ (1,0) [0|7] "" XXX
SG_ SensorID : 34|3@0+ (1,0) [0|7] "" XXX
SG_ SortIndex : 38|3@0+ (1,0) [0|7] "" XXX
SG_ CtrlRelayCfg : 41|1@0+ (1,0) [0|1] "" XXX
SG_ OutputTypeCfg : 43|2@0+ (1,0) [0|3] "" XXX
SG_ QualityInfoCfg : 44|1@0+ (1,0) [0|1] "" XXX
SG_ ExtInfoCfg : 45|1@0+ (1,0) [0|1] "" XXX
SG_ CANBaudRate : 55|3@0+ (1,0) [0|7] "" XXX
SG_ InterfaceType : 57|2@0+ (1,0) [0|3] "" XXX
SG_ RCSThreshold : 58|3@1+ (1,0) [0|7] "" XXX
SG_ CalibrationEnabled : 63|2@0+ (1,0) [0|3] "" XXX
VAL_ 513 NVMReadStatus 0 "Failed" 1 "Successful";
VAL_ 513 NVMWriteStatus 0 "Failed" 1 "Successful";
VAL_ 513 RadarPowerCfg 0 "Standard" 1 "-3dB Gain" 2 "-6dB Gain" 3 "-9dB Gain";
VAL_ 513 SortIndex 0 "No Sorting" 1 "Sort By Range" 2 "Sort By RCS";
VAL_ 513 CtrlRelayCfg 0 "Off" 1 "On";
VAL_ 513 OutputTypeCfg 0 "None" 1 "Objects" 2 "Clusters";
VAL_ 513 QualityInfoCfg 0 "Off" 1 "On";
VAL_ 513 ExtInfoCfg 0 "Off" 1 "On";
VAL_ 513 CANBaudRate 0 "500K" 1 "250K" 2 "1M";
VAL_ 513 RCSThreshold 0 "Standard" 1 "High Sensitivity";
VAL_ 513 CalibrationEnabled 1 "Enabled" 2 "Initial Recovery";
BO_ 1546 Status: 8 RADAR
SG_ NoOfObjects : 7|8@0+ (1,0) [0|255] "" XXX
SG_ MeasCount : 15|16@0+ (1,0) [0|65535] "" XXX
SG_ InterfaceVersion : 31|4@0+ (1,0) [0|15] "" XXX
BO_ 1547 ObjectData: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 1547 "Object detection and tracking information";
VAL_ 1547 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 1547 Class 0 "point" 1 "vehicle";
BO_ 383 ObjectData_0: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 383 "Object detection and tracking information";
VAL_ 383 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 383 Class 0 "point" 1 "vehicle";
BO_ 384 ObjectData_1: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 384 "Object detection and tracking information";
VAL_ 384 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 384 Class 0 "point" 1 "vehicle";
BO_ 385 ObjectData_2: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 385 "Object detection and tracking information";
VAL_ 385 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 385 Class 0 "point" 1 "vehicle";
BO_ 386 ObjectData_3: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 386 "Object detection and tracking information";
VAL_ 386 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 386 Class 0 "point" 1 "vehicle";
BO_ 387 ObjectData_4: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 387 "Object detection and tracking information";
VAL_ 387 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 387 Class 0 "point" 1 "vehicle";
BO_ 388 ObjectData_5: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 388 "Object detection and tracking information";
VAL_ 388 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 388 Class 0 "point" 1 "vehicle";
BO_ 389 ObjectData_6: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 389 "Object detection and tracking information";
VAL_ 389 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 389 Class 0 "point" 1 "vehicle";
BO_ 390 ObjectData_7: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 390 "Object detection and tracking information";
VAL_ 390 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 390 Class 0 "point" 1 "vehicle";
BO_ 391 ObjectData_8: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 391 "Object detection and tracking information";
VAL_ 391 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 391 Class 0 "point" 1 "vehicle";
BO_ 392 ObjectData_9: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 392 "Object detection and tracking information";
VAL_ 392 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 392 Class 0 "point" 1 "vehicle";
BO_ 393 ObjectData_10: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 393 "Object detection and tracking information";
VAL_ 393 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 393 Class 0 "point" 1 "vehicle";
BO_ 394 ObjectData_11: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 394 "Object detection and tracking information";
VAL_ 394 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 394 Class 0 "point" 1 "vehicle";
BO_ 395 ObjectData_12: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 395 "Object detection and tracking information";
VAL_ 395 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 395 Class 0 "point" 1 "vehicle";
BO_ 396 ObjectData_13: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 396 "Object detection and tracking information";
VAL_ 396 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 396 Class 0 "point" 1 "vehicle";
BO_ 397 ObjectData_14: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 397 "Object detection and tracking information";
VAL_ 397 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 397 Class 0 "point" 1 "vehicle";
BO_ 398 ObjectData_15: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 398 "Object detection and tracking information";
VAL_ 398 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 398 Class 0 "point" 1 "vehicle";
BO_ 399 ObjectData_16: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 399 "Object detection and tracking information";
VAL_ 399 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 399 Class 0 "point" 1 "vehicle";
BO_ 400 ObjectData_17: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 400 "Object detection and tracking information";
VAL_ 400 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 400 Class 0 "point" 1 "vehicle";
BO_ 401 ObjectData_18: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 401 "Object detection and tracking information";
VAL_ 401 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 401 Class 0 "point" 1 "vehicle";
BO_ 402 ObjectData_19: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 402 "Object detection and tracking information";
VAL_ 402 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 402 Class 0 "point" 1 "vehicle";
BO_ 403 ObjectData_20: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 403 "Object detection and tracking information";
VAL_ 403 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 403 Class 0 "point" 1 "vehicle";
BO_ 404 ObjectData_21: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 404 "Object detection and tracking information";
VAL_ 404 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 404 Class 0 "point" 1 "vehicle";
BO_ 405 ObjectData_22: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 405 "Object detection and tracking information";
VAL_ 405 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 405 Class 0 "point" 1 "vehicle";
BO_ 406 ObjectData_23: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 406 "Object detection and tracking information";
VAL_ 406 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 406 Class 0 "point" 1 "vehicle";
BO_ 407 ObjectData_24: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 407 "Object detection and tracking information";
VAL_ 407 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 407 Class 0 "point" 1 "vehicle";
BO_ 408 ObjectData_25: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 408 "Object detection and tracking information";
VAL_ 408 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 408 Class 0 "point" 1 "vehicle";
BO_ 409 ObjectData_26: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 409 "Object detection and tracking information";
VAL_ 409 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 409 Class 0 "point" 1 "vehicle";
BO_ 410 ObjectData_27: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 410 "Object detection and tracking information";
VAL_ 410 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 410 Class 0 "point" 1 "vehicle";
BO_ 411 ObjectData_28: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 411 "Object detection and tracking information";
VAL_ 411 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 411 Class 0 "point" 1 "vehicle";
BO_ 412 ObjectData_29: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 412 "Object detection and tracking information";
VAL_ 412 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 412 Class 0 "point" 1 "vehicle";
BO_ 413 ObjectData_30: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 413 "Object detection and tracking information";
VAL_ 413 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 413 Class 0 "point" 1 "vehicle";
BO_ 414 ObjectData_31: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 414 "Object detection and tracking information";
VAL_ 414 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 414 Class 0 "point" 1 "vehicle";
BO_ 415 ObjectData_32: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 415 "Object detection and tracking information";
VAL_ 415 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 415 Class 0 "point" 1 "vehicle";
BO_ 416 ObjectData_33: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 416 "Object detection and tracking information";
VAL_ 416 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 416 Class 0 "point" 1 "vehicle";
BO_ 417 ObjectData_34: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 417 "Object detection and tracking information";
VAL_ 417 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 417 Class 0 "point" 1 "vehicle";
BO_ 418 ObjectData_35: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 418 "Object detection and tracking information";
VAL_ 418 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 418 Class 0 "point" 1 "vehicle";
BO_ 419 ObjectData_36: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 419 "Object detection and tracking information";
VAL_ 419 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 419 Class 0 "point" 1 "vehicle";
BO_ 420 ObjectData_37: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 420 "Object detection and tracking information";
VAL_ 420 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 420 Class 0 "point" 1 "vehicle";
BO_ 421 ObjectData_38: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 421 "Object detection and tracking information";
VAL_ 421 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 421 Class 0 "point" 1 "vehicle";
BO_ 422 ObjectData_39: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 422 "Object detection and tracking information";
VAL_ 422 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 422 Class 0 "point" 1 "vehicle";
BO_ 423 ObjectData_40: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 423 "Object detection and tracking information";
VAL_ 423 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 423 Class 0 "point" 1 "vehicle";
BO_ 424 ObjectData_41: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 424 "Object detection and tracking information";
VAL_ 424 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 424 Class 0 "point" 1 "vehicle";
BO_ 425 ObjectData_42: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 425 "Object detection and tracking information";
VAL_ 425 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 425 Class 0 "point" 1 "vehicle";
BO_ 426 ObjectData_43: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 426 "Object detection and tracking information";
VAL_ 426 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 426 Class 0 "point" 1 "vehicle";
BO_ 427 ObjectData_44: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 427 "Object detection and tracking information";
VAL_ 427 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 427 Class 0 "point" 1 "vehicle";
BO_ 428 ObjectData_45: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 428 "Object detection and tracking information";
VAL_ 428 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 428 Class 0 "point" 1 "vehicle";
BO_ 429 ObjectData_46: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 429 "Object detection and tracking information";
VAL_ 429 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 429 Class 0 "point" 1 "vehicle";
BO_ 430 ObjectData_47: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 430 "Object detection and tracking information";
VAL_ 430 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 430 Class 0 "point" 1 "vehicle";
BO_ 431 ObjectData_48: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 431 "Object detection and tracking information";
VAL_ 431 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 431 Class 0 "point" 1 "vehicle";
BO_ 432 ObjectData_49: 8 RADAR
SG_ ID : 7|8@0+ (1,0) [0|255] "" XXX
SG_ DistLong : 15|13@0+ (0.2,-500) [-500|1138.2] "m" XXX
SG_ DistLat : 18|11@0+ (0.2,-204.6) [-204.6|204.8] "m" XXX
SG_ VRelLong : 39|10@0+ (0.25,-128) [-128|127.75] "m/s" XXX
SG_ VRelLat : 45|9@0+ (0.25,-64) [-64|63.75] "m/s" XXX
SG_ DynProp : 50|3@0+ (1,0) [0|7] "" XXX
SG_ Class : 52|2@0+ (1,0) [0|3] "" XXX
SG_ RCS : 63|8@0+ (0.5,-64) [-64|63.75] "dBm2" XXX
CM_ BO_ 432 "Object detection and tracking information";
VAL_ 432 DynProp 0 "moving" 1 "stationary" 2 "oncoming" 3 "crossing_left" 4 "crossing_right" 5 "unknown" 6 "stopped";
VAL_ 432 Class 0 "point" 1 "vehicle";
+2
View File
@@ -9,3 +9,5 @@ class ALTERNATIVE_EXPERIENCE:
DISABLE_STOCK_AEB = 2
RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX = 8
ALLOW_AEB = 16
ALKA = 2 ** 10
+8 -5
View File
@@ -641,8 +641,9 @@ bool longitudinal_brake_checks(int desired_brake, const LongitudinalLimits limit
bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueSteeringLimits limits) {
bool violation = false;
uint32_t ts = microsecond_timer_get();
bool alka = (alternative_experience & ALT_EXP_ALKA) != 0;
if (controls_allowed) {
if (controls_allowed || alka) {
// *** global torque limit check ***
violation |= max_limit_check(desired_torque, limits.max_steer, -limits.max_steer);
@@ -669,7 +670,7 @@ bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueStee
}
// no torque if controls is not allowed
if (!controls_allowed && (desired_torque != 0)) {
if (!(controls_allowed || alka) && (desired_torque != 0)) {
violation = true;
}
@@ -711,7 +712,7 @@ bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueStee
}
// reset to 0 if either controls is not allowed or there's a violation
if (violation || !controls_allowed) {
if (violation || !(controls_allowed || alka)) {
valid_steer_req_count = 0;
invalid_steer_req_count = 0;
desired_torque_last = 0;
@@ -727,7 +728,9 @@ bool steer_torque_cmd_checks(int desired_torque, int steer_req, const TorqueStee
bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const AngleSteeringLimits limits) {
bool violation = false;
if (controls_allowed && steer_control_enabled) {
bool alka = (alternative_experience & ALT_EXP_ALKA) != 0;
if ((controls_allowed || alka) && steer_control_enabled) {
// convert floating point angle rate limits to integers in the scale of the desired angle on CAN,
// add 1 to not false trigger the violation. also fudge the speed by 1 m/s so rate limits are
// always slightly above openpilot's in case we read an updated speed in between angle commands
@@ -814,7 +817,7 @@ bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const
}
// No angle control allowed when controls are not allowed
violation |= !controls_allowed && steer_control_enabled;
violation |= !(controls_allowed || alka) && steer_control_enabled;
return violation;
}
@@ -242,8 +242,9 @@ static bool honda_tx_hook(const CANPacket_t *to_send) {
}
// STEER: safety check
bool alka = (alternative_experience & ALT_EXP_ALKA) != 0;
if ((addr == 0xE4) || (addr == 0x194)) {
if (!controls_allowed) {
if (!(controls_allowed || alka)) {
bool steer_applied = GET_BYTE(to_send, 0) | GET_BYTE(to_send, 1);
if (steer_applied) {
tx = false;
@@ -10,6 +10,7 @@
TOYOTA_BASE_TX_MSGS \
{0x2E4, 0, 5, true}, \
{0x343, 0, 8, false}, /* ACC cancel cmd */ \
{0x750, 0, 8, false}, /* radar diagnostic address */ \
#define TOYOTA_COMMON_SECOC_TX_MSGS \
TOYOTA_BASE_TX_MSGS \
@@ -24,6 +25,14 @@
{0x750, 0, 8, false}, /* radar diagnostic address */ \
{0x343, 0, 8, true}, /* ACC */ \
#define TOYOTA_COMMON_LONG_TX_MSGS_FILTER \
TOYOTA_COMMON_TX_MSGS \
{0x283, 0, 7, false}, {0x2E6, 0, 8, false}, {0x2E7, 0, 8, false}, {0x33E, 0, 7, false}, {0x344, 0, 8, false}, {0x365, 0, 7, false}, {0x366, 0, 7, false}, {0x4CB, 0, 8, false}, /* DSU bus 0 */ \
{0x128, 1, 6, false}, {0x141, 1, 4, false}, {0x160, 1, 8, false}, {0x161, 1, 7, false}, {0x470, 1, 4, false}, /* DSU bus 1 */ \
{0x411, 0, 8, false}, /* PCS_HUD */ \
{0x750, 0, 8, false}, /* radar diagnostic address */ \
{0x343, 0, 8, false}, /* ACC */ \
#define TOYOTA_COMMON_RX_CHECKS(lta) \
{.msg = {{ 0xaa, 0, 8, .ignore_checksum = true, .ignore_counter = true, .frequency = 83U}, { 0 }, { 0 }}}, \
{.msg = {{0x260, 0, 8, .ignore_counter = true, .quality_flag = (lta), .frequency = 50U}, { 0 }, { 0 }}}, \
@@ -49,6 +58,7 @@ static bool toyota_alt_brake = false;
static bool toyota_stock_longitudinal = false;
static bool toyota_lta = false;
static int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file
static bool toyota_long_filter = false;
static uint32_t toyota_compute_checksum(const CANPacket_t *to_push) {
int addr = GET_ADDR(to_push);
@@ -315,6 +325,7 @@ static bool toyota_tx_hook(const CANPacket_t *to_send) {
}
// UDS: Only tester present ("\x0F\x02\x3E\x00\x00\x00\x00\x00") allowed on diagnostics address
/*
if (addr == 0x750) {
// this address is sub-addressed. only allow tester present to radar (0xF)
bool invalid_uds_msg = (GET_BYTES(to_send, 0, 4) != 0x003E020FU) || (GET_BYTES(to_send, 4, 4) != 0x0U);
@@ -322,6 +333,7 @@ static bool toyota_tx_hook(const CANPacket_t *to_send) {
tx = 0;
}
}
*/
return tx;
}
@@ -339,6 +351,10 @@ static safety_config toyota_init(uint16_t param) {
TOYOTA_COMMON_LONG_TX_MSGS
};
static const CanMsg TOYOTA_LONG_TX_MSGS_FILTER[] = {
TOYOTA_COMMON_LONG_TX_MSGS_FILTER
};
// safety param flags
// first byte is for EPS factor, second is for flags
const uint32_t TOYOTA_PARAM_OFFSET = 8U;
@@ -346,6 +362,7 @@ static safety_config toyota_init(uint16_t param) {
const uint32_t TOYOTA_PARAM_ALT_BRAKE = 1UL << TOYOTA_PARAM_OFFSET;
const uint32_t TOYOTA_PARAM_STOCK_LONGITUDINAL = 2UL << TOYOTA_PARAM_OFFSET;
const uint32_t TOYOTA_PARAM_LTA = 4UL << TOYOTA_PARAM_OFFSET;
const uint32_t TOYOTA_PARAM_LONG_FILTER = 16U << TOYOTA_PARAM_OFFSET;
#ifdef ALLOW_DEBUG
const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET;
@@ -356,6 +373,7 @@ static safety_config toyota_init(uint16_t param) {
toyota_stock_longitudinal = GET_FLAG(param, TOYOTA_PARAM_STOCK_LONGITUDINAL);
toyota_lta = GET_FLAG(param, TOYOTA_PARAM_LTA);
toyota_dbc_eps_torque_factor = param & TOYOTA_EPS_FACTOR;
toyota_long_filter = GET_FLAG(param, TOYOTA_PARAM_LONG_FILTER);
safety_config ret;
if (toyota_stock_longitudinal) {
@@ -365,7 +383,11 @@ static safety_config toyota_init(uint16_t param) {
SET_TX_MSGS(TOYOTA_TX_MSGS, ret);
}
} else {
SET_TX_MSGS(TOYOTA_LONG_TX_MSGS, ret);
if (toyota_long_filter) {
SET_TX_MSGS(TOYOTA_LONG_TX_MSGS_FILTER, ret);
} else {
SET_TX_MSGS(TOYOTA_LONG_TX_MSGS, ret);
}
}
if (toyota_secoc) {
@@ -255,6 +255,8 @@ extern struct sample_t angle_meas; // last 6 steer angles/curvatures
// This flag allows AEB to be commanded from openpilot.
#define ALT_EXP_ALLOW_AEB 16
#define ALT_EXP_ALKA 1024
extern int alternative_experience;
// time since safety mode has been changed
+2 -3
View File
@@ -166,9 +166,6 @@ void ignition_can_hook(CANPacket_t *to_push) {
int addr = GET_ADDR(to_push);
int len = GET_LEN(to_push);
// Check counter position on cars with overlap
static int prev_counter = -1;
// GM exception
if ((addr == 0x1F1) && (len == 8)) {
// SystemPowerMode (2=Run, 3=Crank Request)
@@ -181,6 +178,7 @@ void ignition_can_hook(CANPacket_t *to_push) {
// 0x152 overlaps with Subaru pre-global which has this bit as the high beam
int counter = GET_BYTE(to_push, 1) & 0xFU; // max is only 14
static int prev_counter = -1;
if ((counter == ((prev_counter + 1) % 15)) && (prev_counter != -1)) {
// VDM_OutputSignals->VDM_EpasPowerMode
ignition_can = ((GET_BYTE(to_push, 7) >> 4U) & 0x3U) == 1U; // VDM_EpasPowerMode_Drive_On=1
@@ -194,6 +192,7 @@ void ignition_can_hook(CANPacket_t *to_push) {
// 0x221 overlaps with Rivian which has random data on byte 0
int counter = GET_BYTE(to_push, 6) >> 4;
static int prev_counter = -1;
if ((counter == ((prev_counter + 1) % 16)) && (prev_counter != -1)) {
// VCFRONT_LVPowerState->VCFRONT_vehiclePowerState
int power_state = (GET_BYTE(to_push, 0) >> 5U) & 0x3U;
+42 -20
View File
@@ -16,17 +16,22 @@ if [ -z "$RELEASE_BRANCH" ]; then
exit 1
fi
if [ -z "$SOURCE_BRANCH" ]; then
echo "SOURCE_BRANCH is not set"
exit 1
fi
# set git identity
source $DIR/identity.sh
source /data/identity.sh
echo "[-] Setting up repo T=$SECONDS"
rm -rf $BUILD_DIR
mkdir -p $BUILD_DIR
cd $BUILD_DIR
git init
git remote add origin git@github.com:commaai/openpilot.git
git checkout --orphan $RELEASE_BRANCH
git remote add origin https://github.com/dragonpilot-community/dev.git
git checkout --orphan $SOURCE_BRANCH
# do the files copy
echo "[-] copying files T=$SECONDS"
@@ -44,19 +49,20 @@ echo "#define COMMA_VERSION \"$VERSION-release\"" > common/version.h
echo "[-] committing version $VERSION T=$SECONDS"
git add -f .
git commit -a -m "openpilot v$VERSION release"
git commit -a -m "dragonpilot v$VERSION release"
# Build
export PYTHONPATH="$BUILD_DIR"
scons -j$(nproc) --minimal
if [ -z "$PANDA_DEBUG_BUILD" ]; then
# release panda fw
CERT=/data/pandaextra/certs/release RELEASE=1 scons -j$(nproc) panda/
else
# build with ALLOW_DEBUG=1 to enable features like experimental longitudinal
scons -j$(nproc) panda/
fi
#if [ -z "$PANDA_DEBUG_BUILD" ]; then
# # release panda fw
# CERT=/data/pandaextra/certs/release RELEASE=1 scons -j$(nproc) panda/
#else
# # build with ALLOW_DEBUG=1 to enable features like experimental longitudinal
# scons -j$(nproc) panda/
#fi
scons -j$(nproc) panda/
# Ensure no submodules in release
if test "$(git submodule--helper list | wc -l)" -gt "0"; then
@@ -87,18 +93,34 @@ git checkout third_party/
# Mark as prebuilt release
touch prebuilt
# dragonpilot customized
find . -name '*.cc' -delete
find selfdrive/ui/ -name '*.h' -delete
find . -type d -name "tests" -exec rm -rf {} +
find . -type d -name 'x86_64' -exec rm -rf {} +
find . -type d -name 'Darwin' -exec rm -rf {} +
find . -name 'amd_gpu.py' -delete # 3.8M
cp -fr selfdrive/ui/qt/text_larch64 selfdrive/ui/qt/_text
rm -fr selfdrive/ui/qt/text_larch64 # 3.7M
cp -fr selfdrive/ui/qt/spinner_larch64 selfdrive/ui/qt/_spinner
rm -fr selfdrive/ui/qt/spinner_larch64 # 3.7M
rm -fr tinygrad_repo/docs/tinygrad_intro.pdf # 1.9M
rm -fr cereal/gen/cpp/log.capnp.h # 2.5M
rm -fr tinygrad_repo/extra/hip_gpu_driver/gc_10_3_0_offset.h # 1.4 M
rm -fr tinygrad_repo/extra/accel/tpu/logs/tpu_driver.t1v-n-852cd0d5-w-0.taylor.log.INFO.20210619-062914.26926.gz # 1.3 M
# Add built files to git
git add -f .
git commit --amend -m "openpilot v$VERSION"
git commit --amend -m "dragonpilot v$VERSION"
# Run tests
cd $BUILD_DIR
RELEASE=1 pytest -n0 -s selfdrive/test/test_onroad.py
#pytest selfdrive/car/tests/test_car_interfaces.py
if [ ! -z "$RELEASE_BRANCH" ]; then
echo "[-] pushing release T=$SECONDS"
git push -f origin $RELEASE_BRANCH:$RELEASE_BRANCH
fi
#cd $BUILD_DIR
#RELEASE=1 pytest -n0 -s selfdrive/test/test_onroad.py
##pytest selfdrive/car/tests/test_car_interfaces.py
git branch -m $RELEASE_BRANCH
#if [ ! -z "$RELEASE_BRANCH" ]; then
# echo "[-] pushing release T=$SECONDS"
# git push -f origin $RELEASE_BRANCH:$RELEASE_BRANCH
#fi
echo "[-] done T=$SECONDS"
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="256"
height="256"
viewBox="0 0 64 64"
fill="none"
version="1.1"
id="svg2"
sodipodi:docname="icon_empty.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2" />
<sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="3.0820312"
inkscape:cx="128"
inkscape:cy="114.37262"
inkscape:window-width="2560"
inkscape:window-height="1021"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

+30 -1
View File
@@ -81,6 +81,7 @@ class Car:
self.can_callbacks = can_comm_callbacks(self.can_sock, self.pm.sock['sendcan'])
dp_params = 0
if CI is None:
# wait for one pandaState and one CAN packet
print("Waiting for CAN messages...")
@@ -98,7 +99,28 @@ class Car:
with car.CarParams.from_bytes(cached_params_raw) as _cached_params:
cached_params = _cached_params
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, cached_params)
if self.params.get_bool("dp_lat_alka"):
dp_params |= structs.DPFlags.LateralALKA
if self.params.get_bool("dp_toyota_door_auto_lock_unlock"):
dp_params |= structs.DPFlags.ToyotaDoorAutoLockUnlock
if self.params.get_bool("dp_toyota_tss1_sng"):
dp_params |= structs.DPFlags.ToyotaTSS1SnG
if self.params.get_bool("dp_toyota_stock_lon"):
dp_params |= structs.DPFlags.ToyotaStockLon
if self.params.get_bool("dp_vag_a0_sng"):
dp_params |= structs.DPFlags.VagA0SnG
if self.params.get_bool("dp_vag_pq_steering_patch"):
dp_params |= structs.DPFlags.VAGPQSteeringPatch
if self.params.get_bool("dp_vag_avoid_eps_lockout"):
dp_params |= structs.DPFlags.VagAvoidEPSLockout
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), experimental_long_allowed, num_pandas, dp_params, cached_params)
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP)
self.CP = self.CI.CP
@@ -108,12 +130,19 @@ class Car:
self.CI, self.CP = CI, CI.CP
self.RI = RI
if self.params.get_bool("dp_lon_ext_radar"):
from opendbc.car.radar_interface import RadarInterface
self.RI = RadarInterface(self.CI.CP)
# set alternative experiences from parameters
disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator")
self.CP.alternativeExperience = 0
if not disengage_on_accelerator:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS
if dp_params & structs.DPFlags.LateralALKA:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALKA
openpilot_enabled_toggle = self.params.get_bool("OpenpilotEnabledToggle")
controller_available = self.CI.CC is not None and openpilot_enabled_toggle and not self.CP.dashcamOnly
+1 -1
View File
@@ -12,7 +12,7 @@ V_CRUISE_MIN = 8
V_CRUISE_MAX = 145
V_CRUISE_UNSET = 255
V_CRUISE_INITIAL = 40
V_CRUISE_INITIAL_EXPERIMENTAL_MODE = 105
V_CRUISE_INITIAL_EXPERIMENTAL_MODE = 50
IMPERIAL_INCREMENT = round(CV.MPH_TO_KPH, 1) # round here to avoid rounding errors incrementing set speed
ButtonEvent = car.CarState.ButtonEvent
+1 -1
View File
@@ -39,7 +39,7 @@ class TestCarInterfaces:
args = get_fuzzy_car_interface_args(data.draw)
car_params = CarInterface.get_params(car_name, args['fingerprints'], args['car_fw'],
experimental_long=args['experimental_long'], docs=False)
experimental_long=args['experimental_long'], dp_params=0, docs=False)
car_params = car_params.as_reader()
car_interface = CarInterface(car_params)
assert car_params
+1 -1
View File
@@ -152,7 +152,7 @@ class TestCarModelBase(unittest.TestCase):
cls.openpilot_enabled = cls.car_safety_mode_frame is not None
cls.CarInterface = interfaces[cls.platform]
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, docs=False)
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, experimental_long, dp_params=0, docs=False)
assert cls.CP
assert cls.CP.carFingerprint == cls.platform
+14 -2
View File
@@ -38,7 +38,7 @@ class Controls:
self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
'driverMonitoringState', 'onroadEvents', 'driverAssistance'], poll='selfdriveState')
self.pm = messaging.PubMaster(['carControl', 'controlsState'])
self.pm = messaging.PubMaster(['carControl', 'controlsState', 'dpControlsState'])
self.steer_limited_by_controls = False
self.desired_curvature = 0.0
@@ -56,6 +56,9 @@ class Controls:
elif self.CP.lateralTuning.which() == 'torque':
self.LaC = LatControlTorque(self.CP, self.CI)
self.alka_enabled = self.params.get_bool("dp_lat_alka")
self.alka_active = False
def update(self):
self.sm.update(15)
if self.sm.updated["liveCalibration"]:
@@ -88,7 +91,9 @@ class Controls:
# Check which actuators can be enabled
standstill = abs(CS.vEgo) <= max(self.CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED) or CS.standstill
CC.latActive = self.sm['selfdriveState'].active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and not standstill
self.alka_active = self.alka_enabled and CS.cruiseState.available and not standstill and CS.gearShifter != car.CarState.GearShifter.reverse
lat_active = self.sm['selfdriveState'].active or self.alka_active
CC.latActive = lat_active and not CS.steerFaultTemporary and not CS.steerFaultPermanent and not standstill
CC.longActive = CC.enabled and not any(e.overrideLongitudinal for e in self.sm['onroadEvents']) and self.CP.openpilotLongitudinalControl
actuators = CC.actuators
@@ -169,6 +174,13 @@ class Controls:
# TODO: both controlsState and carControl valids should be set by
# sm.all_checks(), but this creates a circular dependency
# dpControlsState
dat = messaging.new_message('dpControlsState')
dat.valid = True
ncs = dat.dpControlsState
ncs.alkaActive = self.alka_active
self.pm.send('dpControlsState', dat)
# controlsState
dat = messaging.new_message('controlsState')
dat.valid = CS.canValid
+19 -5
View File
@@ -1,6 +1,7 @@
from cereal import log
from openpilot.common.conversions import Conversions as CV
from openpilot.common.realtime import DT_MDL
import time
LaneChangeState = log.LaneChangeState
LaneChangeDirection = log.LaneChangeDirection
@@ -31,7 +32,7 @@ DESIRES = {
class DesireHelper:
def __init__(self):
def __init__(self, dp_lat_lca_speed=LANE_CHANGE_SPEED_MIN, dp_lat_lca_auto_sec=0.):
self.lane_change_state = LaneChangeState.off
self.lane_change_direction = LaneChangeDirection.none
self.lane_change_timer = 0.0
@@ -39,11 +40,14 @@ class DesireHelper:
self.keep_pulse_timer = 0.0
self.prev_one_blinker = False
self.desire = log.Desire.none
self.dp_lat_lca_speed = float(dp_lat_lca_speed * CV.MPH_TO_MS)
self.dp_lat_lca_auto_sec = dp_lat_lca_auto_sec
self.dp_lat_lca_auto_sec_start = 0.
def update(self, carstate, lateral_active, lane_change_prob):
def update(self, carstate, lateral_active, lane_change_prob, left_edge_detected, right_edge_detected):
v_ego = carstate.vEgo
one_blinker = carstate.leftBlinker != carstate.rightBlinker
below_lane_change_speed = v_ego < LANE_CHANGE_SPEED_MIN
below_lane_change_speed = True if self.dp_lat_lca_speed == 0. else v_ego < self.dp_lat_lca_speed
if not lateral_active or self.lane_change_timer > LANE_CHANGE_TIME_MAX:
self.lane_change_state = LaneChangeState.off
@@ -53,6 +57,8 @@ class DesireHelper:
if self.lane_change_state == LaneChangeState.off and one_blinker and not self.prev_one_blinker and not below_lane_change_speed:
self.lane_change_state = LaneChangeState.preLaneChange
self.lane_change_ll_prob = 1.0
if self.dp_lat_lca_auto_sec > 0.:
self.dp_lat_lca_auto_sec_start = time.time()
# LaneChangeState.preLaneChange
elif self.lane_change_state == LaneChangeState.preLaneChange:
@@ -64,8 +70,16 @@ class DesireHelper:
((carstate.steeringTorque > 0 and self.lane_change_direction == LaneChangeDirection.left) or
(carstate.steeringTorque < 0 and self.lane_change_direction == LaneChangeDirection.right))
blindspot_detected = ((carstate.leftBlindspot and self.lane_change_direction == LaneChangeDirection.left) or
(carstate.rightBlindspot and self.lane_change_direction == LaneChangeDirection.right))
blindspot_detected = (((left_edge_detected or carstate.leftBlindspot) and self.lane_change_direction == LaneChangeDirection.left) or
((right_edge_detected or carstate.rightBlindspot) and self.lane_change_direction == LaneChangeDirection.right))
# reset timer
if self.dp_lat_lca_auto_sec > 0.:
if blindspot_detected:
self.dp_lat_lca_auto_sec_start = time.time()
else:
if (time.time() - self.dp_lat_lca_auto_sec_start) >= self.dp_lat_lca_auto_sec:
torque_applied = True
if not one_blinker or below_lane_change_speed:
self.lane_change_state = LaneChangeState.off
+2 -1
View File
@@ -2,8 +2,9 @@ import numpy as np
from abc import abstractmethod, ABC
from openpilot.common.realtime import DT_CTRL
from openpilot.common.params import Params
MIN_LATERAL_CONTROL_SPEED = 0.3 # m/s
MIN_LATERAL_CONTROL_SPEED = 2.5 if Params().get_bool("dp_vag_avoid_eps_lockout") else 0.3 # m/s
class LatControl(ABC):
+78 -10
View File
@@ -14,6 +14,8 @@ from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDX
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, get_speed_error
from openpilot.selfdrive.car.cruise import V_CRUISE_MAX, V_CRUISE_UNSET
from openpilot.common.swaglog import cloudlog
from dragonpilot.selfdrive.controls.lib.acm import ACM
from dragonpilot.selfdrive.controls.lib.aem import AEM
LON_MPC_STEP = 0.2 # first step is 0.2s
A_CRUISE_MAX_VALS = [1.6, 1.2, 0.8, 0.6]
@@ -26,6 +28,12 @@ MIN_ALLOW_THROTTLE_SPEED = 2.5
_A_TOTAL_MAX_V = [1.7, 3.2]
_A_TOTAL_MAX_BP = [20., 40.]
class DPFlags:
ACM = 1
ACM_DOWNHILL = 2
AEM = 2 ** 2
NO_GAS_GATING = 2 ** 3
pass
def get_max_accel(v_ego):
return np.interp(v_ego, A_CRUISE_MAX_BP, A_CRUISE_MAX_VALS)
@@ -84,6 +92,8 @@ class LongitudinalPlanner:
self.a_desired_trajectory = np.zeros(CONTROL_N)
self.j_desired_trajectory = np.zeros(CONTROL_N)
self.solverExecutionTime = 0.0
self.acm = ACM()
self.aem = AEM()
@staticmethod
def parse_model(model_msg, model_error):
@@ -105,8 +115,44 @@ class LongitudinalPlanner:
throttle_prob = 1.0
return x, v, a, j, throttle_prob
def update(self, sm):
self.mpc.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc'
def update(self, sm, dp_flags=0):
v_ego = sm['carState'].vEgo
# --- Calculate current cycle variables needed by AEM ---
self.v_model_error = get_speed_error(sm['modelV2'], v_ego)
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error)
self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED
# --- AEM Logic: Determine MPC mode ---
if sm['selfdriveState'].experimentalMode:
self.mpc.mode = 'blended'
else:
current_cycle_mpc_mode = 'acc' # Default to ACC (primary mode)
if (dp_flags & DPFlags.AEM) and not self.aem.enabled:
self.aem.enabled = True
self.aem._current_mpc_mode = self.mpc.mode
cloudlog.info(f"[LongitudinalPlanner] AEM enabled. Initializing internal mode to: {self.aem._current_mpc_mode}")
if self.aem.enabled:
steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
model_path_plan_for_aem = {'x': x, 'v': v, 'a': a, 'j': j}
current_cycle_mpc_mode = self.aem.get_mode(
v_ego_raw=v_ego,
lead_one_data_raw=sm['radarState'].leadOne,
steering_angle_deg_raw=steer_angle_without_offset,
standstill_raw=sm['carState'].standstill,
long_personality=self.aem.personality,
v_model_error_raw=self.v_model_error,
allow_throttle_planner=self.allow_throttle,
model_path_plan_raw=model_path_plan_for_aem,
a_target_from_prev_cycle=self.output_a_target,
model_predicts_stop_prev=self.output_should_stop,
fcw_active_prev=self.fcw,
mpc_source_prev=self.mpc.source
)
self.mpc.mode = current_cycle_mpc_mode
if len(sm['carControl'].orientationNED) == 3:
accel_coast = get_coast_accel(sm['carControl'].orientationNED[1])
@@ -126,6 +172,20 @@ class LongitudinalPlanner:
# PCM cruise speed may be updated a few cycles later, check if initialized
reset_state = reset_state or not v_cruise_initialized
# Update ACM status
if not sm['selfdriveState'].experimentalMode:
if not self.acm.enabled and dp_flags & DPFlags.ACM:
self.acm.enabled = True
self.acm.downhill_only = dp_flags & DPFlags.ACM_DOWNHILL
else:
self.acm.enabled = False
user_control = long_control_off if self.CP.openpilotLongitudinalControl else not sm['selfdriveState'].enabled
self.acm.update_states(sm['carControl'], sm['radarState'], user_control, v_ego, v_cruise)
if self.acm.just_disabled:
reset_state = True
# No change cost when user is controlling the speed, or when standstill
prev_accel_constraint = not (reset_state or sm['carState'].standstill)
@@ -143,13 +203,14 @@ class LongitudinalPlanner:
# Prevent divergence, smooth in current v_ego
self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego))
# Compute model v_ego error
self.v_model_error = get_speed_error(sm['modelV2'], v_ego)
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error)
# Don't clip at low speeds since throttle_prob doesn't account for creep
self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED
# AEM - move to top so it can access them
# # Compute model v_ego error
# self.v_model_error = get_speed_error(sm['modelV2'], v_ego)
# x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error)
# # Don't clip at low speeds since throttle_prob doesn't account for creep
# self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED
if not self.allow_throttle:
if not (dp_flags & DPFlags.NO_GAS_GATING) and not self.allow_throttle:
clipped_accel_coast = max(accel_coast, accel_clip[0])
clipped_accel_coast_interp = np.interp(v_ego, [MIN_ALLOW_THROTTLE_SPEED, MIN_ALLOW_THROTTLE_SPEED*2], [accel_clip[1], clipped_accel_coast])
accel_clip[1] = min(accel_clip[1], clipped_accel_coast_interp)
@@ -157,14 +218,18 @@ class LongitudinalPlanner:
if force_slow_decel:
v_cruise = 0.0
self.mpc.set_weights(prev_accel_constraint, personality=sm['selfdriveState'].personality)
self.aem.set_personality(v_ego, sm['selfdriveState'].personality)
self.mpc.set_weights(prev_accel_constraint, personality=self.aem.personality)
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=sm['selfdriveState'].personality)
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=self.aem.personality)
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
self.a_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
self.j_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC[:-1], self.mpc.j_solution)
# Apply ACM post-processing to the acceleration trajectory if active
self.a_desired_trajectory = self.acm.update_a_desired_trajectory(self.a_desired_trajectory)
# TODO counter is only needed because radar is glitchy, remove once radar is gone
self.fcw = self.mpc.crash_cnt > 2 and not sm['carState'].standstill
if self.fcw:
@@ -179,6 +244,9 @@ class LongitudinalPlanner:
output_a_target, self.output_should_stop = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory,
action_t=action_t, vEgoStopping=self.CP.vEgoStopping)
# Apply ACM to the final output acceleration target as well
output_a_target = self.acm.update_output_a_target(output_a_target)
for idx in range(2):
accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05)
self.output_a_target = np.clip(output_a_target, accel_clip[0], accel_clip[1])
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""
MIT Non-Commercial License
Copyright (c) 2019-, rav4kumar, Rick Lan, dragonpilot community, and a number of other of contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only,
subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
* Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
* Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import numpy as np
NEARSIDE_PROB = 0.2
EDGE_PROB = 0.35
class RoadEdgeDetector:
def __init__(self, enabled = False):
self._is_enabled = enabled
self.left_edge_detected = False
self.right_edge_detected = False
def update(self, road_edge_stds, lane_line_probs):
if not self._is_enabled:
return
left_road_edge_prob = np.clip(1.0 - road_edge_stds[0], 0.0, 1.0)
left_lane_nearside_prob = lane_line_probs[0]
right_road_edge_prob = np.clip(1.0 - road_edge_stds[1], 0.0, 1.0)
right_lane_nearside_prob = lane_line_probs[3]
self.left_edge_detected = (
left_road_edge_prob > EDGE_PROB and
left_lane_nearside_prob < NEARSIDE_PROB and
right_lane_nearside_prob >= left_lane_nearside_prob
)
self.right_edge_detected = (
right_road_edge_prob > EDGE_PROB and
right_lane_nearside_prob < NEARSIDE_PROB and
left_lane_nearside_prob >= right_lane_nearside_prob
)
def set_enabled(self, enabled):
self._is_enabled = enabled
def is_enabled(self):
return self._is_enabled
+11 -2
View File
@@ -4,7 +4,7 @@ from openpilot.common.params import Params
from openpilot.common.realtime import Priority, config_realtime_process
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.controls.lib.ldw import LaneDepartureWarning
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner
from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner, DPFlags
import cereal.messaging as messaging
@@ -22,10 +22,19 @@ def main():
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', 'selfdriveState'],
poll='modelV2')
dp_flags = 0
if params.get_bool("dp_lon_acm"):
dp_flags |= DPFlags.ACM
if params.get_bool("dp_lon_acm_downhill"):
dp_flags |= DPFlags.ACM_DOWNHILL
if params.get_bool("dp_lon_aem"):
dp_flags |= DPFlags.AEM
if params.get_bool("dp_lon_no_gas_gating"):
dp_flags |= DPFlags.NO_GAS_GATING
while True:
sm.update()
if sm.updated['modelV2']:
longitudinal_planner.update(sm)
longitudinal_planner.update(sm, dp_flags)
longitudinal_planner.publish(sm, pm)
ldw.update(sm.frame, sm['modelV2'], sm['carState'], sm['carControl'])
+5 -9
View File
@@ -6,6 +6,7 @@ from typing import Any
import capnp
from cereal import messaging, log, car
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.params import Params
from openpilot.common.realtime import DT_MDL, Priority, config_realtime_process
from openpilot.common.swaglog import cloudlog
@@ -51,7 +52,7 @@ class Track:
def __init__(self, identifier: int, v_lead: float, kalman_params: KalmanParams):
self.identifier = identifier
self.cnt = 0
self.aLeadTau = _LEAD_ACCEL_TAU
self.aLeadTau = FirstOrderFilter(_LEAD_ACCEL_TAU, 0.45, DT_MDL)
self.K_A = kalman_params.A
self.K_C = kalman_params.C
self.K_K = kalman_params.K
@@ -74,17 +75,12 @@ class Track:
# Learn if constant acceleration
if abs(self.aLeadK) < 0.5:
self.aLeadTau = _LEAD_ACCEL_TAU
self.aLeadTau.x = _LEAD_ACCEL_TAU
else:
self.aLeadTau *= 0.9
self.aLeadTau.update(0.0)
self.cnt += 1
def reset_a_lead(self, aLeadK: float, aLeadTau: float):
self.kf = KF1D([[self.vLead], [aLeadK]], self.K_A, self.K_C, self.K_K)
self.aLeadK = aLeadK
self.aLeadTau = aLeadTau
def get_RadarState(self, model_prob: float = 0.0):
return {
"dRel": float(self.dRel),
@@ -93,7 +89,7 @@ class Track:
"vLead": float(self.vLead),
"vLeadK": float(self.vLeadK),
"aLeadK": float(self.aLeadK),
"aLeadTau": float(self.aLeadTau),
"aLeadTau": float(self.aLeadTau.x),
"status": True,
"fcw": self.is_potential_fcw(model_prob),
"modelProb": model_prob,
+7 -2
View File
@@ -30,6 +30,7 @@ from openpilot.selfdrive.modeld.parse_model_outputs import Parser
from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
from openpilot.selfdrive.controls.lib.road_edge_detector import RoadEdgeDetector
PROCESS_NAME = "selfdrive.modeld.modeld"
@@ -212,7 +213,10 @@ def main(demo=False):
# TODO this needs more thought, use .2s extra for now to estimate other delays
steer_delay = CP.steerActuatorDelay + .2
DH = DesireHelper()
dp_lat_lca_speed = int(params.get("dp_lat_lca_speed"))
dp_lat_lca_auto_sec = float(params.get("dp_lat_lca_auto_sec"))
DH = DesireHelper(dp_lat_lca_speed=dp_lat_lca_speed, dp_lat_lca_auto_sec=dp_lat_lca_auto_sec)
RED = RoadEdgeDetector(params.get_bool("dp_lat_road_edge_detection"))
while True:
# Keep receiving frames until we are at least 1 frame ahead of previous extra frame
@@ -303,7 +307,8 @@ def main(demo=False):
l_lane_change_prob = desire_state[log.Desire.laneChangeLeft]
r_lane_change_prob = desire_state[log.Desire.laneChangeRight]
lane_change_prob = l_lane_change_prob + r_lane_change_prob
DH.update(sm['carState'], sm['carControl'].latActive, lane_change_prob)
RED.update(modelv2_send.modelV2.roadEdgeStds, modelv2_send.modelV2.laneLineProbs)
DH.update(sm['carState'], sm['carControl'].latActive, lane_change_prob, RED.left_edge_detected, RED.right_edge_detected)
modelv2_send.modelV2.meta.laneChangeState = DH.lane_change_state
modelv2_send.modelV2.meta.laneChangeDirection = DH.lane_change_direction
drivingdata_send.drivingModelData.meta.laneChangeState = DH.lane_change_state
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/env python3
import time
import cereal.messaging as messaging
from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, Ratekeeper, DT_DMON
from cereal import log
EventName = log.OnroadEvent.EventName
class SimpleDriverMonitoring:
def __init__(self):
# Timing configuration (in seconds)
self.FIRST_WARNING_TIME = 45.0
self.SECOND_WARNING_TIME = 60.0
self.THIRD_WARNING_TIME = 75.0
# State variables
self.awareness = 1.0 # Full awareness
self.current_events = []
# self.last_interaction_time = 0
self.hands_on_steering = False
# Warning thresholds (normalized to 0-1 scale)
self.threshold_prompt = self.FIRST_WARNING_TIME / self.THIRD_WARNING_TIME # ~0.643 for first warning
self.threshold_critical = self.SECOND_WARNING_TIME / self.THIRD_WARNING_TIME # ~0.857 for second warning
# Step change (how much awareness decreases per step)
self.step_change = DT_DMON / self.THIRD_WARNING_TIME
params = Params()
self.is_rhd = params.get_bool("dp_device_is_rhd")
self.monitoring_disabled = params.get_bool("dp_device_monitoring_disabled")
def update_events(self, reset_condition, op_engaged):
self.current_events = []
if self.monitoring_disabled:
return
# If not engaged, reset awareness and return
if not op_engaged:
self.awareness = 1.0
return
# Reset awareness on any reset condition (standstill, any input)
if reset_condition:
self.awareness = 1.0
return
# Only decrease awareness if we're not detecting hands on steering
self.awareness = max(self.awareness - self.step_change, 0.0)
# Determine alert level based on awareness
if self.awareness <= 0.0:
# Third warning (red alert) at 70 seconds
self.current_events.append(EventName.driverUnresponsive)
elif self.awareness <= (1.0 - self.threshold_critical):
# Second warning (orange alert) at 60 seconds
self.current_events.append(EventName.promptDriverUnresponsive)
elif self.awareness <= (1.0 - self.threshold_prompt):
# First warning (green alert) at 45 seconds
self.current_events.append(EventName.preDriverUnresponsive)
def get_state_packet(self, valid=True):
# Create driver monitoring state message
dat = messaging.new_message('driverMonitoringState', valid=valid)
events = []
for event_name in self.current_events:
event = log.OnroadEvent.new_message()
event.name = event_name
events.append(event)
dat.driverMonitoringState = {
"events": events,
"faceDetected": False, # Not using face detection
"isDistracted": self.awareness <= (1.0 - self.threshold_prompt),
"distractedType": 0, # Not using distraction types
"awarenessStatus": 1.0, #self.awareness, (always 1.0 so no decel)
"posePitchOffset": 0.0,
"posePitchValidCount": 0,
"poseYawOffset": 0.0,
"poseYawValidCount": 0,
"stepChange": self.step_change,
"awarenessActive": self.awareness,
"awarenessPassive": self.awareness,
"isLowStd": True,
"hiStdCount": 0,
"isActiveMode": True,
"isRHD": self.is_rhd,
}
return dat
def dmonitoringd_thread():
# Configure process priority
config_realtime_process([0, 1, 2, 3], 5)
# Initialize parameters and messaging
pm = messaging.PubMaster(['driverMonitoringState'])
sm = messaging.SubMaster(['carState', 'selfdriveState'])
# Initialize driver monitoring system
DM = SimpleDriverMonitoring()
# Create ratekeeper for 20Hz operation
rk = Ratekeeper(20, None)
# Main loop running at 20Hz
while True:
sm.update()
# Check if steering is touched (only monitoring steering for hands-on)
# Reset conditions: not engaged, standstill, or any input
reset_condition = (
sm['carState'].standstill or
sm['carState'].steeringPressed or
sm['carState'].gasPressed or
sm['carState'].brakePressed or
sm['carState'].leftBlinker or
sm['carState'].rightBlinker
)
# Process driver monitoring - monitoring only steering for hands-on
# but resetting on any input
DM.update_events(
reset_condition=reset_condition,
op_engaged=sm['selfdriveState'].enabled
)
# Publish driver monitoring state
dat = DM.get_state_packet()
pm.send('driverMonitoringState', dat)
# Maintain 20Hz
rk.keep_time()
def main():
dmonitoringd_thread()
if __name__ == '__main__':
main()
+3 -2
View File
@@ -372,6 +372,7 @@ void process_peripheral_state(Panda *panda, PubMaster *pm, bool no_fan_control)
static uint16_t prev_fan_speed = 999;
static uint16_t ir_pwr = 0;
static uint16_t prev_ir_pwr = 999;
const bool disable_driver = getenv("DISABLE_DRIVER");
static FirstOrderFilter integ_lines_filter(0, 30.0, 0.05);
@@ -386,7 +387,7 @@ void process_peripheral_state(Panda *panda, PubMaster *pm, bool no_fan_control)
}
}
if (sm.updated("driverCameraState")) {
if (!disable_driver && sm.updated("driverCameraState")) {
auto event = sm["driverCameraState"];
int cur_integ_lines = event.getDriverCameraState().getIntegLines();
@@ -403,7 +404,7 @@ void process_peripheral_state(Panda *panda, PubMaster *pm, bool no_fan_control)
}
// Disable IR on input timeout
if (nanos_since_boot() - last_driver_camera_t > 1e9) {
if (!disable_driver && nanos_since_boot() - last_driver_camera_t > 1e9) {
ir_pwr = 0;
}
+1 -1
View File
@@ -87,7 +87,7 @@ def main() -> None:
# TODO: remove this in the next AGNOS
# wait until USB is up before counting
if time.monotonic() < 25.:
if time.monotonic() < 35.:
no_internal_panda_count = 0
# Handle missing internal panda
+12 -5
View File
@@ -20,6 +20,7 @@ from openpilot.selfdrive.selfdrived.events import Events, ET
from openpilot.selfdrive.selfdrived.state import StateMachine
from openpilot.selfdrive.selfdrived.alertmanager import AlertManager, set_offroad_alert
from openpilot.selfdrive.controls.lib.latcontrol import MIN_LATERAL_CONTROL_SPEED
from openpilot.selfdrive.controls.lib.road_edge_detector import RoadEdgeDetector
from openpilot.system.version import get_build_metadata
@@ -58,6 +59,8 @@ class SelfdriveD:
self.car_events = CarSpecificEvents(self.CP)
self.disengage_on_accelerator = not (self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS)
self.alka = (self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALKA)
# Setup sockets
self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents'])
@@ -75,6 +78,8 @@ class SelfdriveD:
if REPLAY:
# no vipc in replay will make them ignored anyways
ignore += ['roadCameraState', 'wideRoadCameraState']
if os.getenv("DISABLE_DRIVER"):
ignore += ['driverCameraState']
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
@@ -113,11 +118,11 @@ class SelfdriveD:
self.experimental_mode = False
self.personality = self.read_personality_param()
self.recalibrating_seen = False
self.state_machine = StateMachine()
self.state_machine = StateMachine(self.alka)
self.rk = Ratekeeper(100, print_delay_threshold=None)
# Determine startup event
self.startup_event = EventName.startup if build_metadata.openpilot.comma_remote and build_metadata.tested_channel else EventName.startupMaster
self.startup_event = EventName.startup #if build_metadata.openpilot.comma_remote and build_metadata.tested_channel else EventName.startupMaster
if not car_recognized:
self.startup_event = EventName.startupNoCar
elif car_recognized and self.CP.passive:
@@ -131,6 +136,8 @@ class SelfdriveD:
elif self.CP.passive:
self.events.add(EventName.dashcamMode, static=True)
self.RED = RoadEdgeDetector(self.params.get_bool("dp_lat_road_edge_detection"))
def update_events(self, CS):
"""Compute onroadEvents from carState"""
@@ -220,9 +227,10 @@ class SelfdriveD:
# Handle lane change
if self.sm['modelV2'].meta.laneChangeState == LaneChangeState.preLaneChange:
self.RED.update(self.sm['modelV2'].roadEdgeStds, self.sm['modelV2'].laneLineProbs)
direction = self.sm['modelV2'].meta.laneChangeDirection
if (CS.leftBlindspot and direction == LaneChangeDirection.left) or \
(CS.rightBlindspot and direction == LaneChangeDirection.right):
if ((CS.leftBlindspot or self.RED.left_edge_detected) and direction == LaneChangeDirection.left) or \
((CS.rightBlindspot or self.RED.right_edge_detected) and direction == LaneChangeDirection.right):
self.events.add(EventName.laneChangeBlocked)
else:
if direction == LaneChangeDirection.left:
@@ -274,7 +282,6 @@ class SelfdriveD:
self.events.add(EventName.radarTempUnavailable)
else:
self.events.add(EventName.radarFault)
self.events.add(EventName.radarFault)
if not self.sm.valid['pandaStates']:
self.events.add(EventName.usbError)
if CS.canTimeout:
+3 -2
View File
@@ -9,10 +9,11 @@ ACTIVE_STATES = (State.enabled, State.softDisabling, State.overriding)
ENABLED_STATES = (State.preEnabled, *ACTIVE_STATES)
class StateMachine:
def __init__(self):
def __init__(self, alka = False):
self.current_alert_types = [ET.PERMANENT]
self.state = State.disabled
self.soft_disable_timer = 0
self.alka = alka
def update(self, events: Events):
# decrement the soft disable timer at every step, as it's reset on
@@ -92,7 +93,7 @@ class StateMachine:
# Check if openpilot is engaged and actuators are enabled
enabled = self.state in ENABLED_STATES
active = self.state in ACTIVE_STATES
if active:
if active or self.alka:
self.current_alert_types.append(ET.WARNING)
return enabled, active
+1 -1
View File
@@ -187,7 +187,7 @@ class TestOnroad:
def test_manager_starting_time(self):
st = self.ts['managerState']['t'][0]
assert (st - self.manager_st) < 10, f"manager.py took {st - self.manager_st}s to publish the first 'managerState' msg"
assert (st - self.manager_st) < 15, f"manager.py took {st - self.manager_st}s to publish the first 'managerState' msg"
def test_cloudlog_size(self):
msgs = self.msgs['logMessage']
+7 -1
View File
@@ -27,8 +27,9 @@ Export('widgets')
qt_libs = [widgets, qt_util] + base_libs
qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc",
"qt/offroad/model_selector.cc",
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
"qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc",
"qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc", "qt/offroad/dp_panel.cc",
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc",
"qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc",
"qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"]
@@ -58,6 +59,11 @@ qt_env.Command(assets, [assets_src, translations_assets_src], f"rcc $SOURCES -o
qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, translations_assets_src, "#selfdrive/assets/assets.o"]) + [lrelease])
asset_obj = qt_env.Object("assets", assets)
# build text and spinner everytime
qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs)
qt_env.Program("_text", ["qt/text.cc"], LIBS=qt_libs)
qt_env.Program("_spinner", ["qt/spinner.cc"], LIBS=qt_libs)
# build main UI
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
if GetOption('extras'):
+1 -1
View File
@@ -439,7 +439,7 @@ void WifiManager::addTetheringConnection() {
address["prefix"] = 24u;
connection["ipv4"]["address-data"] = QVariant::fromValue(IpConfig() << address);
connection["ipv4"]["gateway"] = "192.168.43.1";
connection["ipv4"]["route-metric"] = 1100;
connection["ipv4"]["never-default"] = true;
connection["ipv6"]["method"] = "ignore";
asyncCall(NM_DBUS_PATH_SETTINGS, NM_DBUS_INTERFACE_SETTINGS, "AddConnection", QVariant::fromValue(connection));
@@ -45,6 +45,21 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) {
// Toggles should be not available to change in onroad state
QObject::connect(uiState(), &UIState::offroadTransition, this, &DeveloperPanel::updateToggles);
// error logs
QPushButton* error_log_btn = new QPushButton(QObject::tr("Show Last Errors"));
error_log_btn->setObjectName("error_log_btn");
error_log_btn->setStyleSheet(R"(
#error_log_btn { height: 120px; border-radius: 15px; background-color: #393939; }
#error_log_btn:pressed { background-color: #4a4a4a; }
)");
addItem(error_log_btn);
QObject::connect(error_log_btn, &QPushButton::clicked, [=]() {
ConfirmationDialog::rich(QString::fromStdString(params.get("dp_device_last_log")), parent);
});
}
void DeveloperPanel::updateToggles(bool _offroad) {
+434
View File
@@ -0,0 +1,434 @@
#include "selfdrive/ui/qt/offroad/dp_panel.h"
void DPPanel::add_toyota_toggles() {
std::vector<std::tuple<QString, QString, QString>> toggle_defs{
{
"",
QString::fromUtf8("🐉 ") + tr("Toyota / Lexus"),
"",
},
{
"dp_toyota_door_auto_lock_unlock",
tr("Enable Door Auto Lock/Unlock"),
"",
},
{
"dp_toyota_tss1_sng",
tr("Enable TSS1 SnG Mod"),
"",
},
{
"dp_toyota_stock_lon",
tr("Use Stock Longitudinal Control"),
"",
},
};
QWidget *label = nullptr;
bool has_toggle = false;
for (auto &[param, title, desc] : toggle_defs) {
if (param.isEmpty()) {
label = new LabelControl(title, "");
addItem(label);
continue;
}
has_toggle = true;
auto toggle = new ParamControl(param, title, desc, "", this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
addItem(toggle);
toggles[param.toStdString()] = toggle;
}
// If no toggles were added, hide the label
if (!has_toggle && label) {
label->hide();
}
}
void DPPanel::add_vag_toggles() {
std::vector<std::tuple<QString, QString, QString>> toggle_defs{
{
"",
QString::fromUtf8("🐉 ") + tr("VW / Audi / Skoda"),
"",
},
{
"dp_vag_a0_sng",
tr("Enable MQB A0 SnG Mod"),
"",
},
{
"dp_vag_pq_steering_patch",
tr("PQ Steering Patch"),
""
},
{
"dp_vag_avoid_eps_lockout",
tr("Avoid EPS Lockout"),
"",
},
};
QWidget *label = nullptr;
bool has_toggle = false;
for (auto &[param, title, desc] : toggle_defs) {
if (param.isEmpty()) {
label = new LabelControl(title, "");
addItem(label);
continue;
}
has_toggle = true;
auto toggle = new ParamControl(param, title, desc, "", this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
addItem(toggle);
toggles[param.toStdString()] = toggle;
}
// If no toggles were added, hide the label
if (!has_toggle && label) {
label->hide();
}
}
void DPPanel::add_mazda_toggles() {
std::vector<std::tuple<QString, QString, QString>> toggle_defs{
{
"",
QString::fromUtf8("🐉 ") + tr("Mazda"),
"",
},
};
QWidget *label = nullptr;
bool has_toggle = false;
for (auto &[param, title, desc] : toggle_defs) {
if (param.isEmpty()) {
label = new LabelControl(title, "");
addItem(label);
continue;
}
has_toggle = true;
auto toggle = new ParamControl(param, title, desc, "", this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
addItem(toggle);
toggles[param.toStdString()] = toggle;
}
// If no toggles were added, hide the label
if (!has_toggle && label) {
label->hide();
}
}
void DPPanel::add_lateral_toggles() {
std::vector<std::tuple<QString, QString, QString>> toggle_defs{
{
"",
QString::fromUtf8("🐉 ") + tr("Lateral Ctrl"),
"",
},
{
"dp_lat_alka",
tr("Always-on Lane Keeping Assist (ALKA)"),
"",
},
{
"dp_lat_road_edge_detection",
tr("Road Edge Detection (RED)"),
tr("Block lane change assist when the system detects the road edge.\nNOTE: This will show 'Car Detected in Blindspot' warning.")
},
};
auto lca_speed_toggle = new ParamSpinBoxControl("dp_lat_lca_speed", tr("LCA Speed:"), tr("Off = Disable LCA\n1 mph ≈ 1.2 km/h"), "", 0, 100, 5, tr(" mph"), tr("Off"));
lca_sec_toggle = new ParamDoubleSpinBoxControl("dp_lat_lca_auto_sec", QString::fromUtf8(" ") + tr("Auto Lane Change after:"), tr("Off = Disable Auto Lane Change."), "", 0, 5.0, 0.5, tr(" sec"), tr("Off"));
QWidget *label = nullptr;
bool has_toggle = false;
for (auto &[param, title, desc] : toggle_defs) {
if (param.isEmpty()) {
label = new LabelControl(title, "");
addItem(label);
addItem(lca_speed_toggle);
addItem(lca_sec_toggle);
has_toggle = true;
continue;
}
has_toggle = true;
auto toggle = new ParamControl(param, title, desc, "", this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
addItem(toggle);
toggles[param.toStdString()] = toggle;
}
// If no toggles were added, hide the label
if (!has_toggle && label) {
label->hide();
}
}
void DPPanel::add_longitudinal_toggles() {
std::vector<std::tuple<QString, QString, QString>> toggle_defs{
{
"",
QString::fromUtf8("🐉 ") + tr("Longitudinal Ctrl"),
"",
},
{
"dp_lon_ext_radar",
tr("Use External Radar"),
tr("See https://github.com/eFiniLan/openpilot-ext-radar-addon for more information."),
},
{
"dp_lon_acm",
QString::fromUtf8("🚧 ") + tr("Enable Adaptive Coasting Mode (ACM)"),
tr("Adaptive Coasting Mode (ACM) reduces braking to allow smoother coasting when appropriate.\nDOES NOT WORK with Experimental Mode enabled."),
},
{
"dp_lon_acm_downhill",
QString::fromUtf8(" ") + tr("Downhill Only"),
tr("Limited to downhill driving."),
},
{
"dp_lon_aem",
QString::fromUtf8("🚧 ") + tr("Adaptive Experimental Mode (AEM)"),
tr("Adaptive mode switcher between ACC and Blended based on driving context."),
},
{
"dp_lon_no_gas_gating",
tr("Enable No Gas Gating (NoGG)"),
tr("Allows the car to accelerate in situations where Gas Gating would normally prevent it, like approaching traffic lights or exits."),
},
};
QWidget *label = nullptr;
bool has_toggle = false;
for (auto &[param, title, desc] : toggle_defs) {
if (param.isEmpty()) {
label = new LabelControl(title, "");
addItem(label);
continue;
}
if (param == "dp_lon_ext_radar") {
if (!vehicle_has_radar_unavailable || !vehicle_has_long_ctrl) {
continue;
}
}
if ((param == "dp_lon_acm" || param == "dp_lon_acm_downhill") && !vehicle_has_long_ctrl) {
continue;
}
if (param == "dp_lon_aem" && !vehicle_has_long_ctrl) {
continue;
}
has_toggle = true;
auto toggle = new ParamControl(param, title, desc, "", this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
addItem(toggle);
toggles[param.toStdString()] = toggle;
}
// If no toggles were added, hide the label
if (!has_toggle && label) {
label->hide();
}
}
void DPPanel::add_ui_toggles() {
std::vector<std::tuple<QString, QString, QString>> toggle_defs{
{
"",
QString::fromUtf8("🐉 ") + tr("UI"),
"",
},
{
"dp_ui_radar_tracks",
tr("Display Radar Tracks"),
"",
},
{
"dp_ui_rainbow",
tr("Rainbow Driving Path like Tesla"),
tr("Why not?"),
},
};
std::vector<QString> display_off_mode_texts{tr("Std."), tr("MAIN+"), tr("OP+"), tr("MAIN-"), tr("OP-")};
ButtonParamControl* display_off_mode_setting = new ButtonParamControl("dp_ui_display_mode", tr("Display Mode"),
tr("Std. - Stock behavior.\nMAIN+ - ACC MAIN on = Display ON.\nOP+ - OP enabled = Display ON.\nMAIN- - ACC MAIN on = Display OFF\nOP- - OP enabled = Display OFF."),
"",
display_off_mode_texts, 200);
auto hide_hud = new ParamSpinBoxControl("dp_ui_hide_hud_speed_kph", tr("Hide HUD When Moves above:"), tr("To prevent screen burn-in, hide Speed, MAX Speed, and Steering/DM Icons when the car moves.\nOff = Stock Behavior\n1 km/h ≈ 0.6 mph"), "", 0, 120, 5, tr(" km/h"), tr("Off"));
QWidget *label = nullptr;
bool has_toggle = false;
for (auto &[param, title, desc] : toggle_defs) {
if (param.isEmpty()) {
label = new LabelControl(title, "");
addItem(label);
addItem(display_off_mode_setting);
has_toggle = true;
addItem(hide_hud);
has_toggle = true;
continue;
}
if (param == "dp_ui_radar_tracks" && !vehicle_has_long_ctrl) {
continue;
}
has_toggle = true;
auto toggle = new ParamControl(param, title, desc, "", this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
addItem(toggle);
toggles[param.toStdString()] = toggle;
}
// If no toggles were added, hide the label
if (!has_toggle && label) {
label->hide();
}
}
void DPPanel::add_device_toggles() {
std::vector<std::tuple<QString, QString, QString>> toggle_defs{
{
"",
QString::fromUtf8("🐉 ") + tr("Device"),
"",
},
{
"dp_device_is_rhd",
tr("Enable Right-Hand Drive Mode"),
tr("Allow openpilot to obey right-hand traffic conventions on right driver seat."),
},
{
"dp_device_monitoring_disabled",
tr("Disable Driver Monitoring"),
"",
},
{
"dp_device_beep",
tr("Enable Beep (Warning)"),
"",
}
};
std::vector<QString> audible_alert_mode_texts{tr("Std."), tr("Warning"), tr("Off")};
ButtonParamControl* audible_alert_mode_setting = new ButtonParamControl("dp_device_audible_alert_mode", tr("Audible Alert Mode"),
tr("Warning - Only emits sound when there is a warning.\nOff - Does not emit any sound at all."),
"",
audible_alert_mode_texts);
auto auto_shutdown_toggle = new ParamSpinBoxControl("dp_device_auto_shutdown_in", tr("Auto Shutdown In:"), tr("0 mins = Immediately"), "", -5, 300, 5, tr(" mins"), tr("Off"));
QWidget *label = nullptr;
bool has_toggle = false;
const bool disable_driver = getenv("DISABLE_DRIVER");
for (auto &[param, title, desc] : toggle_defs) {
if (param.isEmpty()) {
label = new LabelControl(title, "");
addItem(label);
addItem(auto_shutdown_toggle);
continue;
}
if ((param == "dp_device_is_rhd" || param == "dp_device_monitoring_disabled" || param == "dp_device_beep") && !disable_driver) {
continue;
}
has_toggle = true;
auto toggle = new ParamControl(param, title, desc, "", this);
bool locked = params.getBool((param + "Lock").toStdString());
toggle->setEnabled(!locked);
addItem(toggle);
toggles[param.toStdString()] = toggle;
}
if (!getenv("DISABLE_DRIVER")) { // lite check
addItem(audible_alert_mode_setting);
has_toggle = true;
}
// If no toggles were added, hide the label
if (!has_toggle && label) {
label->hide();
}
}
DPPanel::DPPanel(SettingsWindow *parent) : ListWidget(parent) {
auto cp_bytes = params.get("CarParamsPersistent");
if (!cp_bytes.empty()) {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size()));
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
brand = QString::fromStdString(CP.getBrand());
vehicle_has_long_ctrl = hasLongitudinalControl(CP);
vehicle_has_radar_unavailable = CP.getRadarUnavailable();
}
if (brand == "toyota") {
add_toyota_toggles();
} else if (brand == "volkswagen") {
add_vag_toggles();
} else if (brand == "mazda") {
add_mazda_toggles();
}
add_lateral_toggles();
add_longitudinal_toggles();
add_ui_toggles();
add_device_toggles();
auto resetBtn = new ButtonControl(tr("Reset dragonpilot settings"), tr("RESET"));
connect(resetBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset all settings?"), tr("Reset"), this)) {
params.putBool("dp_device_reset_conf", true);
}
});
addItem(resetBtn);
fs_watch = new ParamWatcher(this);
QObject::connect(fs_watch, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
updateStates();
});
connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
is_onroad = !offroad;
updateStates();
});
updateStates();
}
void DPPanel::showEvent(QShowEvent *event) {
updateStates();
}
void DPPanel::updateStates() {
// do fs_watch here
if (!isVisible()) {
return;
}
// do state change logic here
lca_sec_toggle->setVisible(std::atoi(params.get("dp_lat_lca_speed").c_str()) > 0);
if (vehicle_has_long_ctrl) {
toggles["dp_lon_acm_downhill"]->setVisible(params.getBool("dp_lon_acm"));
}
}
void DPPanel::expandToggleDescription(const QString &param) {
toggles[param.toStdString()]->showDescription();
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include "selfdrive/ui/qt/offroad/settings.h"
class DPPanel : public ListWidget {
Q_OBJECT
public:
explicit DPPanel(SettingsWindow *parent);
public slots:
void expandToggleDescription(const QString &param);
private:
Params params;
ParamWatcher *fs_watch;
std::map<std::string, ParamControl*> toggles;
QString brand;
bool is_onroad = false;
bool vehicle_has_long_ctrl;
bool vehicle_has_radar_unavailable;
void add_toyota_toggles();
void add_vag_toggles();
void add_mazda_toggles();
void add_lateral_toggles();
void add_longitudinal_toggles();
void add_ui_toggles();
void add_device_toggles();
void updateStates();
void showEvent(QShowEvent *event) override;
ParamDoubleSpinBoxControl* lca_sec_toggle;
};
+226
View File
@@ -0,0 +1,226 @@
// MIT Non-Commercial License
//
// Copyright (c) 2019, dragonpilot
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
//
// - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// - Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
// - Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "selfdrive/ui/qt/offroad/model_selector.h"
// Define style constants to improve maintainability
namespace {
const QString SELECTOR_BTN_STYLE = "background-color: #00309a; font-size: 48px;";
const QString MODEL_LIST_STYLE = "font-size: 64px;";
const QString SCROLLBAR_STYLE = "width: 96px;";
const QString GROUP_HEADER_BG_COLOR = "#c8c8c8"; // Light gray
const QString GROUP_HEADER_TEXT_COLOR = "#000000"; // Black
// Role for storing the actual model name without indentation
const int ModelNameRole = Qt::UserRole;
}
ModelSelector::ModelSelector(QWidget *parent) : QWidget(parent) {
setupUI();
setupModelListPanel();
connectSignals();
}
QWidget* ModelSelector::setupUI() {
QVBoxLayout* main_layout = new QVBoxLayout(this);
main_layout->addSpacing(10);
// Selector button
QWidget* model_selector_btn_widget = new QWidget;
QHBoxLayout* model_selector_btn_layout = new QHBoxLayout();
QLabel* vehicle_model_label = new QLabel(tr("Vehicle Model:"));
vehicle_model_label->setStyleSheet("margin-right: 2px; font-size: 48px;");
model_selector_btn_layout->addWidget(vehicle_model_label);
QString model_selected = QString::fromUtf8(Params().get("dp_device_model_selected").c_str());
model_selector_btn = new QPushButton(model_selected.isEmpty() ? tr("[AUTO DETECT]") : model_selected);
model_selector_btn->setObjectName("ModelSelectorBtn");
model_selector_btn->setStyleSheet(SELECTOR_BTN_STYLE);
model_selector_btn_layout->addWidget(model_selector_btn);
model_selector_btn_layout->setAlignment(Qt::AlignCenter);
model_selector_btn_layout->setStretch(1, 1);
model_selector_btn_widget->setLayout(model_selector_btn_layout);
main_layout->addWidget(model_selector_btn_widget);
main_layout->addSpacing(10);
main_layout->addStretch(); // Add stretch to push everything to the top
setLayout(main_layout);
return model_selector_btn_widget;
}
void ModelSelector::setupModelListPanel() {
// Create model list panel
model_list_panel = new QWidget();
QVBoxLayout* model_list_layout = new QVBoxLayout(model_list_panel);
model_list_layout->setContentsMargins(50, 25, 50, 25);
model_list = new QListWidget(model_list_panel);
// Set styles using the constants
QString listStyle = QString("QListWidget { %1 } QScrollBar:vertical { %2 }")
.arg(MODEL_LIST_STYLE)
.arg(SCROLLBAR_STYLE);
model_list->setStyleSheet(listStyle);
model_list->setFixedHeight(750);
model_list_layout->addWidget(model_list);
model_list_frame = new ScrollView(model_list_panel, nullptr);
}
void ModelSelector::loadModelList() {
if (model_list->count() > 0) {
// If list is already populated, just update the selection
updateCurrentSelection();
return;
}
// Add auto-detect option
QListWidgetItem* autoDetectItem = new QListWidgetItem(tr("[AUTO DETECT]"));
autoDetectItem->setData(ModelNameRole, tr("[AUTO DETECT]"));
model_list->addItem(autoDetectItem);
Params params;
QString model_list_str = QString::fromStdString(params.get("dp_device_model_list"));
QJsonDocument document = QJsonDocument::fromJson(model_list_str.toUtf8());
if (document.isArray()) {
QJsonArray models = document.array();
for (const auto& groupValue : models) {
QJsonObject group = groupValue.toObject();
QString groupName = group["group"].toString();
// Add group header item
QListWidgetItem* groupHeader = new QListWidgetItem(groupName);
groupHeader->setFlags(Qt::NoItemFlags); // Make non-selectable
groupHeader->setBackground(QColor(GROUP_HEADER_BG_COLOR));
groupHeader->setForeground(QColor(GROUP_HEADER_TEXT_COLOR));
groupHeader->setTextAlignment(Qt::AlignCenter);
model_list->addItem(groupHeader);
// Add models in this group
QJsonArray groupModels = group["models"].toArray();
for (const auto& model : groupModels) {
QString modelName = model.toString();
// Create item with visual indentation
QListWidgetItem* modelItem = new QListWidgetItem(" " + modelName);
// Store actual model name without indentation in user role
modelItem->setData(ModelNameRole, modelName);
model_list->addItem(modelItem);
}
}
}
// Set the current selection after loading
updateCurrentSelection();
}
// New helper method to update the selection
void ModelSelector::updateCurrentSelection() {
// Get the currently selected model from params
Params params;
QString currentModel = QString::fromStdString(params.get("dp_device_model_selected"));
// If empty, select the AUTO DETECT option
if (currentModel.isEmpty()) {
model_list->setCurrentRow(0); // AUTO DETECT is the first item
return;
}
// Otherwise, find and select the matching model
for (int i = 0; i < model_list->count(); i++) {
QListWidgetItem* item = model_list->item(i);
// Only check selectable items (not group headers)
if (item->flags() & Qt::ItemIsSelectable) {
QString modelName = item->data(ModelNameRole).toString();
if (modelName == currentModel) {
model_list->setCurrentItem(item);
break;
}
}
}
}
void ModelSelector::clearModelList() {
model_list->clear();
}
void ModelSelector::updateButtonText(const QString& text) {
model_selector_btn->setText(text);
}
QWidget* ModelSelector::getModelListPanel() {
return model_list_frame;
}
void ModelSelector::setPanelWidget(QStackedWidget* panel) {
panel_widget = panel;
if (panel_widget && model_list_frame->parent() != panel_widget) {
model_list_frame->setParent(panel_widget);
panel_widget->addWidget(model_list_frame);
}
}
void ModelSelector::setNavButtonGroup(QButtonGroup* buttons) {
nav_btns = buttons;
}
void ModelSelector::connectSignals() {
connect(model_selector_btn, &QPushButton::clicked, [this]() {
if (panel_widget) {
// Load the model list when needed
loadModelList();
panel_widget->setCurrentWidget(model_list_frame);
}
emit buttonClicked();
});
connect(model_list, &QListWidget::itemClicked, [this](QListWidgetItem* item) {
// Only process clicks on selectable items (not group headers)
if (item->flags() & Qt::ItemIsSelectable) {
// Get model name from the data role rather than trimming text
QString model_name = item->data(ModelNameRole).toString();
QString param_value = (model_name == tr("[AUTO DETECT]")) ? QString() : model_name;
// Update param and button text
Params().put("dp_device_model_selected", param_value.toStdString());
updateButtonText(param_value.isEmpty() ? tr("[AUTO DETECT]") : model_name);
// Emit signal that model was selected
emit modelSelected(model_name);
// Go back to the previous panel
if (nav_btns && nav_btns->checkedButton()) {
nav_btns->checkedButton()->click();
} else if (nav_btns && nav_btns->buttons().size() > 0) {
// Default to first panel if none selected
nav_btns->buttons().first()->click();
}
}
});
if (model_list_frame) {
model_list_frame->installEventFilter(this);
}
}
bool ModelSelector::eventFilter(QObject *obj, QEvent *event) {
if (obj == model_list_frame) {
if (event->type() == QEvent::Hide) {
clearModelList();
}
}
return QWidget::eventFilter(obj, event);
}
+69
View File
@@ -0,0 +1,69 @@
// MIT Non-Commercial License
//
// Copyright (c) 2019, dragonpilot
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, for non-commercial purposes only, subject to the following conditions:
//
// - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// - Commercial use (e.g., use in a product, service, or activity intended to generate revenue) is prohibited without explicit written permission from dragonpilot. Contact ricklan@gmail.com for inquiries.
// - Any project that uses the Software must visibly mention the following acknowledgment: "This project uses software from dragonpilot and is licensed under a custom license requiring permission for use."
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#pragma once
#include <QWidget>
#include <QPushButton>
#include <QListWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QListWidgetItem>
#include <QDebug>
#include <QStackedWidget>
#include <QButtonGroup> // Add this include for QButtonGroup
#include <QEvent>
#include "common/params.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
class ModelSelector : public QWidget {
Q_OBJECT
public:
explicit ModelSelector(QWidget *parent = nullptr);
// Get the model list panel widget
QWidget* getModelListPanel();
// Set the panel widget to switch to when selecting models
void setPanelWidget(QStackedWidget* panel);
// Set the button group to return to after model selection
void setNavButtonGroup(QButtonGroup* nav_btns);
signals:
void buttonClicked();
void modelSelected(const QString& model_name);
private:
QPushButton* model_selector_btn;
QListWidget* model_list;
QWidget* model_list_panel;
ScrollView* model_list_frame;
QStackedWidget* panel_widget = nullptr;
QButtonGroup* nav_btns = nullptr;
QWidget* setupUI();
void setupModelListPanel();
void loadModelList();
void updateCurrentSelection();
void clearModelList();
void connectSignals();
void updateButtonText(const QString& text);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};
+51 -13
View File
@@ -15,6 +15,8 @@
#include "selfdrive/ui/qt/widgets/scrollview.h"
#include "selfdrive/ui/qt/offroad/developer_panel.h"
#include "selfdrive/ui/qt/offroad/firehose.h"
#include "selfdrive/ui/qt/offroad/dp_panel.h"
#include "selfdrive/ui/qt/offroad/model_selector.h"
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// param, title, desc, icon
@@ -61,6 +63,18 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
tr("Display speed in km/h instead of mph."),
"../assets/offroad/icon_metric.png",
},
{
"DisableLogging",
tr("Disable Logging"),
"",
"../assets/offroad/icon_empty.svg",
},
{
"DisableUpdates",
tr("Disable Updates"),
"",
"../assets/offroad/icon_empty.svg",
},
};
@@ -74,8 +88,11 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
// set up uiState update for personality setting
QObject::connect(uiState(), &UIState::uiUpdate, this, &TogglesPanel::updateState);
const bool disable_driver = getenv("DISABLE_DRIVER");
for (auto &[param, title, desc, icon] : toggle_defs) {
if ((param == "AlwaysOnDM" || param == "RecordFront") && disable_driver) {
continue;
}
auto toggle = new ParamControl(param, title, desc, icon, this);
bool locked = params.getBool((param + "Lock").toStdString());
@@ -173,6 +190,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(new LabelControl(tr("Dongle ID"), getDongleId().value_or(tr("N/A"))));
addItem(new LabelControl(tr("Serial"), params.get("HardwareSerial").c_str()));
const bool disable_driver = getenv("DISABLE_DRIVER");
pair_device = new ButtonControl(tr("Pair Device"), tr("PAIR"),
tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
connect(pair_device, &ButtonControl::clicked, [=]() {
@@ -182,12 +200,12 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(pair_device);
// offroad-only buttons
if (!disable_driver) {
auto dcamBtn = new ButtonControl(tr("Driver Camera"), tr("PREVIEW"),
tr("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)"));
connect(dcamBtn, &ButtonControl::clicked, [=]() { emit showDriverView(); });
addItem(dcamBtn);
}
auto resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), "");
connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, this, &DevicePanel::updateCalibDescription);
connect(resetCalibBtn, &ButtonControl::clicked, [&]() {
@@ -231,13 +249,13 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
QObject::connect(uiState()->prime_state, &PrimeState::changed, [this] (PrimeState::Type type) {
pair_device->setVisible(type == PrimeState::PRIME_TYPE_UNPAIRED);
});
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<ButtonControl *>()) {
if (btn != pair_device) {
btn->setEnabled(offroad);
}
}
});
// QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
// for (auto btn : findChildren<ButtonControl *>()) {
// if (btn != pair_device) {
// btn->setEnabled(offroad);
// }
// }
// });
// power buttons
QHBoxLayout *power_layout = new QHBoxLayout();
@@ -326,7 +344,7 @@ void SettingsWindow::setCurrentPanel(int index, const QString &param) {
if (param.endsWith("Panel")) {
QString panelName = param;
panelName.chop(5); // Remove "Panel" suffix
// Find the panel by name
for (int i = 0; i < nav_btns->buttons().size(); i++) {
if (nav_btns->buttons()[i]->text() == tr(panelName.toStdString().c_str())) {
@@ -338,7 +356,7 @@ void SettingsWindow::setCurrentPanel(int index, const QString &param) {
emit expandToggleDescription(param);
}
}
panel_widget->setCurrentIndex(index);
nav_btns->buttons()[index]->setChecked(true);
}
@@ -387,6 +405,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
{tr("Software"), new SoftwarePanel(this)},
{tr("Firehose"), new FirehosePanel(this)},
{tr("Developer"), new DeveloperPanel(this)},
{"DP", new DPPanel(this)},
};
nav_btns = new QButtonGroup(this);
@@ -431,7 +450,26 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
sidebar_widget->setFixedWidth(500);
main_layout->addWidget(sidebar_widget);
main_layout->addWidget(panel_widget);
// Create right column with model selector on top and panel_widget below
QWidget* right_column = new QWidget(this);
QVBoxLayout* right_layout = new QVBoxLayout(right_column);
right_layout->setContentsMargins(0, 0, 0, 0);
right_layout->setSpacing(20); // Space between model selector and panel
// Create the ModelSelector button at the top of right column
ModelSelector* model_selector = new ModelSelector(this);
right_layout->addWidget(model_selector);
// Set up panel widget and nav button references
model_selector->setPanelWidget(panel_widget);
model_selector->setNavButtonGroup(nav_btns);
// Add panel_widget below the model selector
right_layout->addWidget(panel_widget, 1); // Give panel_widget stretch priority
// Add right column to main layout
main_layout->addWidget(right_column);
setStyleSheet(R"(
* {
+1
View File
@@ -92,6 +92,7 @@ private:
QLabel *onroadLbl;
LabelControl *versionLbl;
ButtonControl *installBtn;
ButtonControl *onOffRoadBtn;
ButtonControl *downloadBtn;
ButtonControl *targetBranchBtn;
+21 -1
View File
@@ -29,6 +29,16 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
versionLbl = new LabelControl(tr("Current Version"), "");
addItem(versionLbl);
// on/off road mode switch
onOffRoadBtn = new ButtonControl(tr("Onroad/Offroad Mode"), tr("Go Offroad"));
connect(onOffRoadBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to switch mode?"), tr("CONFIRM"), this)) {
bool val = params.getBool("dp_device_go_off_road");
params.putBool("dp_device_go_off_road", !val);
}
});
addItem(onOffRoadBtn);
// download update btn
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
connect(downloadBtn, &ButtonControl::clicked, [=]() {
@@ -50,6 +60,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
addItem(installBtn);
// branch selecting
/*
targetBranchBtn = new ButtonControl(tr("Target Branch"), tr("SELECT"));
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
auto current = params.get("GitBranch");
@@ -73,6 +84,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
if (!params.getBool("IsTestedBranch")) {
addItem(targetBranchBtn);
}
*/
// uninstall button
auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL"));
@@ -109,6 +121,7 @@ void SoftwarePanel::updateLabels() {
fs_watch->addParam("UpdateFailedCount");
fs_watch->addParam("UpdaterState");
fs_watch->addParam("UpdateAvailable");
fs_watch->addParam("dp_device_go_off_road");
if (!isVisible()) {
return;
@@ -118,6 +131,13 @@ void SoftwarePanel::updateLabels() {
onroadLbl->setVisible(is_onroad);
downloadBtn->setVisible(!is_onroad);
// on/off road text change
if (params.getBool("dp_device_go_off_road")) {
onOffRoadBtn->setText(tr("Go Onroad"));
} else {
onOffRoadBtn->setText(tr("Go Offroad"));
}
// download update
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
bool failed = std::atoi(params.get("UpdateFailedCount").c_str()) > 0;
@@ -142,7 +162,7 @@ void SoftwarePanel::updateLabels() {
}
downloadBtn->setEnabled(true);
}
targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch")));
// targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch")));
// current + new versions
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")));
+14 -4
View File
@@ -24,7 +24,9 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget *par
void AnnotatedCameraWidget::updateState(const UIState &s) {
// update engageability/experimental mode button
experimental_btn->updateState(s);
dmon.updateState(s);
if (!s.scene.disable_driver) {
dmon.updateState(s);
}
}
void AnnotatedCameraWidget::initializeGL() {
@@ -130,9 +132,17 @@ void AnnotatedCameraWidget::paintGL() {
painter.setPen(Qt::NoPen);
model.draw(painter, rect());
dmon.draw(painter, rect());
hud.updateState(*s);
hud.draw(painter, rect());
bool hide_hud = s->scene.dp_ui_hide_hud_speed_kph > 0 && sm["carState"].getCarState().getVEgo() > s->scene.dp_ui_hide_hud_speed_kph * 0.278;
if (!hide_hud) {
if (!s->scene.disable_driver) {
dmon.draw(painter, rect());
}
hud.updateState(*s);
hud.draw(painter, rect());
experimental_btn->setVisible(true);
} else {
experimental_btn->setVisible(false);
}
double cur_draw_t = millis_since_boot();
double dt = cur_draw_t - prev_draw_t;
+1 -1
View File
@@ -19,7 +19,7 @@ void drawIcon(QPainter &p, const QPoint &center, const QPixmap &img, const QBrus
ExperimentalButton::ExperimentalButton(QWidget *parent) : experimental_mode(false), engageable(false), QPushButton(parent) {
setFixedSize(btn_size, btn_size);
engage_img = loadPixmap("../assets/img_chffr_wheel.png", {img_size, img_size});
engage_img = loadPixmap("../assets/dragonpilot.png", {img_size, img_size});
experimental_img = loadPixmap("../assets/img_experimental.svg", {img_size, img_size});
QObject::connect(this, &QPushButton::clicked, this, &ExperimentalButton::changeMode);
}
+88 -1
View File
@@ -48,6 +48,11 @@ void ModelRenderer::draw(QPainter &painter, const QRect &surface_rect) {
}
}
if (s->scene.dp_ui_radar_tracks) {
const auto &live_tracks = sm["liveTracks"].getLiveTracks();
drawLiveTracks(painter, live_tracks, model, surface_rect);
}
painter.restore();
}
@@ -107,7 +112,39 @@ void ModelRenderer::drawLaneLines(QPainter &painter) {
void ModelRenderer::drawPath(QPainter &painter, const cereal::ModelDataV2::Reader &model, int height) {
QLinearGradient bg(0, height, 0, 0);
if (experimental_mode) {
auto *s = uiState();
if (s->scene.dp_ui_rainbow) {
constexpr int NUM_COLORS = 25;
constexpr int ALPHA = 128;
float v_ego = (*uiState()->sm)["carState"].getCarState().getVEgo();
if (!dp_rainbow_init) {
dp_rainbow_color_list.reserve(NUM_COLORS);
for (int i = 0; i < NUM_COLORS; ++i) {
qreal t = static_cast<qreal>(i) / (NUM_COLORS - 1);
dp_rainbow_color_list.append(QColor::fromHsvF(t, 1.0, 1.0, ALPHA / 255.0));
}
dp_rainbow_init = true;
}
bg.setSpread(QGradient::RepeatSpread);
// bigger = faster, however it is still limited to the global UI_FREQ (refresh rate)
// only way to make it move faster is to reduce NUM_COLORS, but that will also reduce the color smoothness.
qreal rotation_speed = std::max(0.01f, v_ego) / UI_FREQ;
dp_rainbow_rotation -= rotation_speed;
if (dp_rainbow_rotation < 0.0) {
dp_rainbow_rotation += 1.0;
dp_rainbow_color_list.append(dp_rainbow_color_list.takeFirst());
}
// fill color
const qreal step = 1.0 / (NUM_COLORS - 1);
for (int i = 0; i < NUM_COLORS; ++i) {
bg.setColorAt(i * step, dp_rainbow_color_list.at(i));
}
} else if (experimental_mode) {
// The first half of track_vertices are the points for the right side of the path
const auto &acceleration = model.getAcceleration().getX();
const int max_len = std::min<int>(track_vertices.length() / 2, acceleration.size());
@@ -186,6 +223,56 @@ QColor ModelRenderer::blendColors(const QColor &start, const QColor &end, float
(1 - t) * start.alphaF() + t * end.alphaF());
}
void ModelRenderer::drawLiveTracks(QPainter &painter,
const cereal::RadarData::Reader &live_tracks,
const cereal::ModelDataV2::Reader &model_data,
const QRect &surface_rect) {
// Get the model's predicted path for Z-coordinate calculation
const auto& model_path_position = model_data.getPosition();
// Set text properties
painter.setPen(Qt::white);
painter.setFont(QFont("Inter", 24, QFont::Bold));
// Iterate through each radar point from live_tracks
for (const auto& point : live_tracks.getPoints()) {
float dRel = point.getDRel();
float yRel = point.getYRel();
float yvRel = point.getYvRel();
float vRel = point.getVRel();
// Calculate Z-coordinate using the model's path
float z_on_path = path_offset_z; // Default base offset
// Ensure dRel is non-negative for indexing
if (dRel >= 0) {
z_on_path += model_path_position.getZ()[get_path_length_idx(model_path_position, dRel)];
}
QPointF screen_pos;
// mapToScreen projects a point from car space to screen space
if (mapToScreen(dRel, -yRel, z_on_path, &screen_pos)) { // yRel is negated as in update_leads
// Basic drawing: Draw a small circle for the point
painter.setBrush(QColor(255, 0, 0, 200)); // Cyan color for live tracks
painter.drawEllipse(screen_pos, 10, 10); // Draw a small circle of radius 5
// Prepare text to display
QString infoText = QString("ID: %1\nd: %2 m\ny: %3 m\ndV: %4 m/s\nyV: %5 m/s")
.arg(point.getTrackId())
.arg(dRel, 0, 'f', 2)
.arg(yRel, 0, 'f', 2)
.arg(vRel, 0, 'f', 2)
.arg(yvRel, 0, 'f', 2);
// Draw text near the point
// Adjust text position for better visibility (e.g., slightly offset from the point)
QRectF textRect(screen_pos.x() + 10, screen_pos.y() - 20, 250, 250); // Adjust size as needed
painter.drawText(textRect, Qt::AlignLeft, infoText);
}
}
}
void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data,
const QPointF &vd, const QRect &surface_rect) {
const float speedBuff = 10.;
+4
View File
@@ -15,6 +15,7 @@ private:
bool mapToScreen(float in_x, float in_y, float in_z, QPointF *out);
void mapLineToPolygon(const cereal::XYZTData::Reader &line, float y_off, float z_off,
QPolygonF *pvd, int max_idx, bool allow_invert = true);
void drawLiveTracks(QPainter &painter, const cereal::RadarData::Reader &live_tracks, const cereal::ModelDataV2::Reader &model_data, const QRect &surface_rect);
void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, const QRect &surface_rect);
void update_leads(const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line);
void update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead);
@@ -36,4 +37,7 @@ private:
QPointF lead_vertices[2] = {};
Eigen::Matrix3f car_space_transform = Eigen::Matrix3f::Zero();
QRectF clip_region;
QVector<QColor> dp_rainbow_color_list;
qreal dp_rainbow_rotation = 0;
bool dp_rainbow_init = false;
};
+37 -3
View File
@@ -39,16 +39,48 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent) {
QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
}
void OnroadWindow::updateDpIndicatorSideState(bool blinker_state, bool bsm_state, bool &show, bool &show_prev, int &count, QColor &color) {
if (!blinker_state && !bsm_state) {
show = false;
count = 0;
} else {
count += 1;
}
if (bsm_state && blinker_state) {
show = count % DP_INDICATOR_BLINK_RATE_FAST == 0? !show : show;
color = DP_INDICATOR_COLOR_BSM;
} else if (blinker_state) {
show = count % DP_INDICATOR_BLINK_RATE_STD == 0? !show : show;
color = DP_INDICATOR_COLOR_BLINKER;
} else if (bsm_state) {
show = true;
color = DP_INDICATOR_COLOR_BSM;
} else {
show = false;
}
}
void OnroadWindow::updateDpIndicatorStates(const UIState &s) {
const auto cs = (*s.sm)["carState"].getCarState();
updateDpIndicatorSideState(cs.getLeftBlinker(), cs.getLeftBlindspot(), dp_indicator_show_left, dp_indicator_show_left_prev, dp_indicator_count_left, dp_indicator_color_left);
updateDpIndicatorSideState(cs.getRightBlinker(), cs.getRightBlindspot(), dp_indicator_show_right, dp_indicator_show_right_prev, dp_indicator_count_right, dp_indicator_color_right);
}
void OnroadWindow::updateState(const UIState &s) {
if (!s.scene.started) {
return;
}
dp_indicator_show_left_prev = dp_indicator_show_left;
dp_indicator_show_right_prev = dp_indicator_show_right;
updateDpIndicatorStates(s);
bool indicator_states_changed = dp_indicator_show_left != dp_indicator_show_left_prev || dp_indicator_show_right != dp_indicator_show_right_prev;
alerts->updateState(s);
nvg->updateState(s);
QColor bgColor = bg_colors[s.status];
if (bg != bgColor) {
QColor bgColor = bg_colors[s.scene.alka_active && s.status == STATUS_DISENGAGED? STATUS_ALKA : s.status];
if (bg != bgColor || indicator_states_changed) {
// repaint border
bg = bgColor;
update();
@@ -61,5 +93,7 @@ void OnroadWindow::offroadTransition(bool offroad) {
void OnroadWindow::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 255));
p.fillRect(rect(), QColor(bg.red(), bg.green(), bg.blue(), 180));
if (dp_indicator_show_left) p.fillRect(QRect(0, 0, width() * 0.2, height()), dp_indicator_color_left);
if (dp_indicator_show_right) p.fillRect(QRect(width() * 0.8, 0, width() * 0.2, height()), dp_indicator_color_right);
}
+20
View File
@@ -1,11 +1,18 @@
#pragma once
#include <QColor>
#include "selfdrive/ui/qt/onroad/alerts.h"
#include "selfdrive/ui/qt/onroad/annotated_camera.h"
class OnroadWindow : public QWidget {
Q_OBJECT
const int DP_INDICATOR_BLINK_RATE_STD = 8;
const int DP_INDICATOR_BLINK_RATE_FAST = 4;
const QColor DP_INDICATOR_COLOR_BLINKER = QColor(0, 0xff, 0, 255);
const QColor DP_INDICATOR_COLOR_BSM = QColor(0xff, 0xff, 0, 255);
public:
OnroadWindow(QWidget* parent = 0);
@@ -16,6 +23,19 @@ private:
QColor bg = bg_colors[STATUS_DISENGAGED];
QHBoxLayout* split;
void updateDpIndicatorSideState(bool blinker_state, bool bsm_state, bool &show, bool &show_prev, int &count, QColor &color);
void updateDpIndicatorStates(const UIState &s);
// left
int dp_indicator_count_left = 0;
QColor dp_indicator_color_left = DP_INDICATOR_COLOR_BLINKER;
bool dp_indicator_show_left = false;
bool dp_indicator_show_left_prev = false;
// right
int dp_indicator_count_right = 0;
QColor dp_indicator_color_right = DP_INDICATOR_COLOR_BLINKER;
bool dp_indicator_show_right = false;
bool dp_indicator_show_right_prev = false;
private slots:
void offroadTransition(bool offroad);
void updateState(const UIState &s);

Some files were not shown because too many files have changed in this diff Show More