mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-06-24 23:42:05 +08:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 459665ca60 | |||
| c3273aaa07 | |||
| 8f1967a74d | |||
| f91dac60cd | |||
| 7ab310e401 | |||
| 53430b4334 | |||
| a0f46a5556 | |||
| 84d7670880 | |||
| c37ea89539 | |||
| 90e8695072 | |||
| 037bf886b8 | |||
| 8203454d57 | |||
| 38878135b8 | |||
| 723ba7aad3 | |||
| 6f50e36afc | |||
| 7f1fbe1bac | |||
| 0e9b423d8b | |||
| 369fd2352a | |||
| 15634c7342 | |||
| 53d2fc5c3b | |||
| 4d91288053 | |||
| 19c4658ecb | |||
| aec4fa3814 | |||
| 559e657ee8 | |||
| fc621376ba | |||
| 1be247b35d | |||
| 70a05dfa88 | |||
| 0a214f01fd | |||
| 3e6ac60b93 | |||
| ad7ea05d76 | |||
| 69fb27bac2 | |||
| f91696153b | |||
| 8a0a8ff4d0 | |||
| e8caa20aee | |||
| e8edc24252 | |||
| 987efffacb | |||
| 7227c90e7b | |||
| e03c5f3857 | |||
| 49c5184e73 | |||
| c957d58186 | |||
| e99675400e | |||
| 1b06d3975d | |||
| c0119c6bf7 | |||
| aa0a5e440b | |||
| 9b588590f7 | |||
| f5795947b3 | |||
| 87972fb990 | |||
| 19d643610a | |||
| f1e185736c | |||
| 201f65419d | |||
| 75213dfa8c | |||
| 8b747872f1 | |||
| ab8014e3df | |||
| e8a2e5098f | |||
| 7596ebfae0 | |||
| a9aa186b8b | |||
| 5ad4cd34f5 | |||
| 757e2bd9cf | |||
| 1c6a0b8b61 | |||
| 7aa1a5f91e | |||
| 13f67f331f | |||
| ed18bb8ecd | |||
| e826a31e7d | |||
| f9a9dcc912 | |||
| a6e9c34a02 | |||
| 6871bfdb99 | |||
| d1899e3403 | |||
| df4614e585 | |||
| 8c23fdde2b | |||
| f08abf48c5 | |||
| 396576cd01 | |||
| 4e11fbde11 | |||
| 07c7f009cd | |||
| bd09c5be36 | |||
| 39643f9ea8 | |||
| 81727ec2fc | |||
| 139b80e2ef | |||
| 0545016dde | |||
| f4f1beb861 | |||
| c7b91221ff | |||
| e2cc9945df | |||
| 461109b4f6 | |||
| f3b6fa9bd7 | |||
| 2fa36a0088 | |||
| f31977e79a | |||
| 0151a26082 | |||
| 86f1618f75 | |||
| 4326d334cb | |||
| 888d0788f6 | |||
| ef4702c077 | |||
| c11b51a9cc | |||
| 28e1822ad1 | |||
| e9fdbaa779 | |||
| a3f1354f27 |
Vendored
+3
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+16
-1
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
Vendored
+2
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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},
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
@@ -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()
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
|
||||
@@ -9,3 +9,5 @@ class ALTERNATIVE_EXPERIENCE:
|
||||
DISABLE_STOCK_AEB = 2
|
||||
RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX = 8
|
||||
ALLOW_AEB = 16
|
||||
|
||||
ALKA = 2 ** 10
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 |
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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):
|
||||
|
||||
@@ -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
|
||||
@@ -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'])
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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'):
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ¶m_name, const QString ¶m_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 ¶m) {
|
||||
toggles[param.toStdString()]->showDescription();
|
||||
}
|
||||
@@ -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 ¶m);
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 ¶m) {
|
||||
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 ¶m) {
|
||||
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"(
|
||||
* {
|
||||
|
||||
@@ -92,6 +92,7 @@ private:
|
||||
QLabel *onroadLbl;
|
||||
LabelControl *versionLbl;
|
||||
ButtonControl *installBtn;
|
||||
ButtonControl *onOffRoadBtn;
|
||||
ButtonControl *downloadBtn;
|
||||
ButtonControl *targetBranchBtn;
|
||||
|
||||
|
||||
@@ -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")));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -19,7 +19,7 @@ void drawIcon(QPainter &p, const QPoint ¢er, 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);
|
||||
}
|
||||
|
||||
@@ -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.;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user