Compare commits

179 Commits

Author SHA1 Message Date
Your Name
f6831761bf wip 2024-03-09 10:33:28 -06:00
concordia
cd3bd61918 wip 2024-03-02 00:11:55 -06:00
concordia
649bb4076c wip 2024-03-01 23:48:20 -06:00
concordia
6e5b300e5e wip 2024-03-01 23:34:55 -06:00
concordia
105898ba7b wip 2024-03-01 23:17:36 -06:00
concordia
270d28507a wip 2024-03-01 23:15:22 -06:00
concordia
b1e8376fe8 wip 2024-03-01 23:13:38 -06:00
concordia
5d10d44ff5 wip 2024-03-01 23:12:57 -06:00
concordia
d4e73ccb3a wip 2024-03-01 23:10:14 -06:00
concordia
532f551963 wip 2024-03-01 23:08:36 -06:00
concordia
e5b1e616db wip 2024-03-01 23:07:04 -06:00
concordia
8544b57780 wip 2024-03-01 23:06:19 -06:00
concordia
1d507de8a7 wip 2024-03-01 22:54:24 -06:00
concordia
7bc4bfc343 wip 2024-03-01 22:49:33 -06:00
concordia
b1b4bcb29f wip 2024-03-01 22:47:06 -06:00
concordia
5081a5d670 wip 2024-03-01 22:44:45 -06:00
concordia
9de0c5484c wip 2024-03-01 22:39:35 -06:00
concordia
b5e0895bef wip 2024-03-01 21:53:47 -06:00
Your Name
1d6a60338a wip 2024-02-17 19:19:37 -06:00
Your Name
340961b3d6 wip 2024-02-17 19:08:08 -06:00
Your Name
853a46046c wip 2024-02-17 19:04:53 -06:00
Your Name
0a4599280b wip 2024-02-17 19:02:33 -06:00
Your Name
cf322c6e67 wip 2024-02-17 18:36:29 -06:00
Your Name
a75f80af27 wip 2024-02-17 14:47:54 -06:00
Your Name
f2a6706509 wip 2024-02-17 14:46:18 -06:00
Your Name
b21008c466 wip 2024-02-17 14:43:59 -06:00
Your Name
a72cbe9191 wip 2024-02-17 14:42:15 -06:00
Your Name
faab849d39 wip 2024-02-17 14:35:47 -06:00
Your Name
9b036682a3 wip 2024-02-17 14:34:10 -06:00
Your Name
3d35093c86 wip 2024-02-17 14:17:37 -06:00
Your Name
f6a44879ff wip 2024-02-17 14:15:00 -06:00
Your Name
a24e5e0643 wip 2024-02-17 14:13:06 -06:00
Your Name
361365000b wip 2024-02-17 13:54:02 -06:00
Your Name
4e523dc500 wip 2024-02-17 13:48:06 -06:00
Your Name
5f51f7e3d7 wip 2024-02-17 13:46:44 -06:00
Your Name
89b2a14d05 wip 2024-02-17 13:42:55 -06:00
Your Name
24aa5295c0 wip 2024-02-17 13:40:46 -06:00
Your Name
f78628f919 wip 2024-02-17 13:39:07 -06:00
Your Name
f50ad15347 wip 2024-02-17 13:35:47 -06:00
Your Name
8fe60169a2 wip 2024-02-17 13:29:10 -06:00
Your Name
863e79690f wip 2024-02-16 20:15:13 -06:00
Your Name
c20342dfd0 wip 2024-02-16 20:10:55 -06:00
Your Name
38b37d25b2 wip 2024-02-16 20:04:02 -06:00
Your Name
3bc5e8b792 wip 2024-02-16 20:01:24 -06:00
Your Name
c5af834621 wip 2024-02-16 19:58:28 -06:00
Your Name
090af40dc1 wip 2024-02-16 19:56:36 -06:00
Your Name
28ff6d11e2 wip 2024-02-16 19:54:22 -06:00
Your Name
0f492814f4 wip 2024-02-16 19:43:06 -06:00
Your Name
3ba35357c7 wip 2024-02-16 19:38:14 -06:00
Your Name
5c2c44b780 wip 2024-02-16 19:31:03 -06:00
Your Name
19673dc652 wip 2024-02-16 19:18:18 -06:00
Your Name
915d8a058a wip 2024-02-16 19:13:47 -06:00
Your Name
045fc2d62b wip 2024-02-16 19:09:28 -06:00
Your Name
189f0da0dc wip 2024-02-16 19:04:40 -06:00
Your Name
eefd31d5b5 wip 2024-02-16 18:53:44 -06:00
Your Name
d0ccab85ec wip 2024-02-16 18:44:54 -06:00
Your Name
5e9d7d4776 wip 2024-02-16 18:43:17 -06:00
Your Name
7b92ead535 wip 2024-02-16 18:08:51 -06:00
Your Name
e7657d5007 wip 2024-02-16 18:07:28 -06:00
Your Name
915c7554e3 wip 2024-02-16 18:04:40 -06:00
Your Name
a0af73ebdf wip 2024-02-16 18:03:12 -06:00
Your Name
cca2b14612 wip 2024-02-16 17:57:20 -06:00
Your Name
2b1144ad13 wip 2024-02-16 17:54:56 -06:00
Your Name
53acbd82d0 wip 2024-02-16 17:45:30 -06:00
Your Name
0d8e764b7b wip 2024-02-16 17:44:38 -06:00
Your Name
be57e96588 wip 2024-02-16 17:43:02 -06:00
Your Name
74632d4f51 wip 2024-02-16 17:40:12 -06:00
Your Name
f5f8d0ead2 wip 2024-02-16 17:34:24 -06:00
Your Name
274fb8ca0d wip 2024-02-16 17:30:49 -06:00
Your Name
886ca6bde8 wip 2024-02-16 17:27:58 -06:00
Your Name
c93bda5316 wip 2024-02-15 23:42:27 -06:00
Your Name
8c60f0956b wip 2024-02-15 23:36:30 -06:00
Your Name
a5adf74b23 wip 2024-02-15 21:16:05 -06:00
Your Name
16cba7fca5 wip 2024-02-14 06:27:34 -06:00
Your Name
d561d2a1cd wip 2024-02-14 06:15:47 -06:00
Your Name
19bdca892a wip 2024-02-14 05:42:17 -06:00
Your Name
483f5c0297 wip 2024-02-14 01:59:13 -06:00
Your Name
f2559a0f93 wip 2024-02-14 01:15:34 -06:00
Your Name
a6fd1bfe03 wip 2024-02-14 00:42:24 -06:00
Your Name
0c5da80a05 wip 2024-02-13 20:37:35 -06:00
Your Name
53e8eccf4c wip 2024-02-13 20:24:53 -06:00
Your Name
cb4cb801c9 wip 2024-02-13 20:06:59 -06:00
Your Name
c43a5dac85 wip 2024-02-13 19:53:19 -06:00
Your Name
ae865c80de wip 2024-02-13 19:52:13 -06:00
Your Name
9e994a28af wip 2024-02-13 19:52:00 -06:00
Your Name
b0f82bfb4f wip 2024-02-13 19:41:59 -06:00
Your Name
2f94ff2586 wip 2024-02-13 19:41:14 -06:00
Your Name
f9160f89da wip 2024-02-13 19:37:55 -06:00
Your Name
f08ab420a5 wip 2024-02-13 19:28:39 -06:00
Your Name
638378439e wip 2024-02-13 19:19:51 -06:00
Your Name
f61dfc3321 wip 2024-02-13 19:10:00 -06:00
Your Name
4784743817 wip 2024-02-13 19:06:17 -06:00
Your Name
64acac5f13 wip 2024-02-13 19:02:26 -06:00
Your Name
d1c940a7fa wip 2024-02-13 19:01:56 -06:00
Your Name
9365afc690 wip 2024-02-13 18:59:07 -06:00
Your Name
08d77d9350 wip 2024-02-13 18:56:17 -06:00
Your Name
d766a7d1b2 wip 2024-02-13 18:53:52 -06:00
Your Name
b6ed5a2e25 wip 2024-02-13 18:51:46 -06:00
Your Name
7956d2f3d8 wip 2024-02-13 18:51:30 -06:00
Your Name
0076fc1bd7 wip 2024-02-13 18:27:16 -06:00
Your Name
4ee2476dae wip 2024-02-13 17:44:25 -06:00
Your Name
b09e59e104 wip 2024-02-13 17:31:58 -06:00
Your Name
8b60a60703 wip 2024-02-13 16:59:27 -06:00
Your Name
3682b25f81 wip 2024-02-13 16:54:29 -06:00
Your Name
154eee2f08 wip 2024-02-13 16:45:09 -06:00
Your Name
b3d781ef26 wip 2024-02-13 16:44:19 -06:00
Your Name
9eee0333ec wip 2024-02-13 14:42:24 -06:00
Your Name
dc1ee37053 wip 2024-02-13 04:20:54 -06:00
Your Name
05c45829f8 wip 2024-02-13 04:13:20 -06:00
Your Name
808fc67534 wip 2024-02-13 04:10:41 -06:00
Your Name
ee9802a513 wip 2024-02-13 04:08:13 -06:00
Your Name
dc9dfa03e9 wip 2024-02-13 04:05:51 -06:00
Your Name
17bc9cf1f3 wip 2024-02-13 04:03:52 -06:00
Your Name
b8aecdb78c wip 2024-02-13 04:02:33 -06:00
Your Name
e2ebe7048c wip 2024-02-13 04:00:11 -06:00
Your Name
794b1d8bdd wip 2024-02-13 03:51:28 -06:00
Your Name
fccc1221f6 wip 2024-02-13 03:29:32 -06:00
Your Name
4bbc34c365 wip 2024-02-13 03:27:24 -06:00
Your Name
589c5dfc82 wip 2024-02-13 03:17:31 -06:00
Your Name
ff80bfe06a wip 2024-02-13 03:13:35 -06:00
Your Name
f1c84854ed wip 2024-02-13 03:11:19 -06:00
Your Name
d6404224fa wip 2024-02-13 03:05:30 -06:00
Your Name
9fd53b7ec1 wip 2024-02-13 03:03:26 -06:00
Your Name
a82120e55e wip 2024-02-13 03:00:54 -06:00
Your Name
99006f0f6c wip 2024-02-13 02:55:03 -06:00
Your Name
0f5573f493 wip 2024-02-13 02:51:20 -06:00
Your Name
73315b7009 wip 2024-02-13 02:02:10 -06:00
Your Name
821d9c5807 wip 2024-02-13 01:59:03 -06:00
Your Name
ebad1a29b2 wip 2024-02-13 01:47:19 -06:00
Your Name
00bef387df wip 2024-02-13 01:28:39 -06:00
Your Name
13db24e327 wip 2024-02-13 01:26:47 -06:00
Your Name
7f7d487723 wip 2024-02-13 01:26:25 -06:00
Your Name
50dd50f0a7 wip 2024-02-13 01:21:51 -06:00
Your Name
3fc6fa0b52 wip 2024-02-13 00:49:09 -06:00
Your Name
412553e741 wip 2024-02-13 00:46:23 -06:00
Your Name
0f7641ea39 wip 2024-02-13 00:35:55 -06:00
Your Name
b98b001ceb wip 2024-02-13 00:25:34 -06:00
Your Name
af0fc9daec wip 2024-02-13 00:22:00 -06:00
Your Name
e10db32b69 wip 2024-02-12 23:34:32 -06:00
Your Name
e034f72f3d wip 2024-02-12 23:10:07 -06:00
Your Name
2803846987 wip 2024-02-12 23:06:10 -06:00
Your Name
9634f1b2e6 wip 2024-02-12 22:48:35 -06:00
Your Name
8f817dffd6 wip 2024-02-12 22:34:07 -06:00
concordia
a380603637 wip 2024-02-12 18:11:57 -06:00
Your Name
9e67df39d3 wip 2024-02-12 14:41:19 -06:00
Your Name
2236323030 wip 2024-02-12 14:36:04 -06:00
concordia
829b75411b wip 2024-02-10 14:46:06 -06:00
Your Name
da6553c24d wip 2024-02-09 17:24:49 -06:00
Your Name
4948aecc6e wip 2024-02-09 16:29:56 -06:00
Your Name
be628a964e wip 2024-02-09 10:13:44 -06:00
Your Name
52c28f6710 wip 2024-02-08 14:55:48 -06:00
Your Name
8d971f0671 wip 2024-02-08 14:50:44 -06:00
Your Name
88785baa11 wip 2024-02-08 14:36:45 -06:00
Your Name
b5fd185742 wip 2024-02-08 14:28:00 -06:00
Your Name
ad5e06b0ad wip 2024-02-08 14:08:25 -06:00
Your Name
96aa4c3e93 wip 2024-02-08 13:03:21 -06:00
Your Name
5307871310 wip 2024-02-08 12:56:22 -06:00
concordia
90b8e9f072 wip 2024-02-08 12:46:30 -06:00
Your Name
7853d218a3 wip 2024-02-06 01:08:15 -06:00
Your Name
f931d2982e wip 2024-02-06 00:13:57 -06:00
concordia
38a3903009 wip 2024-02-06 00:04:33 -06:00
Your Name
2964ede91a wip 2024-02-05 16:25:47 -06:00
Your Name
7949e90b75 wip 2024-02-05 03:05:34 -06:00
Your Name
a7e39e1ccf wip 2024-02-05 02:59:27 -06:00
Your Name
26af5117de wip 2024-02-05 02:59:08 -06:00
Your Name
a8a7c6a20c wip 2024-02-05 02:45:55 -06:00
Your Name
7fb4b58320 wip 2024-02-05 02:36:26 -06:00
Your Name
377c6e3021 wip 2024-02-05 02:27:20 -06:00
Your Name
9213866846 wip 2024-02-05 02:21:21 -06:00
Your Name
67178ebc4b wip 2024-02-05 02:05:53 -06:00
Your Name
746d9fc631 wip 2024-02-05 01:50:34 -06:00
Your Name
e55aa65d18 wip 2024-02-05 01:43:52 -06:00
Your Name
a4f130ba44 wip 2024-02-05 01:22:54 -06:00
Your Name
a81f6285c1 wip 2024-02-05 01:15:52 -06:00
Your Name
072ed7e50f wip 2024-02-05 00:47:17 -06:00
Your Name
425594dd0c wip 2024-02-05 00:25:42 -06:00
Your Name
b7cb5c8893 wip 2024-02-04 23:36:57 -06:00
Your Name
705a30835a wip 2024-02-04 23:34:58 -06:00
Your Name
76f2507503 iwip 2024-02-04 22:15:38 -06:00
151 changed files with 18507 additions and 803 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.onnx filter=lfs diff=lfs merge=lfs -text

View File

@@ -1,14 +1,33 @@
This is oscarpilot. It is a mod of frogpilot. This is oscarpilot. It is a mod of frogpilot.
- increased driver monitor timeouts to 30 sec warn / 60 second fatal This is for private use by Brian Hanson. If you come across this you are not intended to use it. Unathorized use of this software
- changed some default settings, notably farmville model default is at your own risk.
Released under the MIT license. Some parts of the software are released under other licenses as specified.
Any user of this software shall indemnify and hold harmless Comma.ai, Inc, all developers of openpilot or its forks, and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys fees and costs) which arise out of, relate to or result from any use of this software by user.
THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT. YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS. NO WARRANTY EXPRESSED OR IMPLIED.
Changes from frogpilot 2024-01-15:
- changed some default settings, notably defaults: farmville model, disabled turn intentions
- disabled lane change assist - disabled lane change assist
- disabled activation / deactivation sounds in custom sound theme - disabled activation / deactivation sounds in custom sound theme
- small edits to theme
This is for private use by Brian Hanson. If you come across this you are not intended to use it. Unathorized use of this software - increased driver monitor timeouts to 10 sec warn / 60 second terminate.
is at your own risk. To the greatest extent possible, by using this software, you agree to free Brian Hanson, frogpilot, and openpilot - Justification:
of any liability by your unauthorized choice to use this software, and you use at your own risk. - Many people will turn off the moinitor altogether if given the choice. There has to be a good middle ground.
- The monitor's primary responsibility should be to detect when the driver has fallen asleep perhaps, which shouldn't
ever happen, but if it does will certainly result in the death of the driver and possibly others. This is critical
enough that the car should make very lound angry noises after a threshold which only should happen if the driver
has flat out stopped paying attention to driving. The shreeking and continuing to drive is preferable to a car
stopping in traffic with its hazards off, as a result the car should keep driving for a reasonable amount of time
that the driver would be reawoken.
Goals:
- Conditional experimental mode: override speed on stock ACC, set to experimental mode, revert to last speed when recovered. Last speed updated to car speed limit on change.
![openpilot on the comma 3X](https://i.imgur.com/6l2qbf5.png) ![openpilot on the comma 3X](https://i.imgur.com/6l2qbf5.png)

1
commit_and_push Normal file
View File

@@ -0,0 +1 @@
git add -A; git commit -m "wip"; bash push_private

View File

@@ -244,6 +244,10 @@ std::unordered_map<std::string, uint32_t> keys = {
{"Compass", PERSISTENT}, {"Compass", PERSISTENT},
{"ConditionalExperimental", PERSISTENT}, {"ConditionalExperimental", PERSISTENT},
{"CurrentRandomEvent", PERSISTENT}, {"CurrentRandomEvent", PERSISTENT},
{"CSLCAvailable", PERSISTENT},
{"CSLCEnabled", PERSISTENT},
{"CSLCSetSpeed", PERSISTENT},
{"CSLCTempSpeed", PERSISTENT},
{"CurveSensitivity", PERSISTENT}, {"CurveSensitivity", PERSISTENT},
{"CustomColors", PERSISTENT}, {"CustomColors", PERSISTENT},
{"CustomIcons", PERSISTENT}, {"CustomIcons", PERSISTENT},
@@ -327,6 +331,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"ReverseCruise", PERSISTENT}, {"ReverseCruise", PERSISTENT},
{"RoadEdgesWidth", PERSISTENT}, {"RoadEdgesWidth", PERSISTENT},
{"RoadName", PERSISTENT}, {"RoadName", PERSISTENT},
{"oscar_debug", PERSISTENT},
{"RoadNameUI", PERSISTENT}, {"RoadNameUI", PERSISTENT},
{"RotatingWheel", PERSISTENT}, {"RotatingWheel", PERSISTENT},
{"SchedulePending", PERSISTENT}, {"SchedulePending", PERSISTENT},

View File

@@ -2,7 +2,7 @@ import os
import shutil import shutil
import uuid import uuid
from typing import Optional from typing import Optiona
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.system.hardware.hw import Paths from openpilot.system.hardware.hw import Paths

33
common/watcher.py Normal file
View File

@@ -0,0 +1,33 @@
import socket
import json
# Initialize the socket without connecting
watcher_sock = None
class Watcher:
@staticmethod
def ensure_socket_connected():
global watcher_sock
if watcher_sock is None or watcher_sock.fileno() == -1: # Checks if socket is not initialized or closed
try:
# Attempt to initialize and connect the socket
watcher_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
watcher_sock.connect("/tmp/oscar_watcher.sock")
except socket.error:
# If connection fails, set sock to None and do nothing
watcher_sock = None
@staticmethod
def log_watch(var_name, message):
global watcher_sock
Watcher.ensure_socket_connected() # Ensure the socket is connected before attempting to log
if watcher_sock: # Proceed only if sock is not None (i.e., is connected)
message_json = json.dumps([var_name, message])
try:
watcher_sock.sendall(message_json.encode('utf-8') + b'\n')
except socket.error:
# Handle potential error in sending (e.g., if connection was lost)
watcher_sock = None # Ensure that the next attempt will try to reconnect
finally:
watcher_sock.close() # Close the current socket to clean up resources
watcher_sock = None # Reset sock to ensure reconnection attempt on next call

2
debug.sh Normal file
View File

@@ -0,0 +1,2 @@
cd selfdrive/ui
gdb ./ui

2457
hyundai_can_full.dbc Normal file

File diff suppressed because it is too large Load Diff

719
hyundai_canfd.dbc Normal file
View File

@@ -0,0 +1,719 @@
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_: XXX CAMERA FRONT_RADAR ADRV APRK
BO_ 53 ACCELERATOR: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ GEAR : 192|3@1+ (1,0) [0|7] "" XXX
SG_ ACCELERATOR_PEDAL : 40|8@1+ (1,0) [0|255] "" XXX
BO_ 64 GEAR_ALT: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ GEAR : 32|3@1+ (1,0) [0|7] "" XXX
BO_ 69 GEAR: 24 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ GEAR : 44|3@1+ (1,0) [0|7] "" XXX
BO_ 80 LKAS: 16 XXX
SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX
SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX
SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 27|2@1+ (1,0) [0|255] "" XXX
SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX
SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX
SG_ FCA_SYSWARN : 40|1@0+ (1,0) [0|1] "" XXX
BO_ 81 ADRV_0x51: 32 ADRV
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
BO_ 96 ESP_STATUS: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ TRACTION_AND_STABILITY_CONTROL : 42|3@1+ (1,0) [0|63] "" XXX
SG_ BRAKE_PRESSURE : 128|10@1+ (1,0) [0|65535] "" XXX
SG_ BRAKE_PRESSED : 148|1@1+ (1,0) [0|3] "" XXX
BO_ 101 BRAKE: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ BRAKE_POSITION : 40|16@1- (1,0) [0|65535] "" XXX
SG_ BRAKE_PRESSED : 57|1@1+ (1,0) [0|3] "" XXX
BO_ 112 GEAR_ALT_2: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ GEAR : 60|3@1+ (1,0) [0|7] "" XXX
BO_ 160 WHEEL_SPEEDS: 24 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ MOVING_FORWARD : 56|1@0+ (1,0) [0|1] "" XXX
SG_ MOVING_BACKWARD : 57|1@0+ (1,0) [0|1] "" XXX
SG_ MOVING_FORWARD2 : 58|1@0+ (1,0) [0|1] "" XXX
SG_ MOVING_BACKWARD2 : 59|1@0+ (1,0) [0|1] "" XXX
SG_ WHEEL_SPEED_1 : 64|16@1+ (0.03125,0) [0|65535] "kph" XXX
SG_ WHEEL_SPEED_2 : 80|16@1+ (0.03125,0) [0|65535] "kph" XXX
SG_ WHEEL_SPEED_3 : 96|16@1+ (0.03125,0) [0|65535] "kph" XXX
SG_ WHEEL_SPEED_4 : 112|16@1+ (0.03125,0) [0|65535] "kph" XXX
BO_ 234 MDPS: 24 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ LKA_ACTIVE : 48|1@0+ (1,0) [0|16777215] "" XXX
SG_ LKA_FAULT : 54|1@0+ (1,0) [0|1] "" XXX
SG_ STEERING_OUT_TORQUE : 64|12@1+ (0.1,-204.8) [0|65535] "" XXX
SG_ STEERING_COL_TORQUE : 80|13@1+ (1,-4095) [0|4095] "" XXX
SG_ STEERING_ANGLE : 96|16@1- (-0.1,0) [0|255] "deg" XXX
SG_ STEERING_ANGLE_2 : 128|16@1- (-0.1,0) [0|65535] "deg" XXX
BO_ 256 ACCELERATOR_BRAKE_ALT: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ BRAKE_PRESSED : 32|1@1+ (1,0) [0|1] "" XXX
SG_ ACCELERATOR_PEDAL_PRESSED : 176|1@1+ (1,0) [0|1] "" XXX
BO_ 261 ACCELERATOR_ALT: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ ACCELERATOR_PEDAL : 103|10@1+ (0.25,0) [0|1022] "" XXX
BO_ 272 LKAS_ALT: 32 XXX
SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX
SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX
SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 27|2@1+ (1,0) [0|255] "" XXX
SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX
SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX
SG_ FCA_SYSWARN : 40|1@0+ (1,0) [0|1] "" XXX
BO_ 293 STEERING_SENSORS: 16 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ STEERING_RATE : 40|8@1+ (4,0) [0|1016] "deg/s" XXX
SG_ STEERING_ANGLE : 24|16@1- (-0.1,0) [0|255] "deg" XXX
BO_ 298 LFA: 16 ADRV
SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX
SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX
SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 27|2@1+ (1,0) [0|255] "" XXX
SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX
SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_3 : 111|8@0+ (1,0) [0|255] "" XXX
BO_ 304 GEAR_SHIFTER: 16 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ PARK_BUTTON : 32|2@1+ (1,0) [0|3] "" XXX
SG_ GEAR : 64|3@1+ (1,0) [0|7] "" XXX
SG_ KNOB_POSITION : 40|3@1+ (1,0) [0|3] "" XXX
BO_ 357 SPAS1: 24 APRK
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|0] "" XXX
SG_ NEW_SIGNAL_1 : 96|16@1- (0.1,0) [0|0] "" XXX
SG_ NEW_SIGNAL_2 : 90|3@1+ (1,0) [0|0] "" XXX
BO_ 362 SPAS2: 32 APRK
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|0] "" XXX
SG_ BLINKER_CONTROL : 133|3@1+ (1,0) [0|0] "" XXX
BO_ 373 TCS: 24 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 80|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_2 : 74|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_3 : 76|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_4 : 24|7@1+ (1,0) [0|127] "" XXX
SG_ aBasis : 32|11@1+ (0.01,-10.23) [0|7] "m/s^2" XXX
SG_ NEW_SIGNAL_5 : 72|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_6 : 128|4@1+ (1,0) [0|15] "" XXX
SG_ NEW_SIGNAL_7 : 135|2@0+ (1,0) [0|3] "" XXX
SG_ PROBABLY_EQUIP : 136|2@1+ (1,0) [0|3] "" XXX
SG_ AEB_EQUIP_MAYBE : 96|1@0+ (1,0) [0|1] "" XXX
SG_ EQUIP_MAYBE : 64|1@0+ (1,0) [0|1] "" XXX
SG_ DriverBraking : 81|1@0+ (1,0) [0|1] "" XXX
SG_ DriverBrakingLowSens : 84|1@1+ (1,0) [0|1] "" XXX
SG_ ACC_REQ : 68|1@0+ (1,0) [0|1] "" XXX
SG_ ACCEL_REF_ACC : 48|11@1- (1,0) [0|1023] "" XXX
SG_ ACCEnable : 67|2@0+ (1,0) [0|3] "" XXX
BO_ 352 ADRV_0x160: 16 ADRV
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_FF : 64|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_FC : 72|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_2 : 56|8@1+ (1,0) [0|1] "" XXX
SG_ AEB_SETTING : 24|2@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_9 : 80|8@1+ (1,0) [0|255] "" XXX
BO_ 384 CAM_0x180: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 385 CAM_0x181: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 386 CAM_0x182: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 387 CAM_0x183: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 388 CAM_0x184: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 389 CAM_0x185: 8 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 416 SCC_CONTROL: 32 ADRV
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_1 : 64|2@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_8 : 170|4@1+ (1,0) [0|15] "" XXX
SG_ ZEROS : 215|48@0+ (1,0) [0|281474976710655] "" XXX
SG_ ZEROS_3 : 191|7@0+ (1,0) [0|127] "" XXX
SG_ ZEROS_4 : 183|4@0+ (1,0) [0|63] "" XXX
SG_ ZEROS_6 : 119|16@0+ (1,0) [0|65535] "" XXX
SG_ ZEROS_8 : 95|5@0+ (1,0) [0|31] "" XXX
SG_ NEW_SIGNAL_3 : 109|2@0+ (1,0) [0|1] "" XXX
SG_ SET_ME_TMP_64 : 55|8@0+ (1,0) [0|63] "" XXX
SG_ SET_ME_2 : 105|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_6 : 104|1@0+ (1,0) [0|1] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ ZEROS_9 : 71|5@1+ (1,0) [0|15] "" XXX
SG_ ZEROS_10 : 111|2@0+ (1,0) [0|3] "" XXX
SG_ SET_ME_3 : 45|2@0+ (1,0) [0|3] "" XXX
SG_ ObjValid : 46|1@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_2 : 168|2@1+ (1,0) [0|3] "" XXX
SG_ OBJ_STATUS : 176|3@1+ (1,0) [0|7] "" XXX
SG_ ACC_ObjDist : 24|11@1+ (0.1,0) [0|204.7] "m" XXX
SG_ ZEROS_5 : 77|11@1+ (1,0) [0|2047] "" XXX
SG_ DISTANCE_SETTING : 88|3@1+ (1,0) [0|3] "" XXX
SG_ ZEROS_2 : 207|5@0+ (1,0) [0|63] "" XXX
SG_ CRUISE_STANDSTILL : 76|1@1+ (1,0) [0|1] "" XXX
SG_ aReqRaw : 140|11@1+ (0.01,-10.23) [-10.23|10.24] "m/s^2" XXX
SG_ aReqValue : 128|11@1+ (0.01,-10.23) [-10.23|10.24] "m/s^2" XXX
SG_ ZEROS_7 : 63|8@0+ (1,0) [0|255] "" XXX
SG_ ACCMode : 68|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_12 : 35|9@1+ (0.1,0) [0|255] "" XXX
SG_ JerkLowerLimit : 166|7@0+ (0.1,0) [0|12.7] "m/s^3" XXX
SG_ StopReq : 184|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_15 : 192|11@1+ (0.1,0) [0|204.7] "m" XXX
SG_ VSetDis : 103|8@0+ (1,0) [0|255] "km/h or mph" XXX
SG_ MainMode_ACC : 66|1@1+ (1,0) [0|1] "" XXX
SG_ JerkUpperLimit : 158|7@0+ (0.1,0) [0|0] "" XXX
BO_ 426 CRUISE_BUTTONS_ALT: 16 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 24|4@1+ (1,0) [0|15] "" XXX
SG_ SET_ME_1 : 28|2@1+ (1,0) [0|3] "" XXX
SG_ DISTANCE_UNIT : 30|1@1+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_2 : 31|3@1+ (1,0) [0|7] "" XXX
SG_ ADAPTIVE_CRUISE_MAIN_BTN : 34|1@1+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_3 : 35|1@1+ (1,0) [0|1] "" XXX
SG_ LFA_BTN : 39|1@1+ (1,0) [0|1] "" XXX
SG_ CRUISE_BUTTONS : 36|3@1+ (1,0) [0|4] "" XXX
SG_ NEW_SIGNAL_4 : 40|1@1+ (1,0) [0|1] "" XXX
SG_ NORMAL_CRUISE_MAIN_BTN : 41|1@1+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_5 : 42|2@1+ (1,0) [0|3] "" XXX
SG_ SET_ME_2 : 44|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_6 : 47|1@1+ (1,0) [0|1] "" XXX
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE7 : 56|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE8 : 64|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE9 : 72|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE10 : 80|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE11 : 88|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE12 : 96|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE13 : 104|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE14 : 112|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE15 : 120|8@1+ (1,0) [0|255] "" XXX
BO_ 438 CAM_0x1b6: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 439 CAM_0x1b7: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 440 CAM_0x1b8: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 441 CAM_0x1b9: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 463 CRUISE_BUTTONS: 8 XXX
SG_ _CHECKSUM : 0|8@1+ (1,0) [0|65535] "" XXX
SG_ LKAS_BTN : 23|1@1+ (1,0) [0|1] "" XXX
SG_ SET_ME_1 : 29|1@1+ (1,0) [0|1] "" XXX
SG_ ADAPTIVE_CRUISE_MAIN_BTN : 19|1@1+ (1,0) [0|1] "" XXX
SG_ NORMAL_CRUISE_MAIN_BTN : 21|1@1+ (1,0) [0|1] "" XXX
SG_ COUNTER : 12|4@1+ (1,0) [0|255] "" XXX
SG_ CRUISE_BUTTONS : 16|3@1+ (1,0) [0|3] "" XXX
SG_ RIGHT_PADDLE : 25|1@1+ (1,0) [0|1] "" XXX
SG_ LEFT_PADDLE : 27|1@1+ (1,0) [0|1] "" XXX
BO_ 474 ADRV_0x1da: 32 ADRV
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_22 : 31|8@0+ (1,0) [0|255] "" XXX
SG_ SET_ME_41 : 47|8@0+ (1,0) [0|255] "" XXX
BO_ 480 LFAHDA_CLUSTER: 16 ADRV
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ HDA_ICON : 31|1@1+ (1,0) [0|1] "" XXX
SG_ LFA_ICON : 47|2@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_1 : 32|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_2 : 30|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_3 : 49|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_4 : 24|1@0+ (1,0) [0|1] "" XXX
BO_ 490 ADRV_0x1ea: 32 ADRV
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_1C : 31|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_1 : 32|2@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_2 : 47|2@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_3 : 55|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_4 : 64|6@1+ (1,0) [0|31] "" XXX
SG_ NEW_SIGNAL_5 : 72|2@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_6 : 75|5@1+ (1,0) [0|31] "" XXX
SG_ NEW_SIGNAL_7 : 80|5@1+ (1,0) [0|31] "" XXX
SG_ NEW_SIGNAL_8 : 88|7@1+ (1,0) [0|127] "" XXX
SG_ NEW_SIGNAL_9 : 96|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_10 : 143|5@0+ (1,0) [0|31] "" XXX
SG_ NEW_SIGNAL_11 : 144|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_12 : 152|6@1+ (1,0) [0|63] "" XXX
SG_ NEW_SIGNAL_13 : 160|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_14 : 163|5@1+ (1,0) [0|31] "" XXX
SG_ NEW_SIGNAL_15 : 175|4@0+ (1,0) [0|63] "" XXX
SG_ NEW_SIGNAL_16 : 168|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_17 : 176|2@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_18 : 184|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_19 : 208|3@1+ (1,0) [0|7] "" XXX
SG_ NEW_SIGNAL_20 : 212|1@0+ (1,0) [0|1] "" XXX
SG_ SET_ME_FF : 120|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_TMP_F : 232|5@1+ (1,0) [0|31] "" XXX
SG_ SET_ME_TMP_F_2 : 240|5@1+ (1,0) [0|31] "" XXX
BO_ 507 CAM_0x1fb: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 512 ADRV_0x200: 8 ADRV
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_E1 : 24|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_3A : 32|8@1+ (1,0) [0|255] "" XXX
BO_ 513 RADAR_0x201: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 528 RADAR_0x210: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 529 RADAR_0x211: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 530 RADAR_0x212: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 531 RADAR_0x213: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 532 RADAR_0x214: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 533 RADAR_0x215: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 534 RADAR_0x216: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 535 RADAR_0x217: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 536 RADAR_0x218: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 537 RADAR_0x219: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 538 RADAR_0x21a: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 539 RADAR_0x21b: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 540 RADAR_0x21c: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 541 RADAR_0x21d: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 542 RADAR_0x21e: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 543 RADAR_0x21f: 32 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 576 RADAR_0x240: 16 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 593 RADAR_0x251: 16 FRONT_RADAR
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 674 CAM_0x2a2: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 675 CAM_0x2a3: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 676 CAM_0x2a4: 24 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE4 : 32|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
SG_ LEFT_LANE_LINE : 56|2@1+ (1,0) [0|3] "" XXX
SG_ SET_ME_0 : 58|2@1+ (1,0) [0|3] "" XXX
SG_ RIGHT_LANE_LINE : 60|2@1+ (1,0) [0|3] "" XXX
SG_ SET_ME_0_2 : 62|2@1+ (1,0) [0|3] "" XXX
SG_ BYTE8 : 64|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE9 : 72|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE10 : 80|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE11 : 88|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE12 : 96|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE13 : 104|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE14 : 112|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE15 : 120|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE16 : 128|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE17 : 136|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE18 : 144|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE19 : 152|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE20 : 160|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE21 : 168|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE22 : 176|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE23 : 184|8@1+ (1,0) [0|255] "" XXX
BO_ 699 CAM_0x2bb: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 700 CAM_0x2bc: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 701 CAM_0x2bd: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 702 CAM_0x2be: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
BO_ 736 MANUAL_SPEED_LIMIT_ASSIST: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ MSLA_STATUS : 26|2@1+ (1,0) [0|3] "" XXX
SG_ MSLA_ENABLED : 38|1@1+ (1,0) [0|1] "" XXX
SG_ MAX_SPEED : 55|8@0+ (1,0) [0|255] "" XXX
SG_ MAX_SPEED_COPY : 144|8@1+ (1,0) [0|255] "" XXX
BO_ 837 ADRV_0x345: 8 ADRV
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ SET_ME_15 : 24|8@1+ (1,0) [0|255] "" XXX
BO_ 866 CAM_0x362: 32 CAMERA
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE3 : 24|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE4 : 32|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE5 : 40|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE6 : 48|8@1+ (1,0) [0|255] "" XXX
SG_ LEFT_LANE_LINE : 56|2@1+ (1,0) [0|3] "" XXX
SG_ SET_ME_0 : 58|2@1+ (1,0) [0|3] "" XXX
SG_ RIGHT_LANE_LINE : 60|2@1+ (1,0) [0|3] "" XXX
SG_ SET_ME_0_2 : 62|2@1+ (1,0) [0|3] "" XXX
SG_ BYTE8 : 64|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE9 : 72|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE10 : 80|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE11 : 88|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE12 : 96|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE13 : 104|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE14 : 112|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE15 : 120|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE16 : 128|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE17 : 136|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE18 : 144|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE19 : 152|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE20 : 160|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE21 : 168|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE22 : 176|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE23 : 184|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE24 : 192|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE25 : 200|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE26 : 208|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE27 : 216|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE28 : 224|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE29 : 232|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE30 : 240|8@1+ (1,0) [0|255] "" XXX
SG_ BYTE31 : 248|8@1+ (1,0) [0|255] "" XXX
BO_ 961 BLINKER_STALKS: 8 XXX
SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX
SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|255] "" XXX
SG_ HIGHBEAM_FORWARD : 18|1@0+ (1,0) [0|1] "" XXX
SG_ HIGHBEAM_BACKWARD : 26|1@0+ (1,0) [0|1] "" XXX
SG_ RIGHT_BLINKER : 32|1@0+ (1,0) [0|1] "" XXX
SG_ LEFT_BLINKER : 30|1@0+ (1,0) [0|1] "" XXX
SG_ LIGHT_KNOB_POSITION : 21|2@0+ (1,0) [0|3] "" XXX
BO_ 1041 DOORS_SEATBELTS: 8 XXX
SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|65535] "" XXX
SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX
SG_ DRIVER_DOOR : 24|1@1+ (1,0) [0|1] "" XXX
SG_ PASSENGER_DOOR : 34|1@0+ (1,0) [0|1] "" XXX
SG_ DRIVER_REAR_DOOR : 52|1@0+ (1,0) [0|1] "" XXX
SG_ PASSENGER_REAR_DOOR : 56|1@0+ (1,0) [0|1] "" XXX
SG_ DRIVER_SEATBELT : 42|1@0+ (1,0) [0|1] "" XXX
SG_ PASSENGER_SEATBELT : 36|1@0+ (1,0) [0|1] "" XXX
BO_ 1043 BLINKERS: 8 XXX
SG_ LEFT_STALK : 8|1@0+ (1,0) [0|1] "" XXX
SG_ RIGHT_STALK : 10|1@0+ (1,0) [0|1] "" XXX
SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX
SG_ LEFT_LAMP : 20|1@0+ (1,0) [0|1] "" XXX
SG_ RIGHT_LAMP : 22|1@0+ (1,0) [0|1] "" XXX
SG_ LEFT_LAMP_ALT : 59|1@0+ (1,0) [0|1] "" XXX
SG_ RIGHT_LAMP_ALT : 61|1@0+ (1,0) [0|1] "" XXX
SG_ USE_ALT_LAMP : 62|1@0+ (1,0) [0|1] "" XXX
BO_ 1240 CLUSTER_INFO: 8 XXX
SG_ DISTANCE_UNIT : 0|1@1+ (1,0) [0|1] "" XXX
BO_ 442 BLINDSPOTS_REAR_CORNERS: 24 XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_2 : 96|1@0+ (1,0) [0|1] "" XXX
SG_ COLLISION_AVOIDANCE_ACTIVE : 68|1@0+ (1,0) [0|1] "" XXX
SG_ LEFT_MB : 30|1@0+ (1,0) [0|3] "" XXX
SG_ LEFT_BLOCKED : 24|1@0+ (1,0) [0|1] "" XXX
SG_ MORE_LEFT_PROB : 32|1@1+ (1,0) [0|3] "" XXX
SG_ FL_INDICATOR : 46|6@0+ (1,0) [0|1] "" XXX
SG_ FR_INDICATOR : 54|6@0+ (1,0) [0|63] "" XXX
SG_ RIGHT_BLOCKED : 64|1@0+ (1,0) [0|1] "" XXX
BO_ 874 BLINDSPOTS_FRONT_CORNER_2: 16 XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
BO_ 485 BLINDSPOTS_FRONT_CORNER_1: 16 XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ NEW_SIGNAL_1 : 108|2@0+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_2 : 96|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_3 : 88|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_4 : 80|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_5 : 31|2@0+ (1,0) [0|3] "" XXX
SG_ REVERSING : 24|1@0+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_7 : 32|2@1+ (1,0) [0|3] "" XXX
SG_ NEW_SIGNAL_8 : 47|8@0+ (1,0) [0|255] "" XXX
SG_ NEW_SIGNAL_9 : 55|8@0+ (1,0) [0|255] "" XXX
BO_ 506 CLUSTER_SPEED_LIMIT: 32 XXX
SG_ SPEED_LIMIT_3 : 119|8@0+ (1,0) [0|255] "" XXX
SG_ SPEED_LIMIT_2 : 47|7@0+ (1,0) [0|255] "" XXX
SG_ SPEED_LIMIT_1 : 39|7@0+ (1,0) [0|255] "" XXX
SG_ SPEED_CHANGE_BLINKING : 129|1@1+ (1,0) [0|3] "" XXX
SG_ CHIME_2 : 122|2@1+ (1,0) [0|7] "" XXX
SG_ CHIME_1 : 133|1@0+ (1,0) [0|1] "" XXX
SG_ ARROW_DOWN : 120|1@0+ (1,0) [0|1] "" XXX
SG_ ARROW_UP : 121|1@0+ (1,0) [0|1] "" XXX
SG_ SECONDARY_LIMIT_1 : 79|8@0+ (1,0) [0|127] "" XXX
SG_ SECONDARY_LIMIT_2 : 103|8@0+ (1,0) [0|127] "" XXX
SG_ SCHOOL_ZONE : 155|1@0+ (1,0) [0|1] "" XXX
BO_ 1144 DRIVE_MODE: 8 XXX
SG_ DRIVE_MODE : 0|16@1+ (1,-61611) [0|61611] "" XXX
SG_ DRIVE_MODE2 : 28|3@1+ (1,0) [1|3] "" XXX
BO_ 1151 HVAC_TOUCH_BUTTONS: 8 XXX
SG_ AUTO_BUTTON : 8|1@0+ (1,0) [0|1] "" XXX
SG_ SYNC_BUTTON : 12|1@0+ (1,0) [0|1] "" XXX
SG_ FR_DEFROST_BUTTON : 20|1@0+ (1,0) [0|1] "" XXX
SG_ RR_DEFROST_BUTTON : 22|1@0+ (1,0) [0|1] "" XXX
SG_ FAN_SPEED_UP_BUTTON : 24|1@0+ (1,0) [0|1] "" XXX
SG_ FAN_SPEED_DOWN_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX
SG_ AIR_DIRECTION_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX
SG_ AC_BUTTON : 40|1@0+ (1,0) [0|1] "" XXX
SG_ DRIVER_ONLY_BUTTON : 44|1@0+ (1,0) [0|1] "" XXX
SG_ RECIRC_BUTTON : 48|1@0+ (1,0) [0|1] "" XXX
SG_ HEAT_BUTTON : 52|1@0+ (1,0) [0|1] "" XXX
BO_ 1259 LOCAL_TIME2: 8 XXX
SG_ HOURS : 15|5@0+ (1,0) [0|31] "" XXX
SG_ MINUTES : 21|6@0+ (1,0) [0|63] "" XXX
SG_ SECONDS : 24|6@1+ (1,0) [0|63] "" XXX
SG_ NEW_SIGNAL_3 : 39|1@0+ (1,0) [0|1] "" XXX
BO_ 1264 LOCAL_TIME: 8 XXX
SG_ HOURS : 12|5@0+ (1,0) [0|31] "" XXX
SG_ MINUTES : 21|6@0+ (1,0) [0|63] "" XXX
SG_ SECONDS : 31|8@0+ (1,0) [0|59] "" XXX
CM_ 272 "Alternative LKAS message, used on cars such as 2023 Ioniq 6, 2nd gen Kona. Matches LKAS except size is 32 bytes";
CM_ 676 "Contains signals with detailed lane line information. Used by ADAS ECU on HDA 2 vehicles to operate LFA.";
CM_ 866 "Contains signals with detailed lane line information. Used by ADAS ECU on HDA 2 vehicles to operate LFA. Used on cars that use message 272.";
CM_ 1043 "Lamp signals do not seem universal on cars that use LKAS_ALT, but stalk signals do.";
CM_ SG_ 80 HAS_LANE_SAFETY "If 0, hides LKAS 'Lane Safety' menu from vehicle settings";
CM_ SG_ 96 BRAKE_PRESSURE "User applied brake pedal pressure. Ramps from computer applied pressure on falling edge of cruise. Cruise cancels if !=0";
CM_ SG_ 101 BRAKE_POSITION "User applied brake pedal position, max is ~700. Signed on some vehicles";
CM_ SG_ 373 PROBABLY_EQUIP "aeb equip?";
CM_ SG_ 373 ACCEnable "Likely a copy of CAN's TCS13->ACCEnable";
CM_ SG_ 373 DriverBraking "Likely derived from BRAKE->BRAKE_POSITION";
CM_ SG_ 373 DriverBrakingLowSens "Higher threshold version of DriverBraking";
CM_ SG_ 352 SET_ME_9 "has something to do with AEB settings";
CM_ SG_ 416 VSetDis "set speed in display units";
CM_ SG_ 676 LEFT_LANE_LINE "Left lane line confidence";
CM_ SG_ 676 RIGHT_LANE_LINE "Right lane line confidence";
CM_ SG_ 736 MAX_SPEED "Display units. Restricts car from driving above this speed unless accelerator pedal is depressed beyond pressure point";
CM_ SG_ 866 LEFT_LANE_LINE "Left lane line confidence";
CM_ SG_ 866 RIGHT_LANE_LINE "Right lane line confidence";
CM_ SG_ 961 COUNTER_ALT "only increments on change";
CM_ SG_ 1041 COUNTER_ALT "only increments on change";
CM_ SG_ 1043 COUNTER_ALT "only increments on change";
CM_ SG_ 1043 USE_ALT_LAMP "likely 1 on cars that use alt lamp signals";
VAL_ 53 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ;
VAL_ 64 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ;
VAL_ 69 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ;
VAL_ 112 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ;
VAL_ 80 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ;
VAL_ 80 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ;
VAL_ 96 TRACTION_AND_STABILITY_CONTROL 0 "On" 5 "Limited" 1 "Off";
VAL_ 234 LKA_FAULT 0 "ok" 1 "lka fault" ;
VAL_ 272 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ;
VAL_ 272 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ;
VAL_ 298 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ;
VAL_ 298 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ;
VAL_ 304 PARK_BUTTON 1 "Pressed" 2 "Not Pressed";
VAL_ 304 KNOB_POSITION 1 "R" 2 "N (on R side)" 3 "Centered" 4 "N (on D side)" 5 "D";
VAL_ 304 GEAR 1 "P" 2 "R" 3 "N" 4 "D" ;
VAL_ 352 AEB_SETTING 1 "off" 2 "warning only" 3 "active assist" ;
VAL_ 362 BLINKER_CONTROL 1 "hazards" 2 "hazards button backlight" 3 "left blinkers" 4 "right blinkers";
VAL_ 373 ACCEnable 0 "SCC ready" 1 "SCC temp fault" 2 "SCC permanent fault" 3 "SCC permanent fault, communication issue";
VAL_ 416 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault" 4 "cancelled" ;
VAL_ 426 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4 "pause_resume" ;
VAL_ 463 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4 "pause_resume" ;
VAL_ 463 RIGHT_PADDLE 0 "Not Pulled" 1 "Pulled";
VAL_ 463 LEFT_PADDLE 0 "Not Pulled" 1 "Pulled";
VAL_ 676 LEFT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
VAL_ 676 RIGHT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
VAL_ 736 MSLA_STATUS 0 "disabled" 1 "active" 2 "paused";
VAL_ 866 LEFT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
VAL_ 866 RIGHT_LANE_LINE 0 "Not Detected" 1 "Low Confidence" 2 "Medium Confidence" 3 "High Confidence";
VAL_ 1041 DRIVER_DOOR 0 "Closed" 1 "Opened";
VAL_ 1041 PASSENGER_DOOR 0 "Closed" 1 "Opened";
VAL_ 1041 DRIVER_REAR_DOOR 0 "Closed" 1 "Opened";
VAL_ 1041 PASSENGER_REAR_DOOR 0 "Closed" 1 "Opened";
VAL_ 1041 DRIVER_SEATBELT 0 "Unlatched" 1 "Latched";
VAL_ 1041 PASSENGER_SEATBELT 0 "Unlatched" 1 "Latched";
VAL_ 1144 DRIVE_MODE2 3 "Set Sport" 1 "Set Normal" 2 "Set Eco";
VAL_ 1240 DISTANCE_UNIT 1 "Miles" 0 "Kilometers";

View File

@@ -1,5 +1,12 @@
#!/usr/bin/bash #!/usr/bin/bash
cd /data/openpilot
bash /data/openpilot/shell/init_shell.sh
# Blank the UI (assuming framebuffer device is /dev/fb0)
# This command clears the framebuffer, effectively blanking the screen
export PASSIVE="0" export PASSIVE="0"
exec ./launch_chffrplus.sh exec ./launch_chffrplus.sh

0
logs/blank Normal file
View File

103
notes Normal file
View File

@@ -0,0 +1,103 @@
hyundai/interface ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
- The speedometer icons are at:
- void AnnotatedCameraWidget::updateState(const UIState &s) {
- Test: no more wheel or record icons
- test: 5 minute timeout settings screen
- SSH reverse proxy if it is my personal dongle ID
- Read all car variables:
- Dash speed
- Experimental desired speed
- engagement
- UI updates:
- Turn HDA2 icon off if in experimental mode
- Show "AUTO" in green if in auto speed mode via wheel icon
- Simulated button presses
- Down / Up / Resume
- CAN data:
- Windows down
- Lead car distance while in stocklong
- Lead car is getting away
- Disable all telemetry to comma
- Conditional telemetry to me
- More than one wifi network - specify set wifi network for dashcam / route uploads and one for basic coms
- Implement stocklong on GM cars
- settings screen updates
- store / restore OP settings on my server
- cleanup code / reorganize fork
- Info button interception
- Set speedometer as main display on car start
- Set hvac to standard when car started w/ key (openpilot on)
- Store settings on device and on server when possible by dongle id. restore settings on boot.
- Bring up wifi before compile. If automatic updates are enabled and a update is available, fetch it and compile.
- disable lat turn signal below hwy speeds
- setting: disable lane change assist alltogether
- setting: auto passive dash cam recorder / uploader
- new home screen - weather, radar, location, car status, other fun things
- car location tracker automatic upload / api of car location
- can i get the cabin temprature from the comma??? display on home screen?
- review "BBOTCRASH" - rewrite the crash handler
- show map illustration, and drive stats, on route completion
https://github.com/pfeiferj/pfeifer-pilot-patches/tree/pfeifer-openpilot-patches/speed-limit-control
https://github.com/pfeiferj/pfeifer-pilot-patches/tree/pfeifer-openpilot-patches/disable-dcam-upload
https://github.com/pfeiferj/pfeifer-pilot-patches/tree/pfeifer-openpilot-patches/disable-registration
https://github.com/pfeiferj/pfeifer-pilot-patches/blob/pfeifer-openpilot-patches/notes/bus%20numbers.md
- It should do practically everything that opkr fork does
- MOST NOTABLY SteerActuatorDelay
Weather:
Ideally we intercept the info buttons on the wheel to control this.
UI: https://github.com/jclarke0000/MMM-OpenWeatherForecast/blob/master/README.md
Radar: https://github.com/jojoduquartier/MMM-RAIN-RADAR
Embed possibility: https://cefview.github.io/QCefView/docs/intros/buiding
Openpilot implementation example: https://github.com/twilsonco/openpilot
Get modem tempratures:
gdbus call --system --dest org.freedesktop.ModemManager1 --object-path /org/freedesktop/ModemManager1/Modem/1 --method org.freedesktop.ModemManager1.Modem.Command "AT+QTEMP" 50
Goals:
1 - get current dash speed via calculate_speed_limit_canfd
2 - issue set decel button via LFA button (proof of concept)
3 - chirp when speed limit changes
4 - Use LFA button to set speed of cruise to desired speed - 0-36 speed limit +3, 36-59 + 5, >= 60 +7
5 - On experimental mode engage, adjust cruise speed to experimental model speed
- Verify pause lateral under 30 is enabled
Wishlist:
- When car reports seeing lanes, turn lane keep over to car, resume when car can't see lanes or turn signal is activated
- Get hyundai speed limit reader merged into frogpilot
- Get stock experimental mode controller merged into frogpilot
- send canbus messages for current nav instruction / song playing - add my own notices
- start car on speedometer display
- signal windows are rolled down when turning off car
- Setup sentry
Pie in the sky:
- Roll up windows on power off (debug dump canbus)
OPKR features:
- Auto Resume at Stop
- Button Spam CC
- auto select speedometer on boot
- Accelerated Departure by Cruise Gap: Cruise gap automatically changed to step 1 for faster departure, sets back to orignal gap selection after few second.
- Weather radar screen (accessible by wheel)
- weather forecast screen (also by wheel)
- auto consent to carplay on car start
- auto hvac to comfortable on drve mode
ret.cruiseState.standstill cp_scc.vl["SCC11"]["SCCInfoDisplay"] == 4.
Interesting can messages
SG_ C_SunRoofOpenState
CR_Datc_DrTempDispF
2-9 test notes
- Only the icon with the lane lines is showing after recent lkas mods - can we show amber?
- pressing lkas distance does in fact cancel cruise, if only for a second. that means that it response to the cancel button - and possibly resume too?
- test: set cluster speed directly
- test: simulate down button on wheel
- we should put a splash screen of the pacman ghost over the main startup display, and only reveal the interface when we tap on it
- Fix: read speed limit from car computer, flicker LKAS button when it is out of range

View File

@@ -273,7 +273,8 @@ static bool hyundai_tx_hook(CANPacket_t *to_send) {
bool allowed_resume = (button == 1) && controls_allowed; bool allowed_resume = (button == 1) && controls_allowed;
bool allowed_cancel = (button == 4) && cruise_engaged_prev; bool allowed_cancel = (button == 4) && cruise_engaged_prev;
if (!(allowed_resume || allowed_cancel)) { bool allowed_cslc = (button == 1 || button == 2) && controls_allowed;
if (!(allowed_resume || allowed_cancel || allowed_cslc)) {
tx = false; tx = false;
} }
} }

View File

@@ -247,8 +247,9 @@ static bool hyundai_canfd_tx_hook(CANPacket_t *to_send) {
int button = GET_BYTE(to_send, 2) & 0x7U; int button = GET_BYTE(to_send, 2) & 0x7U;
bool is_cancel = (button == HYUNDAI_BTN_CANCEL); bool is_cancel = (button == HYUNDAI_BTN_CANCEL);
bool is_resume = (button == HYUNDAI_BTN_RESUME); bool is_resume = (button == HYUNDAI_BTN_RESUME);
bool is_cslc = (button == HYUNDAI_BTN_RESUME || button == HYUNDAI_BTN_SET);
bool allowed = (is_cancel && cruise_engaged_prev) || (is_resume && controls_allowed); bool allowed = (is_cancel && cruise_engaged_prev) || (is_resume && controls_allowed) || (is_cslc && controls_allowed);
if (!allowed) { if (!allowed) {
tx = false; tx = false;
} }

1
push_github Normal file
View File

@@ -0,0 +1 @@
GIT_SSH_COMMAND='ssh -i ~/.ssh/id_rsa_brianhansonxyzgithub -o IdentitiesOnly=yes' git push github oscarpilot

1
push_private Normal file
View File

@@ -0,0 +1 @@
git push origin oscarpilot

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

View File

@@ -0,0 +1,210 @@
from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip
from openpilot.common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance
from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR
VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState
# EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second
# All slightly below EPS thresholds to avoid fault
MAX_ANGLE = 85
MAX_ANGLE_FRAMES = 89
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
def process_hud_alert(enabled, fingerprint, hud_control):
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
# initialize to no line visible
# TODO: this is not accurate for all cars
sys_state = 1
if hud_control.leftLaneVisible and hud_control.rightLaneVisible or sys_warning: # HUD alert only display when LKAS status is active
sys_state = 3 if enabled or sys_warning else 4
elif hud_control.leftLaneVisible:
sys_state = 5
elif hud_control.rightLaneVisible:
sys_state = 6
# initialize to no warnings
left_lane_warning = 0
right_lane_warning = 0
if hud_control.leftLaneDepart:
left_lane_warning = 1 if fingerprint in (CAR.GENESIS_G90, CAR.GENESIS_G80) else 2
if hud_control.rightLaneDepart:
right_lane_warning = 1 if fingerprint in (CAR.GENESIS_G90, CAR.GENESIS_G80) else 2
return sys_warning, sys_state, left_lane_warning, right_lane_warning
class CarController:
def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.CAN = CanBus(CP)
self.params = CarControllerParams(CP)
self.packer = CANPacker(dbc_name)
self.angle_limit_counter = 0
self.frame = 0
self.accel_last = 0
self.apply_steer_last = 0
self.car_fingerprint = CP.carFingerprint
self.last_button_frame = 0
def update(self, CC, CS, now_nanos, sport_plus):
actuators = CC.actuators
hud_control = CC.hudControl
# steering torque
new_steer = int(round(actuators.steer * self.params.STEER_MAX))
apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
# >90 degree steering fault prevention
self.angle_limit_counter, apply_steer_req = common_fault_avoidance(abs(CS.out.steeringAngleDeg) >= MAX_ANGLE, CC.latActive,
self.angle_limit_counter, MAX_ANGLE_FRAMES,
MAX_ANGLE_CONSECUTIVE_FRAMES)
if not CC.latActive:
apply_steer = 0
# Hold torque with induced temporary fault when cutting the actuation bit
torque_fault = CC.latActive and not apply_steer_req
self.apply_steer_last = apply_steer
# accel + longitudinal
if sport_plus:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX_PLUS)
else:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
hud_control)
can_sends = []
# *** common hyundai stuff ***
# tester present - w/ no response (keeps relevant ECU disabled)
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and self.CP.openpilotLongitudinalControl:
# for longitudinal control, either radar or ADAS driving ECU
addr, bus = 0x7d0, 0
if self.CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, self.CAN.ECAN
can_sends.append([addr, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", bus])
# for blinkers
if self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.append([0x7b1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", self.CAN.ECAN])
# CAN-FD platforms
if self.CP.carFingerprint in CANFD_CAR:
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
hda2_long = hda2 and self.CP.openpilotLongitudinalControl
# steering control
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_steer))
# prevent LFA from activating on HDA2 by sending "no lane lines detected" to ADAS ECU
if self.frame % 5 == 0 and hda2:
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.hda2_lfa_block_msg,
self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING))
# LFA and HDA icons
if self.frame % 5 == 0 and (not hda2 or hda2_long):
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled))
# blinkers
if hda2 and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, self.frame, CC.leftBlinker, CC.rightBlinker))
if self.CP.openpilotLongitudinalControl:
if hda2:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
if self.frame % 2 == 0:
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
set_speed_in_units, CS.personality_profile))
self.accel_last = accel
else:
# button presses
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
if not self.CP.openpilotLongitudinalControl:
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=True))
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
# TODO: unclear if this is needed
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
hud_control.leadVisible, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, CS.out.cruiseState.available, CS.personality_profile))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
# 5 Hz ACC options
if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.extend(hyundaican.create_acc_opt(self.packer))
# 2 Hz front radar options
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
new_actuators = actuators.copy()
new_actuators.steer = apply_steer / self.params.STEER_MAX
new_actuators.steerOutputCan = apply_steer
new_actuators.accel = accel
self.frame += 1
return new_actuators, can_sends
def create_button_messages(self, CC: car.CarControl, CS: car.CarState, use_clu11: bool):
can_sends = []
if use_clu11:
if CC.cruiseControl.cancel:
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP.carFingerprint))
elif CC.cruiseControl.resume:
# send resume at a max freq of 10Hz
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
# send 25 messages at a time to increases the likelihood of resume being accepted
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP.carFingerprint)] * 25)
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
else:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# cruise cancel
if CC.cruiseControl.cancel:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
self.last_button_frame = self.frame
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame
# cruise standstill resume
elif CC.cruiseControl.resume:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
# TODO: resume for alt button cars
pass
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
return can_sends

View File

@@ -0,0 +1,500 @@
from collections import deque
import copy
import math
from cereal import car
from openpilot.common.conversions import Conversions as CV
from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \
CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController
from openpilot.common.watcher import Watcher
PREV_BUTTON_SAMPLES = 8
CLUSTER_SAMPLE_RATE = 20 # frames
STANDSTILL_THRESHOLD = 12 * 0.03125 * CV.KPH_TO_MS
class CarState(CarStateBase):
def __init__(self, CP):
super().__init__(CP)
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
self.cruise_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.gear_msg_canfd = "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
"GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \
"GEAR_SHIFTER"
if CP.carFingerprint in CANFD_CAR:
self.shifter_values = can_define.dv[self.gear_msg_canfd]["GEAR"]
elif self.CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"]
elif self.CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
self.shifter_values = can_define.dv["TCU12"]["CUR_GR"]
else: # preferred and elect gear methods use same definition
self.shifter_values = can_define.dv["LVR12"]["CF_Lvr_Gear"]
self.accelerator_msg_canfd = "ACCELERATOR" if CP.carFingerprint in EV_CAR else \
"ACCELERATOR_ALT" if CP.carFingerprint in HYBRID_CAR else \
"ACCELERATOR_BRAKE_ALT"
self.cruise_btns_msg_canfd = "CRUISE_BUTTONS_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else \
"CRUISE_BUTTONS"
self.is_metric = False
self.buttons_counter = 0
self.cruise_info = {}
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
self.cluster_speed = 0
self.cluster_speed_counter = CLUSTER_SAMPLE_RATE
self.params = CarControllerParams(CP)
def update(self, cp, cp_cam):
Watcher.log_watch("hyundai_carstate_update_cp", cp)
Watcher.log_watch("hyundai_carstate_update_cp_cam", cp_cam)
if self.CP.carFingerprint in CANFD_CAR:
return self.update_canfd(cp, cp_cam)
ret = car.CarState.new_message()
cp_cruise = cp_cam if self.CP.carFingerprint in CAMERA_SCC_CAR else cp
self.is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0
speed_conv = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
ret.doorOpen = any([cp.vl["CGW1"]["CF_Gway_DrvDrSw"], cp.vl["CGW1"]["CF_Gway_AstDrSw"],
cp.vl["CGW2"]["CF_Gway_RLDrSw"], cp.vl["CGW2"]["CF_Gway_RRDrSw"]])
ret.seatbeltUnlatched = cp.vl["CGW1"]["CF_Gway_DrvSeatBeltSw"] == 0
ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHL_SPD11"]["WHL_SPD_FL"],
cp.vl["WHL_SPD11"]["WHL_SPD_FR"],
cp.vl["WHL_SPD11"]["WHL_SPD_RL"],
cp.vl["WHL_SPD11"]["WHL_SPD_RR"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
self.cluster_speed_counter += 1
if self.cluster_speed_counter > CLUSTER_SAMPLE_RATE:
self.cluster_speed = cp.vl["CLU15"]["CF_Clu_VehicleSpeed"]
self.cluster_speed_counter = 0
# Mimic how dash converts to imperial.
# Sorento is the only platform where CF_Clu_VehicleSpeed is already imperial when not is_metric
# TODO: CGW_USM1->CF_Gway_DrLockSoundRValue may describe this
if not self.is_metric and self.CP.carFingerprint not in (CAR.KIA_SORENTO,):
self.cluster_speed = math.floor(self.cluster_speed * CV.KPH_TO_MPH + CV.KPH_TO_MPH)
ret.vEgoCluster = self.cluster_speed * speed_conv
ret.steeringAngleDeg = cp.vl["SAS11"]["SAS_Angle"]
ret.steeringRateDeg = cp.vl["SAS11"]["SAS_Speed"]
ret.yawRate = cp.vl["ESP12"]["YAW_RATE"]
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(
50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"])
ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"]
ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0
# cruise state
if self.CP.openpilotLongitudinalControl:
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
ret.cruiseState.available = self.main_enabled
ret.cruiseState.enabled = cp.vl["TCS13"]["ACC_REQ"] == 1
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
else:
ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
ret.cruiseState.standstill = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 4.
ret.cruiseState.nonAdaptive = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
ret.cruiseState.speed = cp_cruise.vl["SCC11"]["VSetDis"] * speed_conv
# TODO: Find brake pressure
ret.brake = 0
ret.brakePressed = cp.vl["TCS13"]["DriverOverride"] == 2 # 2 includes regen braking by user on HEV/EV
ret.brakeHoldActive = cp.vl["TCS15"]["AVH_LAMP"] == 2 # 0 OFF, 1 ERROR, 2 ACTIVE, 3 READY
ret.parkingBrake = cp.vl["TCS13"]["PBRAKE_ACT"] == 1
ret.accFaulted = cp.vl["TCS13"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if self.CP.carFingerprint in (HYBRID_CAR | EV_CAR):
if self.CP.carFingerprint in HYBRID_CAR:
ret.gas = cp.vl["E_EMS11"]["CR_Vcu_AccPedDep_Pos"] / 254.
else:
ret.gas = cp.vl["E_EMS11"]["Accel_Pedal_Pos"] / 254.
ret.gasPressed = ret.gas > 0
else:
ret.gas = cp.vl["EMS12"]["PV_AV_CAN"] / 100.
ret.gasPressed = bool(cp.vl["EMS16"]["CF_Ems_AclAct"])
# Gear Selection via Cluster - For those Kia/Hyundai which are not fully discovered, we can use the Cluster Indicator for Gear Selection,
# as this seems to be standard over all cars, but is not the preferred method.
if self.CP.carFingerprint in (HYBRID_CAR | EV_CAR):
gear = cp.vl["ELECT_GEAR"]["Elect_Gear_Shifter"]
elif self.CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
gear = cp.vl["CLU15"]["CF_Clu_Gear"]
elif self.CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
gear = cp.vl["TCU12"]["CUR_GR"]
else:
gear = cp.vl["LVR12"]["CF_Lvr_Gear"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if not self.CP.openpilotLongitudinalControl:
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1 # sometimes only SCC system shows an FCW
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
ret.stockAeb = aeb_warning and aeb_braking
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0
# save the entire LKAS11 and CLU11
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
self.clu11 = copy.copy(cp.vl["CLU11"])
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
self.prev_cruise_buttons = self.cruise_buttons[-1]
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
self.prev_main_buttons = self.main_buttons[-1]
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
if self.prev_main_buttons == 0 and self.main_buttons[-1] != 0:
self.main_enabled = not self.main_enabled
# BBot functions for lfa and gap buttons - test speed up / down
self.oscar_lane_center_btn_pressed= False
self.oscar_slc_decel = False
self.oscar_slc_accel = False
self.param_memory.put("oscar_debug", "Hello World")
# if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
# self.oscar_lane_center_btn_pressed= True
# lkas_pressed = cp.vl["BCM_PO_11"]["LFA_Pressed"]
# if lkas_pressed and not self.lkas_previously_pressed:
# self.custom_speed_up = True
# self.lkas_previously_pressed = lkas_pressed
# Driving personalities function
# if self.personalities_via_wheel and ret.cruiseState.available:
# # Sync with the onroad UI button
# if self.param_memory.get_bool("PersonalityChangedViaUI"):
# self.personality_profile = self.param.get_int("LongitudinalPersonality")
# self.param_memory.put_bool("PersonalityChangedViaUI", False)
# # Change personality upon steering wheel button press
# if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
# self.param_memory.put_bool("PersonalityChangedViaWheel", True)
# self.personality_profile = (self.previous_personality_profile + 2) % 3
# if self.personality_profile != self.previous_personality_profile and self.personality_profile >= 0:
# self.param.put_int("LongitudinalPersonality", self.personality_profile)
# self.previous_personality_profile = self.personality_profile
# # Toggle Experimental Mode from steering wheel function
# if self.experimental_mode_via_lkas and ret.cruiseState.available:
# lkas_pressed = cp.vl["BCM_PO_11"]["LFA_Pressed"]
# if lkas_pressed and not self.lkas_previously_pressed:
# if self.conditional_experimental_mode:
# # Set "CEStatus" to work with "Conditional Experimental Mode"
# conditional_status = self.param_memory.get_int("CEStatus")
# override_value = 0 if conditional_status in (1, 2, 3, 4) else 1 if conditional_status >= 5 else 2
# self.param_memory.put_int("CEStatus", override_value)
# else:
# experimental_mode = self.param.get_bool("ExperimentalMode")
# # Invert the value of "ExperimentalMode"
# put_bool_nonblocking("ExperimentalMode", not experimental_mode)
# self.lkas_previously_pressed = lkas_pressed
Watcher.log_watch("hyundai_carstate_update_self", self)
return ret
def update_canfd(self, cp, cp_cam):
Watcher.log_watch("hyundai_carstate_update_canfd_cp", cp)
Watcher.log_watch("hyundai_carstate_update_canfd_cp_cam", cp_cam)
ret = car.CarState.new_message()
self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
if self.CP.carFingerprint in (EV_CAR | HYBRID_CAR):
offset = 255. if self.CP.carFingerprint in EV_CAR else 1023.
ret.gas = cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL"] / offset
ret.gasPressed = ret.gas > 1e-5
else:
ret.gasPressed = bool(cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL_PRESSED"])
ret.brakePressed = cp.vl["TCS"]["DriverBraking"] == 1
ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR"] == 1
ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT"] == 0
gear = cp.vl[self.gear_msg_canfd]["GEAR"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
# TODO: figure out positions
ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_1"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_2"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_3"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_4"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEERING_RATE"]
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1
ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"]
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
# TODO: alt signal usage may be described by cp.vl['BLINKERS']['USE_ALT_LAMP']
left_blinker_sig, right_blinker_sig = "LEFT_LAMP", "RIGHT_LAMP"
if self.CP.carFingerprint == CAR.KONA_EV_2ND_GEN:
left_blinker_sig, right_blinker_sig = "LEFT_LAMP_ALT", "RIGHT_LAMP_ALT"
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"][left_blinker_sig],
cp.vl["BLINKERS"][right_blinker_sig])
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] != 0
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0
# cruise state
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
ret.cruiseState.available = self.main_enabled
if self.CP.openpilotLongitudinalControl:
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1
ret.cruiseState.standstill = False
else:
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2)
ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["CRUISE_STANDSTILL"] == 1
ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor
self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"])
# Manual Speed Limit Assist is a feature that replaces non-adaptive cruise control on EV CAN FD platforms.
# It limits the vehicle speed, overridable by pressing the accelerator past a certain point.
# The car will brake, but does not respect positive acceleration commands in this mode
# TODO: find this message on ICE & HYBRID cars + cruise control signals (if exists)
if self.CP.carFingerprint in EV_CAR:
ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
self.prev_cruise_buttons = self.cruise_buttons[-1]
self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
self.prev_main_buttons = self.main_buttons[-1]
self.main_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["ADAPTIVE_CRUISE_MAIN_BTN"])
if self.prev_main_buttons == 0 and self.main_buttons[-1] != 0:
self.main_enabled = not self.main_enabled
self.buttons_counter = cp.vl[self.cruise_btns_msg_canfd]["COUNTER"]
ret.accFaulted = cp.vl["TCS"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if self.CP.flags & HyundaiFlags.CANFD_HDA2:
self.hda2_lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x362"] if self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING
else cp_cam.vl["CAM_0x2a4"])
SpeedLimitController.load_state()
SpeedLimitController.car_speed_limit = self.calculate_speed_limit_canfd(self.CP, cp, cp_cam) * speed_factor
SpeedLimitController.write_car_state()
# self.custom_speed_up = False
self.oscar_lane_center_btn_pressed = False
if ret.cruiseState.available:
if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
self.oscar_lane_center_btn_pressed = True
lkas_pressed = False
try:
lkas_pressed = cp.vl[self.cruise_btns_msg_canfd]["LKAS_BTN"]
except:
nothing = 0
# intentionally cause a failure
if lkas_pressed:
floog=norpdywoop
# if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
# self.oscar_lane_center_btn_pressed= True
# Driving personalities function
# if self.personalities_via_wheel and ret.cruiseState.available:
# # Sync with the onroad UI button
# if self.param_memory.get_bool("PersonalityChangedViaUI"):
# self.personality_profile = self.param.get_int("LongitudinalPersonality")
# self.param_memory.put_bool("PersonalityChangedViaUI", False)
# # Change personality upon steering wheel button press
# if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
# self.param_memory.put_bool("PersonalityChangedViaWheel", True)
# self.personality_profile = (self.previous_personality_profile + 2) % 3
# if self.personality_profile != self.previous_personality_profile and self.personality_profile >= 0:
# self.param.put_int("LongitudinalPersonality", self.personality_profile)
# self.previous_personality_profile = self.personality_profile
# # Toggle Experimental Mode from steering wheel function
# if self.experimental_mode_via_lkas and ret.cruiseState.available:
# lkas_pressed = cp.vl[self.cruise_btns_msg_canfd]["LKAS_BTN"]
# if lkas_pressed and not self.lkas_previously_pressed:
# if self.conditional_experimental_mode:
# # Set "CEStatus" to work with "Conditional Experimental Mode"
# conditional_status = self.param_memory.get_int("CEStatus")
# override_value = 0 if conditional_status in (1, 2, 3, 4) else 1 if conditional_status >= 5 else 2
# self.param_memory.put_int("CEStatus", override_value)
# else:
# experimental_mode = self.param.get_bool("ExperimentalMode")
# # Invert the value of "ExperimentalMode"
# put_bool_nonblocking("ExperimentalMode", not experimental_mode)
# self.lkas_previously_pressed = lkas_pressed
Watcher.log_watch("hyundai_carstate_update_canfd_self", self)
return ret
# BBOT does not work
def calculate_speed_limit_canfd(self, CP, cp, cp_cam):
try:
self._speed_limit_clu = cp.vl["CLUSTER_SPEED_LIMIT"]["SPEED_LIMIT_1"]
return self._speed_limit_clu if self._speed_limit_clu not in (0, 255) else 0
except:
return 0
def get_can_parser(self, CP):
if CP.carFingerprint in CANFD_CAR:
return self.get_can_parser_canfd(CP)
messages = [
# address, frequency
("MDPS12", 50),
("TCS13", 50),
("TCS15", 10),
("CLU11", 50),
("CLU15", 5),
("ESP12", 100),
("CGW1", 10),
("CGW2", 5),
("CGW4", 5),
("WHL_SPD11", 50),
("SAS11", 100),
]
if not CP.openpilotLongitudinalControl and CP.carFingerprint not in CAMERA_SCC_CAR:
messages += [
("SCC11", 50),
("SCC12", 50),
]
if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))
if CP.enableBsm:
messages.append(("LCA11", 50))
if CP.carFingerprint in (HYBRID_CAR | EV_CAR):
messages.append(("E_EMS11", 50))
else:
messages += [
("EMS12", 100),
("EMS16", 100),
]
if CP.carFingerprint in (HYBRID_CAR | EV_CAR):
messages.append(("ELECT_GEAR", 20))
elif CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
pass
elif CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
messages.append(("TCU12", 100))
else:
messages.append(("LVR12", 100))
messages.append(("BCM_PO_11", 50))
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0)
@staticmethod
def get_cam_can_parser(CP):
if CP.carFingerprint in CANFD_CAR:
return CarState.get_cam_can_parser_canfd(CP)
messages = [
("LKAS11", 100)
]
if not CP.openpilotLongitudinalControl and CP.carFingerprint in CAMERA_SCC_CAR:
messages += [
("SCC11", 50),
("SCC12", 50),
]
if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2)
def get_can_parser_canfd(self, CP):
messages = [
(self.gear_msg_canfd, 100),
(self.accelerator_msg_canfd, 100),
("WHEEL_SPEEDS", 100),
("STEERING_SENSORS", 100),
("MDPS", 100),
("TCS", 50),
("CRUISE_BUTTONS_ALT", 50),
("BLINKERS", 4),
("DOORS_SEATBELTS", 4),
]
if CP.carFingerprint in EV_CAR:
messages += [
("MANUAL_SPEED_LIMIT_ASSIST", 10),
]
if not (CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
messages += [
("CRUISE_BUTTONS", 50)
]
if CP.enableBsm:
messages += [
("BLINDSPOTS_REAR_CORNERS", 20),
]
# if CP.nav_msg:
# messages.append(("CLUSTER_SPEED_LIMIT", 10))
if not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and not CP.openpilotLongitudinalControl:
messages += [
("SCC_CONTROL", 50),
]
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).ECAN)
@staticmethod
def get_cam_can_parser_canfd(CP):
messages = []
if CP.flags & HyundaiFlags.CANFD_HDA2:
block_lfa_msg = "CAM_0x362" if CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else "CAM_0x2a4"
messages += [(block_lfa_msg, 20)]
elif CP.flags & HyundaiFlags.CANFD_CAMERA_SCC:
messages += [
("SCC_CONTROL", 50),
]
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).CAM)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
import crcmod
from openpilot.selfdrive.car.hyundai.values import CAR, CHECKSUM, CAMERA_SCC_CAR
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req,
torque_fault, lkas11, sys_warning, sys_state, enabled,
left_lane, right_lane,
left_lane_depart, right_lane_depart):
values = {s: lkas11[s] for s in [
"CF_Lkas_LdwsActivemode",
"CF_Lkas_LdwsSysState",
"CF_Lkas_SysWarning",
"CF_Lkas_LdwsLHWarning",
"CF_Lkas_LdwsRHWarning",
"CF_Lkas_HbaLamp",
"CF_Lkas_FcwBasReq",
"CF_Lkas_HbaSysState",
"CF_Lkas_FcwOpt",
"CF_Lkas_HbaOpt",
"CF_Lkas_FcwSysState",
"CF_Lkas_FcwCollisionWarning",
"CF_Lkas_FusionState",
"CF_Lkas_FcwOpt_USM",
"CF_Lkas_LdwsOpt_USM",
]}
values["CF_Lkas_LdwsSysState"] = sys_state
values["CF_Lkas_SysWarning"] = 3 if sys_warning else 0
values["CF_Lkas_LdwsLHWarning"] = left_lane_depart
values["CF_Lkas_LdwsRHWarning"] = right_lane_depart
values["CR_Lkas_StrToqReq"] = apply_steer
values["CF_Lkas_ActToi"] = steer_req
values["CF_Lkas_ToiFlt"] = torque_fault # seems to allow actuation on CR_Lkas_StrToqReq
values["CF_Lkas_MsgCount"] = frame % 0x10
if car_fingerprint in (CAR.SONATA, CAR.PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.SANTA_FE,
CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020,
CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022,
CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022,
CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN, CAR.CUSTIN_1ST_GEN):
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
values["CF_Lkas_LdwsOpt_USM"] = 2
# FcwOpt_USM 5 = Orange blinking car + lanes
# FcwOpt_USM 4 = Orange car + lanes
# FcwOpt_USM 3 = Green blinking car + lanes
# FcwOpt_USM 2 = Green car + lanes
# FcwOpt_USM 1 = White car + lanes
# FcwOpt_USM 0 = No car + lanes
values["CF_Lkas_FcwOpt_USM"] = 2 if enabled else 1
# SysWarning 4 = keep hands on wheel
# SysWarning 5 = keep hands on wheel (red)
# SysWarning 6 = keep hands on wheel (red) + beep
# Note: the warning is hidden while the blinkers are on
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
# Likely cars lacking the ability to show individual lane lines in the dash
elif car_fingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL):
# SysWarning 4 = keep hands on wheel + beep
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
# SysState 0 = no icons
# SysState 1-2 = white car + lanes
# SysState 3 = green car + lanes, green steering wheel
# SysState 4 = green car + lanes
values["CF_Lkas_LdwsSysState"] = 3 if enabled else 1
values["CF_Lkas_LdwsOpt_USM"] = 2 # non-2 changes above SysState definition
# these have no effect
values["CF_Lkas_LdwsActivemode"] = 0
values["CF_Lkas_FcwOpt_USM"] = 0
elif car_fingerprint == CAR.HYUNDAI_GENESIS:
# This field is actually LdwsActivemode
# Genesis and Optima fault when forwarding while engaged
values["CF_Lkas_LdwsActivemode"] = 2
dat = packer.make_can_msg("LKAS11", 0, values)[2]
if car_fingerprint in CHECKSUM["crc8"]:
# CRC Checksum as seen on 2019 Hyundai Santa Fe
dat = dat[:6] + dat[7:8]
checksum = hyundai_checksum(dat)
elif car_fingerprint in CHECKSUM["6B"]:
# Checksum of first 6 Bytes, as seen on 2018 Kia Sorento
checksum = sum(dat[:6]) % 256
else:
# Checksum of first 6 Bytes and last Byte as seen on 2018 Kia Stinger
checksum = (sum(dat[:6]) + dat[7]) % 256
values["CF_Lkas_Chksum"] = checksum
return packer.make_can_msg("LKAS11", 0, values)
def create_clu11(packer, frame, clu11, button, car_fingerprint):
values = {s: clu11[s] for s in [
"CF_Clu_CruiseSwState",
"CF_Clu_CruiseSwMain",
"CF_Clu_SldMainSW",
"CF_Clu_ParityBit1",
"CF_Clu_VanzDecimal",
"CF_Clu_Vanz",
"CF_Clu_SPEED_UNIT",
"CF_Clu_DetentOut",
"CF_Clu_RheostatLevel",
"CF_Clu_CluInfo",
"CF_Clu_AmpInfo",
"CF_Clu_AliveCnt1",
]}
values["CF_Clu_CruiseSwState"] = button
values["CF_Clu_AliveCnt1"] = frame % 0x10
# send buttons to camera on camera-scc based cars
bus = 2 if car_fingerprint in CAMERA_SCC_CAR else 0
return packer.make_can_msg("CLU11", bus, values)
def create_lfahda_mfc(packer, enabled, hda_set_speed=0):
values = {
"LFA_Icon_State": 2 if enabled else 0,
"HDA_Active": 1 if hda_set_speed else 0,
"HDA_Icon_State": 2 if hda_set_speed else 0,
"HDA_VSetReq": hda_set_speed,
}
return packer.make_can_msg("LFAHDA_MFC", 0, values)
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_visible, set_speed, stopping, long_override, use_fca, cruise_available, personality):
commands = []
scc11_values = {
"MainMode_ACC": 1 if cruise_available else 0,
"TauGapSet": personality + 1,
"VSetDis": set_speed if enabled else 0,
"AliveCounterACC": idx % 0x10,
"ObjValid": 1, # close lead makes controls tighter
"ACC_ObjStatus": 1, # close lead makes controls tighter
"ACC_ObjLatPos": 0,
"ACC_ObjRelSpd": 0,
"ACC_ObjDist": 1, # close lead makes controls tighter
}
commands.append(packer.make_can_msg("SCC11", 0, scc11_values))
scc12_values = {
"ACCMode": 2 if enabled and long_override else 1 if enabled else 0,
"StopReq": 1 if stopping else 0,
"aReqRaw": accel,
"aReqValue": accel, # stock ramps up and down respecting jerk limit until it reaches aReqRaw
"CR_VSM_Alive": idx % 0xF,
}
# show AEB disabled indicator on dash with SCC12 if not sending FCA messages.
# these signals also prevent a TCS fault on non-FCA cars with alpha longitudinal
if not use_fca:
scc12_values["CF_VSM_ConfMode"] = 1
scc12_values["AEB_Status"] = 1 # AEB disabled
scc12_dat = packer.make_can_msg("SCC12", 0, scc12_values)[2]
scc12_values["CR_VSM_ChkSum"] = 0x10 - sum(sum(divmod(i, 16)) for i in scc12_dat) % 0x10
commands.append(packer.make_can_msg("SCC12", 0, scc12_values))
scc14_values = {
"ComfortBandUpper": 0.0, # stock usually is 0 but sometimes uses higher values
"ComfortBandLower": 0.0, # stock usually is 0 but sometimes uses higher values
"JerkUpperLimit": upper_jerk, # stock usually is 1.0 but sometimes uses higher values
"JerkLowerLimit": 5.0, # stock usually is 0.5 but sometimes uses higher values
"ACCMode": 2 if enabled and long_override else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
"ObjGap": 2 if lead_visible else 0, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
}
commands.append(packer.make_can_msg("SCC14", 0, scc14_values))
# Only send FCA11 on cars where it exists on the bus
if use_fca:
# note that some vehicles most likely have an alternate checksum/counter definition
# https://github.com/commaai/opendbc/commit/9ddcdb22c4929baf310295e832668e6e7fcfa602
fca11_values = {
"CR_FCA_Alive": idx % 0xF,
"PAINT1_Status": 1,
"FCA_DrvSetStatus": 1,
"FCA_Status": 1, # AEB disabled
}
fca11_dat = packer.make_can_msg("FCA11", 0, fca11_values)[2]
fca11_values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
commands.append(packer.make_can_msg("FCA11", 0, fca11_values))
return commands
def create_acc_opt(packer):
commands = []
scc13_values = {
"SCCDrvModeRValue": 2,
"SCC_Equip": 1,
"Lead_Veh_Dep_Alert_USM": 2,
}
commands.append(packer.make_can_msg("SCC13", 0, scc13_values))
# TODO: this needs to be detected and conditionally sent on unsupported long cars
fca12_values = {
"FCA_DrvSetState": 2,
"FCA_USM": 1, # AEB disabled
}
commands.append(packer.make_can_msg("FCA12", 0, fca12_values))
return commands
def create_frt_radar_opt(packer):
frt_radar11_values = {
"CF_FCA_Equip_Front_Radar": 1,
}
return packer.make_can_msg("FRT_RADAR11", 0, frt_radar11_values)

View File

@@ -0,0 +1,224 @@
from openpilot.common.numpy_fast import clip
from openpilot.selfdrive.car import CanBusBase
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags
class CanBus(CanBusBase):
def __init__(self, CP, hda2=None, fingerprint=None) -> None:
super().__init__(CP, fingerprint)
if hda2 is None:
assert CP is not None
hda2 = CP.flags & HyundaiFlags.CANFD_HDA2.value
# On the CAN-FD platforms, the LKAS camera is on both A-CAN and E-CAN. HDA2 cars
# have a different harness than the HDA1 and non-HDA variants in order to split
# a different bus, since the steering is done by different ECUs.
self._a, self._e = 1, 0
if hda2:
self._a, self._e = 0, 1
self._a += self.offset
self._e += self.offset
self._cam = 2 + self.offset
@property
def ECAN(self):
return self._e
@property
def ACAN(self):
return self._a
@property
def CAM(self):
return self._cam
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_steer):
ret = []
values = {
"LKA_MODE": 2,
"LKA_ICON": 2 if enabled else 1 if lat_active else 0,
"TORQUE_REQUEST": apply_steer,
"LKA_ASSIST": 0,
"STEER_REQ": 1 if lat_active else 0,
"STEER_MODE": 0,
"HAS_LANE_SAFETY": 0, # hide LKAS settings
"NEW_SIGNAL_1": 0,
"NEW_SIGNAL_2": 0,
}
if CP.flags & HyundaiFlags.CANFD_HDA2:
hda2_lkas_msg = "LKAS_ALT" if CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else "LKAS"
if CP.openpilotLongitudinalControl:
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
ret.append(packer.make_can_msg(hda2_lkas_msg, CAN.ACAN, values))
else:
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
return ret
def create_suppress_lfa(packer, CAN, hda2_lfa_block_msg, hda2_alt_steering):
suppress_msg = "CAM_0x362" if hda2_alt_steering else "CAM_0x2a4"
msg_bytes = 32 if hda2_alt_steering else 24
values = {f"BYTE{i}": hda2_lfa_block_msg[f"BYTE{i}"] for i in range(3, msg_bytes) if i != 7}
values["COUNTER"] = hda2_lfa_block_msg["COUNTER"]
values["SET_ME_0"] = 0
values["SET_ME_0_2"] = 0
values["LEFT_LANE_LINE"] = 0
values["RIGHT_LANE_LINE"] = 0
return packer.make_can_msg(suppress_msg, CAN.ACAN, values)
def create_buttons(packer, CP, CAN, cnt, btn):
values = {
"COUNTER": cnt,
"SET_ME_1": 1,
"CRUISE_BUTTONS": btn,
}
bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_HDA2 else CAN.CAM
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
# TODO: why do we copy different values here?
if CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value:
values = {s: cruise_info_copy[s] for s in [
"COUNTER",
"CHECKSUM",
"NEW_SIGNAL_1",
"MainMode_ACC",
"ACCMode",
"ZEROS_9",
"CRUISE_STANDSTILL",
"ZEROS_5",
"DISTANCE_SETTING",
"VSetDis",
]}
else:
values = {s: cruise_info_copy[s] for s in [
"COUNTER",
"CHECKSUM",
"ACCMode",
"VSetDis",
"CRUISE_STANDSTILL",
]}
values.update({
"ACCMode": 4,
"aReqRaw": 0.0,
"aReqValue": 0.0,
})
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_lfahda_cluster(packer, CAN, enabled):
values = {
"HDA_ICON": 1 if enabled else 0,
# 0 off, 1 gray, 2 green, 3 blinking (wheel icon)
"LFA_ICON": 0 if enabled else 0, # was green before i think in 2? 3 should be flashing?
# "LFA_ICON": 2 if enabled else 0,
}
return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, personality):
jerk = 5
jn = jerk / 50
if not enabled or gas_override:
a_val, a_raw = 0, 0
else:
a_raw = accel
a_val = clip(accel, accel_last - jn, accel_last + jn)
values = {
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
"MainMode_ACC": 1,
"StopReq": 1 if stopping else 0,
"aReqValue": a_val,
"aReqRaw": a_raw,
"VSetDis": set_speed,
"JerkLowerLimit": jerk if enabled else 1,
"JerkUpperLimit": 3.0,
"ACC_ObjDist": 1,
"ObjValid": 0,
"OBJ_STATUS": 2,
"SET_ME_2": 0x4,
"SET_ME_3": 0x3,
"SET_ME_TMP_64": 0x64,
"DISTANCE_SETTING": personality + 1,
}
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_spas_messages(packer, CAN, frame, left_blink, right_blink):
ret = []
values = {
}
ret.append(packer.make_can_msg("SPAS1", CAN.ECAN, values))
blink = 0
if left_blink:
blink = 3
elif right_blink:
blink = 4
values = {
"BLINKER_CONTROL": blink,
}
ret.append(packer.make_can_msg("SPAS2", CAN.ECAN, values))
return ret
def create_adrv_messages(packer, CAN, frame):
# messages needed to car happy after disabling
# the ADAS Driving ECU to do longitudinal control
ret = []
values = {
}
ret.append(packer.make_can_msg("ADRV_0x51", CAN.ACAN, values))
if frame % 2 == 0:
values = {
'AEB_SETTING': 0x1, # show AEB disabled icon
'SET_ME_2': 0x2,
'SET_ME_FF': 0xff,
'SET_ME_FC': 0xfc,
'SET_ME_9': 0x9,
}
ret.append(packer.make_can_msg("ADRV_0x160", CAN.ECAN, values))
if frame % 5 == 0:
values = {
'SET_ME_1C': 0x1c,
'SET_ME_FF': 0xff,
'SET_ME_TMP_F': 0xf,
'SET_ME_TMP_F_2': 0xf,
}
ret.append(packer.make_can_msg("ADRV_0x1ea", CAN.ECAN, values))
values = {
'SET_ME_E1': 0xe1,
'SET_ME_3A': 0x3a,
}
ret.append(packer.make_can_msg("ADRV_0x200", CAN.ECAN, values))
if frame % 20 == 0:
values = {
'SET_ME_15': 0x15,
}
ret.append(packer.make_can_msg("ADRV_0x345", CAN.ECAN, values))
if frame % 100 == 0:
values = {
'SET_ME_22': 0x22,
'SET_ME_41': 0x41,
}
ret.append(packer.make_can_msg("ADRV_0x1da", CAN.ECAN, values))
return ret

View File

@@ -0,0 +1,375 @@
from cereal import car
from panda import Panda
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, EV_CAR, HYBRID_CAR, LEGACY_SAFETY_MODE_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, Buttons
from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
from openpilot.selfdrive.car import create_button_events, get_safety_config
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.disable_ecu import disable_ecu
from openpilot.common.params import Params
params_memory = Params("/dev/shm/params")
Ecu = car.CarParams.Ecu
SafetyModel = car.CarParams.SafetyModel
ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName
ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: ButtonType.decelCruise,
Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel}
def set_safety_config_hyundai(candidate, CAN, can_fd=False):
platform = SafetyModel.hyundaiCanfd if can_fd else \
SafetyModel.hyundaiLegacy if candidate in LEGACY_SAFETY_MODE_CAR else \
SafetyModel.hyundai
cfgs = [get_safety_config(platform), ]
if CAN.ECAN >= 4:
cfgs.insert(0, get_safety_config(SafetyModel.noOutput))
return cfgs
class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
ret.carName = "hyundai"
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None
params_memory.put_bool("CSLCAvailable", True)
# These cars have been put into dashcam only due to both a lack of users and test coverage.
# These cars likely still work fine. Once a user confirms each car works and a test route is
# added to selfdrive/car/tests/routes.py, we can remove it from this list.
# FIXME: the Optima Hybrid 2017 uses a different SCC12 checksum
ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, }
hda2 = Ecu.adas in [fw.ecu for fw in car_fw]
CAN = CanBus(None, hda2, fingerprint)
if candidate in CANFD_CAR:
# detect HDA2 with ADAS Driving ECU
if hda2:
if 0x110 in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
params_memory.put_bool("CSLCAvailable", False)
else:
# non-HDA2
if 0x1cf not in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
# ICE cars do not have 0x130; GEARS message on 0x40 or 0x70 instead
if 0x130 not in fingerprint[CAN.ECAN]:
if 0x40 not in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlags.CANFD_ALT_GEARS_2.value
else:
ret.flags |= HyundaiFlags.CANFD_ALT_GEARS.value
if candidate not in CANFD_RADAR_SCC_CAR:
ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value
else:
# Send LFA message on cars with HDA
if 0x485 in fingerprint[2]:
ret.flags |= HyundaiFlags.SEND_LFA.value
# These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
if 0x38d in fingerprint[0] or 0x38d in fingerprint[2]:
ret.flags |= HyundaiFlags.USE_FCA.value
ret.steerActuatorDelay = 0.1 # Default delay
ret.steerLimitTimer = 0.4
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate in (CAR.AZERA_6TH_GEN, CAR.AZERA_HEV_6TH_GEN):
ret.mass = 1600. if candidate == CAR.AZERA_6TH_GEN else 1675. # ICE is ~average of 2.5L and 3.5L
ret.wheelbase = 2.885
ret.steerRatio = 14.5
elif candidate in (CAR.SANTA_FE, CAR.SANTA_FE_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022):
ret.mass = 3982. * CV.LB_TO_KG
ret.wheelbase = 2.766
# Values from optimizer
ret.steerRatio = 16.55 # 13.8 is spec end-to-end
ret.tireStiffnessFactor = 0.82
elif candidate in (CAR.SONATA, CAR.SONATA_HYBRID):
ret.mass = 1513.
ret.wheelbase = 2.84
ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.SONATA_LF:
ret.mass = 1536.
ret.wheelbase = 2.804
ret.steerRatio = 13.27 * 1.15 # 15% higher at the center seems reasonable
elif candidate == CAR.PALISADE:
ret.mass = 1999.
ret.wheelbase = 2.90
ret.steerRatio = 15.6 * 1.15
ret.tireStiffnessFactor = 0.63
elif candidate in (CAR.ELANTRA, CAR.ELANTRA_GT_I30):
ret.mass = 1275.
ret.wheelbase = 2.7
ret.steerRatio = 15.4 # 14 is Stock | Settled Params Learner values are steerRatio: 15.401566348670535
ret.tireStiffnessFactor = 0.385 # stiffnessFactor settled on 1.0081302973865127
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate == CAR.ELANTRA_2021:
ret.mass = 2800. * CV.LB_TO_KG
ret.wheelbase = 2.72
ret.steerRatio = 12.9
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.ELANTRA_HEV_2021:
ret.mass = 3017. * CV.LB_TO_KG
ret.wheelbase = 2.72
ret.steerRatio = 12.9
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.HYUNDAI_GENESIS:
ret.mass = 2060.
ret.wheelbase = 3.01
ret.steerRatio = 16.5
ret.minSteerSpeed = 60 * CV.KPH_TO_MS
elif candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.KONA_EV_2ND_GEN):
ret.mass = {CAR.KONA_EV: 1685., CAR.KONA_HEV: 1425., CAR.KONA_EV_2022: 1743., CAR.KONA_EV_2ND_GEN: 1740.}.get(candidate, 1275.)
ret.wheelbase = {CAR.KONA_EV_2ND_GEN: 2.66, }.get(candidate, 2.6)
ret.steerRatio = {CAR.KONA_EV_2ND_GEN: 13.6, }.get(candidate, 13.42) # Spec
ret.tireStiffnessFactor = 0.385
elif candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019, CAR.IONIQ_HEV_2022, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV):
ret.mass = 1490. # weight per hyundai site https://www.hyundaiusa.com/ioniq-electric/specifications.aspx
ret.wheelbase = 2.7
ret.steerRatio = 13.73 # Spec
ret.tireStiffnessFactor = 0.385
if candidate in (CAR.IONIQ, CAR.IONIQ_EV_LTD, CAR.IONIQ_PHEV_2019):
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate in (CAR.IONIQ_5, CAR.IONIQ_6):
ret.mass = 1948
ret.wheelbase = 2.97
ret.steerRatio = 14.26
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.VELOSTER:
ret.mass = 2917. * CV.LB_TO_KG
ret.wheelbase = 2.80
ret.steerRatio = 13.75 * 1.15
ret.tireStiffnessFactor = 0.5
elif candidate == CAR.TUCSON:
ret.mass = 3520. * CV.LB_TO_KG
ret.wheelbase = 2.67
ret.steerRatio = 14.00 * 1.15
ret.tireStiffnessFactor = 0.385
elif candidate in (CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN):
ret.steerActuatorDelay = 0.1 # Or .15? BBOT test oscarpilot
ret.mass = 1630. # average
ret.wheelbase = 2.756
ret.steerRatio = 16.
ret.tireStiffnessFactor = 0.385
elif candidate == CAR.SANTA_CRUZ_1ST_GEN:
ret.mass = 1870. # weight from Limited trim - the only supported trim
ret.wheelbase = 3.000
# steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf
ret.steerRatio = 14.2
elif candidate == CAR.CUSTIN_1ST_GEN:
ret.mass = 1690. # from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0
ret.wheelbase = 3.055
ret.steerRatio = 17.0 # from learner
elif candidate == CAR.STARIA_4TH_GEN:
ret.mass = 2205.
ret.wheelbase = 3.273
ret.steerRatio = 11.94 # https://www.hyundai.com/content/dam/hyundai/au/en/models/staria-load/premium-pip-update-2023/spec-sheet/STARIA_Load_Spec-Table_March_2023_v3.1.pdf
# Kia
elif candidate == CAR.KIA_SORENTO:
ret.mass = 1985.
ret.wheelbase = 2.78
ret.steerRatio = 14.4 * 1.1 # 10% higher at the center seems reasonable
elif candidate in (CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_PHEV_2022):
ret.mass = 3543. * CV.LB_TO_KG # average of all the cars
ret.wheelbase = 2.7
ret.steerRatio = 13.6 # average of all the cars
ret.tireStiffnessFactor = 0.385
if candidate == CAR.KIA_NIRO_PHEV:
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate == CAR.KIA_SELTOS:
ret.mass = 1337.
ret.wheelbase = 2.63
ret.steerRatio = 14.56
elif candidate == CAR.KIA_SPORTAGE_5TH_GEN:
ret.mass = 1700. # weight from SX and above trims, average of FWD and AWD versions
ret.wheelbase = 2.756
ret.steerRatio = 13.6 # steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sportage/2023/specifications
elif candidate in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL):
ret.mass = 3558. * CV.LB_TO_KG
ret.wheelbase = 2.80
ret.steerRatio = 13.75
ret.tireStiffnessFactor = 0.5
if candidate == CAR.KIA_OPTIMA_G4:
ret.minSteerSpeed = 32 * CV.MPH_TO_MS
elif candidate in (CAR.KIA_STINGER, CAR.KIA_STINGER_2022):
ret.mass = 1825.
ret.wheelbase = 2.78
ret.steerRatio = 14.4 * 1.15 # 15% higher at the center seems reasonable
elif candidate == CAR.KIA_FORTE:
ret.mass = 2878. * CV.LB_TO_KG
ret.wheelbase = 2.80
ret.steerRatio = 13.75
ret.tireStiffnessFactor = 0.5
elif candidate == CAR.KIA_CEED:
ret.mass = 1450.
ret.wheelbase = 2.65
ret.steerRatio = 13.75
ret.tireStiffnessFactor = 0.5
elif candidate in (CAR.KIA_K5_2021, CAR.KIA_K5_HEV_2020):
ret.mass = 3381. * CV.LB_TO_KG
ret.wheelbase = 2.85
ret.steerRatio = 13.27 # 2021 Kia K5 Steering Ratio (all trims)
ret.tireStiffnessFactor = 0.5
elif candidate == CAR.KIA_EV6:
ret.mass = 2055
ret.wheelbase = 2.9
ret.steerRatio = 16.
ret.tireStiffnessFactor = 0.65
elif candidate == CAR.KIA_SPORTAGE_HYBRID_5TH_GEN:
ret.mass = 1767. # SX Prestige trim support only
ret.wheelbase = 2.756
ret.steerRatio = 13.6
elif candidate in (CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN):
ret.wheelbase = 2.81
ret.steerRatio = 13.5 # average of the platforms
if candidate == CAR.KIA_SORENTO_4TH_GEN:
ret.mass = 3957 * CV.LB_TO_KG
elif candidate == CAR.KIA_SORENTO_HEV_4TH_GEN:
ret.mass = 4255 * CV.LB_TO_KG
else:
ret.mass = 4537 * CV.LB_TO_KG
elif candidate == CAR.KIA_CARNIVAL_4TH_GEN:
ret.mass = 2087.
ret.wheelbase = 3.09
ret.steerRatio = 14.23
elif candidate == CAR.KIA_K8_HEV_1ST_GEN:
ret.mass = 1630. # https://carprices.ae/brands/kia/2023/k8/1.6-turbo-hybrid
ret.wheelbase = 2.895
ret.steerRatio = 13.27 # guesstimate from K5 platform
# Genesis
elif candidate == CAR.GENESIS_GV60_EV_1ST_GEN:
ret.mass = 2205
ret.wheelbase = 2.9
# https://www.motor1.com/reviews/586376/2023-genesis-gv60-first-drive/#:~:text=Relative%20to%20the%20related%20Ioniq,5%2FEV6%27s%2014.3%3A1.
ret.steerRatio = 12.6
elif candidate == CAR.GENESIS_G70:
ret.steerActuatorDelay = 0.1
ret.mass = 1640.0
ret.wheelbase = 2.84
ret.steerRatio = 13.56
elif candidate == CAR.GENESIS_G70_2020:
ret.mass = 3673.0 * CV.LB_TO_KG
ret.wheelbase = 2.83
ret.steerRatio = 12.9
elif candidate == CAR.GENESIS_GV70_1ST_GEN:
ret.mass = 1950.
ret.wheelbase = 2.87
ret.steerRatio = 14.6
elif candidate == CAR.GENESIS_G80:
ret.mass = 2060.
ret.wheelbase = 3.01
ret.steerRatio = 16.5
elif candidate == CAR.GENESIS_G90:
ret.mass = 2200.
ret.wheelbase = 3.15
ret.steerRatio = 12.069
elif candidate == CAR.GENESIS_GV80:
ret.mass = 2258.
ret.wheelbase = 2.95
ret.steerRatio = 14.14
# *** longitudinal control ***
if candidate in CANFD_CAR:
ret.longitudinalTuning.kpV = [0.1]
ret.longitudinalTuning.kiV = [0.0]
ret.experimentalLongitudinalAvailable = candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR)
else:
ret.longitudinalTuning.kpV = [0.5]
ret.longitudinalTuning.kiV = [0.0]
ret.experimentalLongitudinalAvailable = candidate not in (UNSUPPORTED_LONGITUDINAL_CAR | CAMERA_SCC_CAR)
ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable
ret.pcmCruise = not ret.openpilotLongitudinalControl
ret.stoppingControl = True
ret.startingState = True
ret.vEgoStarting = 0.1
ret.startAccel = 1.0
ret.longitudinalActuatorDelayLowerBound = 0.5
ret.longitudinalActuatorDelayUpperBound = 0.5
# *** feature detection ***
# if candidate in CANFD_CAR:
# ret.enableBsm = 0x1e5 in fingerprint[CAN.ECAN]
# ret.nav_msg = 0x544 in fingerprint[0]
# else:
# ret.enableBsm = 0x58b in fingerprint[0]
# ret.nav_msg = False
# *** panda safety config ***
ret.safetyConfigs = set_safety_config_hyundai(candidate, CAN, can_fd=(candidate in CANFD_CAR))
if hda2:
ret.flags |= HyundaiFlags.CANFD_HDA2.value
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2
if candidate in CANFD_CAR:
if hda2 and ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING
if ret.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CANFD_ALT_BUTTONS
if ret.flags & HyundaiFlags.CANFD_CAMERA_SCC or candidate in CAMERA_SCC_CAR:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG
params_memory.put_bool("CSLCAvailable", False)
if candidate in HYBRID_CAR:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_HYBRID_GAS
elif candidate in EV_CAR:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_EV_GAS
if candidate in (CAR.KONA, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022):
ret.flags |= HyundaiFlags.ALT_LIMITS.value
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_ALT_LIMITS
ret.centerToFront = ret.wheelbase * 0.4
return ret
@staticmethod
def init(CP, logcan, sendcan):
if CP.openpilotLongitudinalControl and not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value):
addr, bus = 0x7d0, 0
if CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, CanBus(CP).ECAN
disable_ecu(logcan, sendcan, bus=bus, addr=addr, com_cont_req=b'\x28\x83\x01')
# for blinkers
if CP.flags & HyundaiFlags.ENABLE_BLINKERS:
disable_ecu(logcan, sendcan, bus=CanBus(CP).ECAN, addr=0x7B1, com_cont_req=b'\x28\x83\x01')
def _update(self, c):
ret = self.CS.update(self.cp, self.cp_cam)
if self.CS.CP.openpilotLongitudinalControl:
ret.buttonEvents = create_button_events(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT)
# On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state
# To avoid re-engaging when openpilot cancels, check user engagement intention via buttons
# Main button also can trigger an engagement on these cars
allow_enable = any(btn in ENABLE_BUTTONS for btn in self.CS.cruise_buttons) or any(self.CS.main_buttons)
events = self.create_common_events(ret, pcm_enable=self.CS.CP.pcmCruise, allow_enable=allow_enable)
# low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s)
if ret.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.:
self.low_speed_alert = True
if ret.vEgo > (self.CP.minSteerSpeed + 4.):
self.low_speed_alert = False
if self.low_speed_alert:
events.add(car.CarEvent.EventName.belowSteerSpeed)
ret.events = events.to_msg()
return ret
def apply(self, c, now_nanos, sport_plus):
return self.CC.update(c, self.CS, now_nanos, sport_plus)

View File

@@ -0,0 +1,79 @@
import math
from cereal import car
from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.interfaces import RadarInterfaceBase
from openpilot.selfdrive.car.hyundai.values import DBC
RADAR_START_ADDR = 0x500
RADAR_MSG_COUNT = 32
# POC for parsing corner radars: https://github.com/commaai/openpilot/pull/24221/
def get_radar_can_parser(CP):
if DBC[CP.carFingerprint]['radar'] is None:
return None
messages = [(f"RADAR_TRACK_{addr:x}", 50) for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT)]
return CANParser(DBC[CP.carFingerprint]['radar'], messages, 1)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.updated_messages = set()
self.trigger_msg = RADAR_START_ADDR + RADAR_MSG_COUNT - 1
self.track_id = 0
self.radar_off_can = CP.radarUnavailable
self.rcp = get_radar_can_parser(CP)
def update(self, can_strings):
if self.radar_off_can or (self.rcp is None):
return super().update(None)
vls = self.rcp.update_strings(can_strings)
self.updated_messages.update(vls)
if self.trigger_msg not in self.updated_messages:
return None
rr = self._update(self.updated_messages)
self.updated_messages.clear()
return rr
def _update(self, updated_messages):
ret = car.RadarData.new_message()
if self.rcp is None:
return ret
errors = []
if not self.rcp.can_valid:
errors.append("canError")
ret.errors = errors
for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT):
msg = self.rcp.vl[f"RADAR_TRACK_{addr:x}"]
if addr not in self.pts:
self.pts[addr] = car.RadarData.RadarPoint.new_message()
self.pts[addr].trackId = self.track_id
self.track_id += 1
valid = msg['STATE'] in (3, 4)
if valid:
azimuth = math.radians(msg['AZIMUTH'])
self.pts[addr].measured = True
self.pts[addr].dRel = math.cos(azimuth) * msg['LONG_DIST']
self.pts[addr].yRel = 0.5 * -math.sin(azimuth) * msg['LONG_DIST']
self.pts[addr].vRel = msg['REL_SPEED']
self.pts[addr].aRel = msg['REL_ACCEL']
self.pts[addr].yvRel = float('nan')
else:
del self.pts[addr]
ret.points = list(self.pts.values())
return ret

View File

@@ -0,0 +1,591 @@
import re
from dataclasses import dataclass
from enum import Enum, IntFlag, StrEnum
from typing import Dict, List, Optional, Set, Tuple, Union
from cereal import car
from panda.python import uds
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car import dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16
Ecu = car.CarParams.Ecu
class CarControllerParams:
ACCEL_MIN = -3.5 # m/s
ACCEL_MAX = 2.0 # m/s
ACCEL_MAX_PLUS = 4.0 # m/s
def __init__(self, CP):
self.STEER_DELTA_UP = 3
self.STEER_DELTA_DOWN = 7
self.STEER_DRIVER_ALLOWANCE = 50
self.STEER_DRIVER_MULTIPLIER = 2
self.STEER_DRIVER_FACTOR = 1
self.STEER_THRESHOLD = 150
self.STEER_STEP = 1 # 100 Hz
if CP.carFingerprint in CANFD_CAR:
self.STEER_MAX = 270
self.STEER_DRIVER_ALLOWANCE = 250
self.STEER_DRIVER_MULTIPLIER = 2
self.STEER_THRESHOLD = 250
self.STEER_DELTA_UP = 2
self.STEER_DELTA_DOWN = 3
# To determine the limit for your car, find the maximum value that the stock LKAS will request.
# If the max stock LKAS request is <384, add your car to this list.
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.GENESIS_G90, CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.IONIQ,
CAR.IONIQ_EV_LTD, CAR.SANTA_FE_PHEV_2022, CAR.SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV,
CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.KIA_SORENTO):
self.STEER_MAX = 255
# these cars have significantly more torque than most HKG; limit to 70% of max
elif CP.flags & HyundaiFlags.ALT_LIMITS:
self.STEER_MAX = 270
self.STEER_DELTA_UP = 2
self.STEER_DELTA_DOWN = 3
# Default for most HKG
else:
self.STEER_MAX = 384
class HyundaiFlags(IntFlag):
CANFD_HDA2 = 1
CANFD_ALT_BUTTONS = 2
CANFD_ALT_GEARS = 4
CANFD_CAMERA_SCC = 8
ALT_LIMITS = 16
ENABLE_BLINKERS = 32
CANFD_ALT_GEARS_2 = 64
SEND_LFA = 128
USE_FCA = 256
CANFD_HDA2_ALT_STEERING = 512
class CAR(StrEnum):
# Hyundai
AZERA_6TH_GEN = "HYUNDAI AZERA 6TH GEN"
AZERA_HEV_6TH_GEN = "HYUNDAI AZERA HYBRID 6TH GEN"
ELANTRA = "HYUNDAI ELANTRA 2017"
ELANTRA_GT_I30 = "HYUNDAI I30 N LINE 2019 & GT 2018 DCT"
ELANTRA_2021 = "HYUNDAI ELANTRA 2021"
ELANTRA_HEV_2021 = "HYUNDAI ELANTRA HYBRID 2021"
HYUNDAI_GENESIS = "HYUNDAI GENESIS 2015-2016"
IONIQ = "HYUNDAI IONIQ HYBRID 2017-2019"
IONIQ_HEV_2022 = "HYUNDAI IONIQ HYBRID 2020-2022"
IONIQ_EV_LTD = "HYUNDAI IONIQ ELECTRIC LIMITED 2019"
IONIQ_EV_2020 = "HYUNDAI IONIQ ELECTRIC 2020"
IONIQ_PHEV_2019 = "HYUNDAI IONIQ PLUG-IN HYBRID 2019"
IONIQ_PHEV = "HYUNDAI IONIQ PHEV 2020"
KONA = "HYUNDAI KONA 2020"
KONA_EV = "HYUNDAI KONA ELECTRIC 2019"
KONA_EV_2022 = "HYUNDAI KONA ELECTRIC 2022"
KONA_EV_2ND_GEN = "HYUNDAI KONA ELECTRIC 2ND GEN"
KONA_HEV = "HYUNDAI KONA HYBRID 2020"
SANTA_FE = "HYUNDAI SANTA FE 2019"
SANTA_FE_2022 = "HYUNDAI SANTA FE 2022"
SANTA_FE_HEV_2022 = "HYUNDAI SANTA FE HYBRID 2022"
SANTA_FE_PHEV_2022 = "HYUNDAI SANTA FE PlUG-IN HYBRID 2022"
SONATA = "HYUNDAI SONATA 2020"
SONATA_LF = "HYUNDAI SONATA 2019"
STARIA_4TH_GEN = "HYUNDAI STARIA 4TH GEN"
TUCSON = "HYUNDAI TUCSON 2019"
PALISADE = "HYUNDAI PALISADE 2020"
VELOSTER = "HYUNDAI VELOSTER 2019"
SONATA_HYBRID = "HYUNDAI SONATA HYBRID 2021"
IONIQ_5 = "HYUNDAI IONIQ 5 2022"
IONIQ_6 = "HYUNDAI IONIQ 6 2023"
TUCSON_4TH_GEN = "HYUNDAI TUCSON 4TH GEN"
TUCSON_HYBRID_4TH_GEN = "HYUNDAI TUCSON HYBRID 4TH GEN"
SANTA_CRUZ_1ST_GEN = "HYUNDAI SANTA CRUZ 1ST GEN"
CUSTIN_1ST_GEN = "HYUNDAI CUSTIN 1ST GEN"
# Kia
KIA_FORTE = "KIA FORTE E 2018 & GT 2021"
KIA_K5_2021 = "KIA K5 2021"
KIA_K5_HEV_2020 = "KIA K5 HYBRID 2020"
KIA_K8_HEV_1ST_GEN = "KIA K8 HYBRID 1ST GEN"
KIA_NIRO_EV = "KIA NIRO EV 2020"
KIA_NIRO_EV_2ND_GEN = "KIA NIRO EV 2ND GEN"
KIA_NIRO_PHEV = "KIA NIRO HYBRID 2019"
KIA_NIRO_PHEV_2022 = "KIA NIRO PLUG-IN HYBRID 2022"
KIA_NIRO_HEV_2021 = "KIA NIRO HYBRID 2021"
KIA_NIRO_HEV_2ND_GEN = "KIA NIRO HYBRID 2ND GEN"
KIA_OPTIMA_G4 = "KIA OPTIMA 4TH GEN"
KIA_OPTIMA_G4_FL = "KIA OPTIMA 4TH GEN FACELIFT"
KIA_OPTIMA_H = "KIA OPTIMA HYBRID 2017 & SPORTS 2019"
KIA_OPTIMA_H_G4_FL = "KIA OPTIMA HYBRID 4TH GEN FACELIFT"
KIA_SELTOS = "KIA SELTOS 2021"
KIA_SPORTAGE_5TH_GEN = "KIA SPORTAGE 5TH GEN"
KIA_SORENTO = "KIA SORENTO GT LINE 2018"
KIA_SORENTO_4TH_GEN = "KIA SORENTO 4TH GEN"
KIA_SORENTO_HEV_4TH_GEN = "KIA SORENTO HYBRID 4TH GEN"
KIA_SORENTO_PHEV_4TH_GEN = "KIA SORENTO PLUG-IN HYBRID 4TH GEN"
KIA_SPORTAGE_HYBRID_5TH_GEN = "KIA SPORTAGE HYBRID 5TH GEN"
KIA_STINGER = "KIA STINGER GT2 2018"
KIA_STINGER_2022 = "KIA STINGER 2022"
KIA_CEED = "KIA CEED INTRO ED 2019"
KIA_EV6 = "KIA EV6 2022"
KIA_CARNIVAL_4TH_GEN = "KIA CARNIVAL 4TH GEN"
# Genesis
GENESIS_GV60_EV_1ST_GEN = "GENESIS GV60 ELECTRIC 1ST GEN"
GENESIS_G70 = "GENESIS G70 2018"
GENESIS_G70_2020 = "GENESIS G70 2020"
GENESIS_GV70_1ST_GEN = "GENESIS GV70 1ST GEN"
GENESIS_G80 = "GENESIS G80 2017"
GENESIS_G90 = "GENESIS G90 2017"
GENESIS_GV80 = "GENESIS GV80 2023"
class Footnote(Enum):
CANFD = CarFootnote(
"Requires a <a href=\"https://comma.ai/shop/can-fd-panda-kit\" target=\"_blank\">CAN FD panda kit</a> if not using " +
"comma 3X for this <a href=\"https://en.wikipedia.org/wiki/CAN_FD\" target=\"_blank\">CAN FD car</a>.",
Column.MODEL, shop_footnote=False)
@dataclass
class HyundaiCarInfo(CarInfo):
package: str = "Smart Cruise Control (SCC)"
def init_make(self, CP: car.CarParams):
if CP.carFingerprint in CANFD_CAR:
self.footnotes.insert(0, Footnote.CANFD)
CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = {
CAR.AZERA_6TH_GEN: HyundaiCarInfo("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.AZERA_HEV_6TH_GEN: [
HyundaiCarInfo("Hyundai Azera Hybrid 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Hyundai Azera Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CAR.ELANTRA: [
# TODO: 2017-18 could be Hyundai G
HyundaiCarInfo("Hyundai Elantra 2017-18", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])),
HyundaiCarInfo("Hyundai Elantra 2019", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_g])),
],
CAR.ELANTRA_GT_I30: [
HyundaiCarInfo("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c",
car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.HYUNDAI_GENESIS: [
# TODO: check 2015 packages
HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])),
HyundaiCarInfo("Genesis G80 2017", "All", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])),
],
CAR.IONIQ: HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.IONIQ_HEV_2022: HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_h])), # TODO: confirm 2020-21 harness
CAR.IONIQ_EV_LTD: HyundaiCarInfo("Hyundai Ioniq Electric 2019", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.IONIQ_EV_2020: HyundaiCarInfo("Hyundai Ioniq Electric 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CAR.IONIQ_PHEV_2019: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.IONIQ_PHEV: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CAR.KONA: HyundaiCarInfo("Hyundai Kona 2020", car_parts=CarParts.common([CarHarness.hyundai_b])),
CAR.KONA_EV: HyundaiCarInfo("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g])),
CAR.KONA_EV_2022: HyundaiCarInfo("Hyundai Kona Electric 2022-23", car_parts=CarParts.common([CarHarness.hyundai_o])),
CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages
# TODO: this is the 2024 US MY, not yet released
CAR.KONA_EV_2ND_GEN: HyundaiCarInfo("Hyundai Kona Electric (with HDA II, Korea only) 2023", video_link="https://www.youtube.com/watch?v=U2fOCmcQ8hw",
car_parts=CarParts.common([CarHarness.hyundai_r])),
CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", video_link="https://youtu.be/bjDR0YjM__s",
car_parts=CarParts.common([CarHarness.hyundai_d])),
CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-23", "All", video_link="https://youtu.be/VnHzSTygTS4",
car_parts=CarParts.common([CarHarness.hyundai_l])),
CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw",
car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.STARIA_4TH_GEN: HyundaiCarInfo("Hyundai Staria 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e])),
CAR.TUCSON: [
HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Hyundai Tucson Diesel 2019", car_parts=CarParts.common([CarHarness.hyundai_l])),
],
CAR.PALISADE: [
HyundaiCarInfo("Hyundai Palisade 2020-22", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", car_parts=CarParts.common([CarHarness.hyundai_h])),
HyundaiCarInfo("Kia Telluride 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
],
CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e])),
CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.IONIQ_5: [
HyundaiCarInfo("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_q])),
HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])),
HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
],
CAR.IONIQ_6: [
HyundaiCarInfo("Hyundai Ioniq 6 (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])),
],
CAR.TUCSON_4TH_GEN: [
HyundaiCarInfo("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])),
HyundaiCarInfo("Hyundai Tucson 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_n])),
CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2022-23", car_parts=CarParts.common([CarHarness.hyundai_n])),
CAR.CUSTIN_1ST_GEN: HyundaiCarInfo("Hyundai Custin 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
# Kia
CAR.KIA_FORTE: [
HyundaiCarInfo("Kia Forte 2019-21", car_parts=CarParts.common([CarHarness.hyundai_g])),
HyundaiCarInfo("Kia Forte 2023", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-24", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_K5_HEV_2020: HyundaiCarInfo("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_K8_HEV_1ST_GEN: HyundaiCarInfo("Kia K8 Hybrid (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
CAR.KIA_NIRO_EV: [
HyundaiCarInfo("Kia Niro EV 2019", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])),
HyundaiCarInfo("Kia Niro EV 2020", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_f])),
HyundaiCarInfo("Kia Niro EV 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Kia Niro EV 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])),
],
CAR.KIA_NIRO_EV_2ND_GEN: HyundaiCarInfo("Kia Niro EV 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_NIRO_PHEV: [
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_d])),
],
CAR.KIA_NIRO_PHEV_2022: HyundaiCarInfo("Kia Niro Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
CAR.KIA_NIRO_HEV_2021: [
HyundaiCarInfo("Kia Niro Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_d])),
HyundaiCarInfo("Kia Niro Hybrid 2022", car_parts=CarParts.common([CarHarness.hyundai_f])),
],
CAR.KIA_NIRO_HEV_2ND_GEN: HyundaiCarInfo("Kia Niro Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_OPTIMA_G4: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control",
car_parts=CarParts.common([CarHarness.hyundai_b])), # TODO: may support 2016, 2018
CAR.KIA_OPTIMA_G4_FL: HyundaiCarInfo("Kia Optima 2019-20", car_parts=CarParts.common([CarHarness.hyundai_g])),
# TODO: may support adjacent years. may have a non-zero minimum steering speed
CAR.KIA_OPTIMA_H: HyundaiCarInfo("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.KIA_OPTIMA_H_G4_FL: HyundaiCarInfo("Kia Optima Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_h])),
CAR.KIA_SELTOS: HyundaiCarInfo("Kia Seltos 2021", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_SPORTAGE_5TH_GEN: HyundaiCarInfo("Kia Sportage 2023", car_parts=CarParts.common([CarHarness.hyundai_n])),
CAR.KIA_SORENTO: [
HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control & LKAS", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8",
car_parts=CarParts.common([CarHarness.hyundai_e])),
HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.KIA_SORENTO_HEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])),
CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0",
car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.KIA_STINGER_2022: HyundaiCarInfo("Kia Stinger 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", car_parts=CarParts.common([CarHarness.hyundai_e])),
CAR.KIA_EV6: [
HyundaiCarInfo("Kia EV6 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarInfo("Kia EV6 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Kia EV6 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CAR.KIA_CARNIVAL_4TH_GEN: [
HyundaiCarInfo("Kia Carnival 2022-24", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarInfo("Kia Carnival (China only) 2023", car_parts=CarParts.common([CarHarness.hyundai_k]))
],
# Genesis
CAR.GENESIS_GV60_EV_1ST_GEN: [
HyundaiCarInfo("Genesis GV60 (Advanced Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarInfo("Genesis GV60 (Performance Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_f])),
CAR.GENESIS_GV70_1ST_GEN: [
HyundaiCarInfo("Genesis GV70 (2.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarInfo("Genesis GV70 (3.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
],
CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_h])),
CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2017-18", "All", car_parts=CarParts.common([CarHarness.hyundai_c])),
CAR.GENESIS_GV80: HyundaiCarInfo("Genesis GV80 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
}
class Buttons:
NONE = 0
RES_ACCEL = 1
SET_DECEL = 2
GAP_DIST = 3
CANCEL = 4 # on newer models, this is a pause/resume button
def get_platform_codes(fw_versions: List[bytes]) -> Set[Tuple[bytes, Optional[bytes]]]:
# Returns unique, platform-specific identification codes for a set of versions
codes = set() # (code-Optional[part], date)
for fw in fw_versions:
code_match = PLATFORM_CODE_FW_PATTERN.search(fw)
part_match = PART_NUMBER_FW_PATTERN.search(fw)
date_match = DATE_FW_PATTERN.search(fw)
if code_match is not None:
code: bytes = code_match.group()
part = part_match.group() if part_match else None
date = date_match.group() if date_match else None
if part is not None:
# part number starts with generic ECU part type, add what is specific to platform
code += b"-" + part[-5:]
codes.add((code, date))
return codes
def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> Set[str]:
# Non-electric CAN FD platforms often do not have platform code specifiers needed
# to distinguish between hybrid and ICE. All EVs so far are either exclusively
# electric or specify electric in the platform code.
# TODO: whitelist platforms that we've seen hybrid and ICE versions of that have these specifiers
fuzzy_platform_blacklist = {str(c) for c in set(CANFD_CAR - EV_CAR)}
candidates: Set[str] = set()
for candidate, fws in offline_fw_versions.items():
# Keep track of ECUs which pass all checks (platform codes, within date range)
valid_found_ecus = set()
valid_expected_ecus = {ecu[1:] for ecu in fws if ecu[0] in PLATFORM_CODE_ECUS}
for ecu, expected_versions in fws.items():
addr = ecu[1:]
# Only check ECUs expected to have platform codes
if ecu[0] not in PLATFORM_CODE_ECUS:
continue
# Expected platform codes & dates
codes = get_platform_codes(expected_versions)
expected_platform_codes = {code for code, _ in codes}
expected_dates = {date for _, date in codes if date is not None}
# Found platform codes & dates
codes = get_platform_codes(live_fw_versions.get(addr, set()))
found_platform_codes = {code for code, _ in codes}
found_dates = {date for _, date in codes if date is not None}
# Check platform code + part number matches for any found versions
if not any(found_platform_code in expected_platform_codes for found_platform_code in found_platform_codes):
break
if ecu[0] in DATE_FW_ECUS:
# If ECU can have a FW date, require it to exist
# (this excludes candidates in the database without dates)
if not len(expected_dates) or not len(found_dates):
break
# Check any date within range in the database, format is %y%m%d
if not any(min(expected_dates) <= found_date <= max(expected_dates) for found_date in found_dates):
break
valid_found_ecus.add(addr)
# If all live ECUs pass all checks for candidate, add it as a match
if valid_expected_ecus.issubset(valid_found_ecus):
candidates.add(candidate)
return candidates - fuzzy_platform_blacklist
HYUNDAI_VERSION_REQUEST_LONG = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(0xf100) # Long description
HYUNDAI_VERSION_REQUEST_ALT = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(0xf110) # Alt long description
HYUNDAI_VERSION_REQUEST_MULTI = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_SPARE_PART_NUMBER) + \
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_SOFTWARE_IDENTIFICATION) + \
p16(0xf100)
HYUNDAI_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40])
# Regex patterns for parsing platform code, FW date, and part number from FW versions
PLATFORM_CODE_FW_PATTERN = re.compile(b'((?<=' + HYUNDAI_VERSION_REQUEST_LONG[1:] +
b')[A-Z]{2}[A-Za-z0-9]{0,2})')
DATE_FW_PATTERN = re.compile(b'(?<=[ -])([0-9]{6}$)')
PART_NUMBER_FW_PATTERN = re.compile(b'(?<=[0-9][.,][0-9]{2} )([0-9]{5}[-/]?[A-Z][A-Z0-9]{3}[0-9])')
# List of ECUs expected to have platform codes, camera and radar should exist on all cars
# TODO: use abs, it has the platform code and part number on many platforms
PLATFORM_CODE_ECUS = [Ecu.fwdRadar, Ecu.fwdCamera, Ecu.eps]
# So far we've only seen dates in fwdCamera
# TODO: there are date codes in the ABS firmware versions in hex
DATE_FW_ECUS = [Ecu.fwdCamera]
FW_QUERY_CONFIG = FwQueryConfig(
requests=[
# TODO: minimize shared whitelists for CAN and cornerRadar for CAN-FD
# CAN queries (OBD-II port)
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.transmission, Ecu.eps, Ecu.abs, Ecu.fwdRadar, Ecu.fwdCamera],
),
Request(
[HYUNDAI_VERSION_REQUEST_MULTI],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.engine, Ecu.transmission, Ecu.eps, Ecu.abs, Ecu.fwdRadar],
),
# CAN-FD queries (from camera)
# TODO: combine shared whitelists with CAN requests
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera, Ecu.fwdRadar, Ecu.cornerRadar, Ecu.hvac],
bus=0,
auxiliary=True,
),
Request(
[HYUNDAI_VERSION_REQUEST_LONG],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.fwdCamera, Ecu.adas, Ecu.cornerRadar, Ecu.hvac],
bus=1,
auxiliary=True,
obd_multiplexing=False,
),
# CAN-FD debugging queries
Request(
[HYUNDAI_VERSION_REQUEST_ALT],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.parkingAdas, Ecu.hvac],
bus=0,
auxiliary=True,
),
Request(
[HYUNDAI_VERSION_REQUEST_ALT],
[HYUNDAI_VERSION_RESPONSE],
whitelist_ecus=[Ecu.parkingAdas, Ecu.hvac],
bus=1,
auxiliary=True,
obd_multiplexing=False,
),
],
extra_ecus=[
(Ecu.adas, 0x730, None), # ADAS Driving ECU on HDA2 platforms
(Ecu.parkingAdas, 0x7b1, None), # ADAS Parking ECU (may exist on all platforms)
(Ecu.hvac, 0x7b3, None), # HVAC Control Assembly
(Ecu.cornerRadar, 0x7b7, None),
],
# Custom fuzzy fingerprinting function using platform codes, part numbers + FW dates:
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
)
CHECKSUM = {
"crc8": [CAR.SANTA_FE, CAR.SONATA, CAR.PALISADE, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.ELANTRA_HEV_2021,
CAR.SONATA_HYBRID, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022,
CAR.KIA_K5_HEV_2020, CAR.CUSTIN_1ST_GEN],
"6B": [CAR.KIA_SORENTO, CAR.HYUNDAI_GENESIS],
}
CAN_GEARS = {
# which message has the gear. hybrid and EV use ELECT_GEAR
"use_cluster_gears": {CAR.ELANTRA, CAR.ELANTRA_GT_I30, CAR.KONA},
"use_tcu_gears": {CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.SONATA_LF, CAR.VELOSTER, CAR.TUCSON},
}
CANFD_CAR = {CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN, CAR.KIA_SPORTAGE_HYBRID_5TH_GEN,
CAR.SANTA_CRUZ_1ST_GEN, CAR.KIA_SPORTAGE_5TH_GEN, CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN,
CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_NIRO_HEV_2ND_GEN, CAR.KIA_NIRO_EV_2ND_GEN,
CAR.GENESIS_GV80, CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KONA_EV_2ND_GEN, CAR.KIA_K8_HEV_1ST_GEN,
CAR.STARIA_4TH_GEN}
# The radar does SCC on these cars when HDA I, rather than the camera
CANFD_RADAR_SCC_CAR = {CAR.GENESIS_GV70_1ST_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_SORENTO_4TH_GEN, CAR.GENESIS_GV80,
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN}
# These CAN FD cars do not accept communication control to disable the ADAS ECU,
# responds with 0x7F2822 - 'conditions not correct'
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = {CAR.IONIQ_6, CAR.KONA_EV_2ND_GEN}
# The camera does SCC on these cars, rather than the radar
CAMERA_SCC_CAR = {CAR.KONA_EV_2022, }
# these cars use a different gas signal
HYBRID_CAR = {CAR.IONIQ_PHEV, CAR.ELANTRA_HEV_2021, CAR.KIA_NIRO_PHEV, CAR.KIA_NIRO_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_HEV, CAR.IONIQ,
CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, CAR.SANTA_FE_PHEV_2022, CAR.IONIQ_PHEV_2019, CAR.TUCSON_HYBRID_4TH_GEN,
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN, CAR.KIA_SORENTO_PHEV_4TH_GEN, CAR.KIA_K5_HEV_2020, CAR.KIA_NIRO_HEV_2ND_GEN,
CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.KIA_K8_HEV_1ST_GEN,
CAR.AZERA_HEV_6TH_GEN, CAR.KIA_NIRO_PHEV_2022}
EV_CAR = {CAR.IONIQ_EV_2020, CAR.IONIQ_EV_LTD, CAR.KONA_EV, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_EV_2ND_GEN, CAR.KONA_EV_2022,
CAR.KIA_EV6, CAR.IONIQ_5, CAR.IONIQ_6, CAR.GENESIS_GV60_EV_1ST_GEN, CAR.KONA_EV_2ND_GEN}
# these cars require a special panda safety mode due to missing counters and checksums in the messages
LEGACY_SAFETY_MODE_CAR = {CAR.HYUNDAI_GENESIS, CAR.IONIQ_EV_LTD, CAR.KIA_OPTIMA_G4,
CAR.VELOSTER, CAR.GENESIS_G70, CAR.GENESIS_G80, CAR.KIA_CEED, CAR.ELANTRA, CAR.IONIQ_HEV_2022,
CAR.KIA_OPTIMA_H, CAR.ELANTRA_GT_I30}
# these cars have not been verified to work with longitudinal yet - radar disable, sending correct messages, etc.
UNSUPPORTED_LONGITUDINAL_CAR = LEGACY_SAFETY_MODE_CAR | {CAR.KIA_NIRO_PHEV, CAR.KIA_SORENTO, CAR.SONATA_LF, CAR.KIA_OPTIMA_G4_FL,
CAR.KIA_OPTIMA_H_G4_FL}
# If 0x500 is present on bus 1 it probably has a Mando radar outputting radar points.
# If no points are outputted by default it might be possible to turn it on using selfdrive/debug/hyundai_enable_radar_points.py
DBC = {
CAR.AZERA_6TH_GEN: dbc_dict('hyundai_kia_generic', None),
CAR.AZERA_HEV_6TH_GEN: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_GT_I30: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_2021: dbc_dict('hyundai_kia_generic', None),
CAR.ELANTRA_HEV_2021: dbc_dict('hyundai_kia_generic', None),
CAR.GENESIS_G70: dbc_dict('hyundai_kia_generic', None),
CAR.GENESIS_G70_2020: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.GENESIS_G80: dbc_dict('hyundai_kia_generic', None),
CAR.GENESIS_G90: dbc_dict('hyundai_kia_generic', None),
CAR.HYUNDAI_GENESIS: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_PHEV_2019: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_PHEV: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_EV_2020: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_EV_LTD: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.IONIQ: dbc_dict('hyundai_kia_generic', None),
CAR.IONIQ_HEV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_FORTE: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_K5_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_K5_HEV_2020: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.KIA_NIRO_EV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.KIA_NIRO_PHEV: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.KIA_NIRO_HEV_2021: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_G4: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_G4_FL: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_H: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_OPTIMA_H_G4_FL: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_SELTOS: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_SORENTO: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format
CAR.KIA_STINGER: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_STINGER_2022: dbc_dict('hyundai_kia_generic', None),
CAR.KONA: dbc_dict('hyundai_kia_generic', None),
CAR.KONA_EV: dbc_dict('hyundai_kia_generic', None),
CAR.KONA_EV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.KONA_HEV: dbc_dict('hyundai_kia_generic', None),
CAR.SANTA_FE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.SANTA_FE_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SANTA_FE_HEV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SANTA_FE_PHEV_2022: dbc_dict('hyundai_kia_generic', None),
CAR.SONATA: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.SONATA_LF: dbc_dict('hyundai_kia_generic', None), # Has 0x5XX messages, but different format
CAR.TUCSON: dbc_dict('hyundai_kia_generic', None),
CAR.PALISADE: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.VELOSTER: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_CEED: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_EV6: dbc_dict('hyundai_canfd', None),
CAR.SONATA_HYBRID: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.TUCSON_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.TUCSON_HYBRID_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.IONIQ_5: dbc_dict('hyundai_canfd', None),
CAR.IONIQ_6: dbc_dict('hyundai_canfd', None),
CAR.SANTA_CRUZ_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SPORTAGE_5TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.GENESIS_GV70_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SORENTO_PHEV_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.GENESIS_GV60_EV_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SORENTO_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_NIRO_HEV_2ND_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_NIRO_EV_2ND_GEN: dbc_dict('hyundai_canfd', None),
CAR.GENESIS_GV80: dbc_dict('hyundai_canfd', None),
CAR.KIA_CARNIVAL_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_SORENTO_HEV_4TH_GEN: dbc_dict('hyundai_canfd', None),
CAR.KONA_EV_2ND_GEN: dbc_dict('hyundai_canfd', None),
CAR.KIA_K8_HEV_1ST_GEN: dbc_dict('hyundai_canfd', None),
CAR.CUSTIN_1ST_GEN: dbc_dict('hyundai_kia_generic', None),
CAR.KIA_NIRO_PHEV_2022: dbc_dict('hyundai_kia_generic', 'hyundai_kia_mando_front_radar_generated'),
CAR.STARIA_4TH_GEN: dbc_dict('hyundai_canfd', None),
}

View File

@@ -0,0 +1,332 @@
from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip
from openpilot.common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance
from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR, LEGACY_SAFETY_MODE_CAR
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
from openpilot.common.params import Params
from openpilot.common.watcher import Watcher
VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState
params_memory = Params("/dev/shm/params")
# EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second
# All slightly below EPS thresholds to avoid fault
MAX_ANGLE = 85
MAX_ANGLE_FRAMES = 89
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
def process_hud_alert(enabled, fingerprint, hud_control):
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
# initialize to no line visible
# TODO: this is not accurate for all cars
sys_state = 1
if hud_control.leftLaneVisible and hud_control.rightLaneVisible or sys_warning: # HUD alert only display when LKAS status is active
sys_state = 3 if enabled or sys_warning else 4
elif hud_control.leftLaneVisible:
sys_state = 5
elif hud_control.rightLaneVisible:
sys_state = 6
# initialize to no warnings
left_lane_warning = 0
right_lane_warning = 0
if hud_control.leftLaneDepart:
left_lane_warning = 1 if fingerprint in (CAR.GENESIS_G90, CAR.GENESIS_G80) else 2
if hud_control.rightLaneDepart:
right_lane_warning = 1 if fingerprint in (CAR.GENESIS_G90, CAR.GENESIS_G80) else 2
return sys_warning, sys_state, left_lane_warning, right_lane_warning
class CarController:
def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.CAN = CanBus(CP)
self.params = CarControllerParams(CP)
self.packer = CANPacker(dbc_name)
self.angle_limit_counter = 0
self.frame = 0
self.accel_last = 0
self.apply_steer_last = 0
self.car_fingerprint = CP.carFingerprint
self.last_button_frame = 0
self.last_resume_frame = 0
self.last_debug_frame = 0
def update(self, CC, CS, now_nanos, sport_plus):
actuators = CC.actuators
hud_control = CC.hudControl
# hud_v_cruise = hud_control.setSpeed
# if hud_v_cruise > 70:
# hud_v_cruise = 0
# steering torque
new_steer = int(round(actuators.steer * self.params.STEER_MAX))
apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
# >90 degree steering fault prevention
self.angle_limit_counter, apply_steer_req = common_fault_avoidance(abs(CS.out.steeringAngleDeg) >= MAX_ANGLE, CC.latActive,
self.angle_limit_counter, MAX_ANGLE_FRAMES,
MAX_ANGLE_CONSECUTIVE_FRAMES)
if not CC.latActive:
apply_steer = 0
# Hold torque with induced temporary fault when cutting the actuation bit
torque_fault = CC.latActive and not apply_steer_req
self.apply_steer_last = apply_steer
# accel + longitudinal
if sport_plus:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX_PLUS)
else:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
hud_control)
can_sends = []
# *** common hyundai stuff ***
# tester present - w/ no response (keeps relevant ECU disabled)
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and self.CP.openpilotLongitudinalControl:
# for longitudinal control, either radar or ADAS driving ECU
addr, bus = 0x7d0, 0
if self.CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, self.CAN.ECAN
can_sends.append([addr, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", bus])
# for blinkers
if self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.append([0x7b1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", self.CAN.ECAN])
# CAN-FD platforms
if self.CP.carFingerprint in CANFD_CAR:
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
hda2_long = hda2 and self.CP.openpilotLongitudinalControl
# steering control
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_steer))
# MODIFIED BBOT
if self.frame % 5 == 0 and hda2:
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.hda2_lfa_block_msg,
self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING))
# MODIFIED BBOT
# LFA and HDA icons
if self.frame % 5 == 0 and (not hda2 or hda2_long):
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled))
# blinkers
if hda2 and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, self.frame, CC.leftBlinker, CC.rightBlinker))
# can_sends.append(hyundaicanfd.create_misc_messages(self.packer, self.CAN, self.frame))
if self.CP.openpilotLongitudinalControl:
if hda2:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
if self.frame % 2 == 0:
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
set_speed_in_units, CS.personality_profile))
self.accel_last = accel
else:
# button presses
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False, set_speed_in_units = None))
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
if not self.CP.openpilotLongitudinalControl:
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=True))
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
# TODO: unclear if this is needed
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
hud_control.leadVisible, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, CS.out.cruiseState.available, CS.personality_profile))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
# 5 Hz ACC options
if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.extend(hyundaican.create_acc_opt(self.packer))
# 2 Hz front radar options
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
# CSLC
# if not self.CP.openpilotLongitudinalControl and CS.cruiseState.available and not CS.out.gasPressed and CS.cruise_buttons == Buttons.NONE:
# if False and not self.CP.openpilotLongitudinalControl and CC.enabled and CC.experimental_mode and CS.cruiseState.available and not CS.out.gasPressed and CS.cruise_buttons == Buttons.NONE:
# # cslcSetSpeed = get_set_speed(self, hud_v_cruise)
# cslcSetSpeed = set_speed_in_units
# self.cruise_button = get_cslc_button(self, cslcSetSpeed, CS)
# if self.cruise_button != Buttons.NONE:
# # if self.CP.carFingerprint in LEGACY_SAFETY_MODE_CAR:
# # send_freq = 1
# # # send resume at a max freq of 10Hz
# # if (self.frame - self.last_button_frame) * DT_CTRL > 0.1 * send_freq:
# # # send 25 messages at a time to increases the likelihood of cruise buttons being accepted
# # can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, self.cruise_button, self.CP.carFingerprint)] * 25)
# # if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15 * send_freq:
# # self.last_button_frame = self.frame
# if self.frame % 2 == 0:
# if self.CP.carFingerprint in CANFD_CAR:
# can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, ((self.frame // 2) + 1) % 0x10, self.cruise_button))
# else:
# can_sends.extend([hyundaican.create_clu11(self.packer, (self.frame // 2) + 1, CS.clu11, self.cruise_button, self.CP.carFingerprint)] * 25)
# Test - Works???
# if CS.cruise_buttons == Buttons.NONE and CS.cruiseState.enabled:
# can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, ((self.frame // 2) + 1) % 0x10, Buttons.SET_DECEL))
new_actuators = actuators.copy()
new_actuators.steer = apply_steer / self.params.STEER_MAX
new_actuators.steerOutputCan = apply_steer
new_actuators.accel = accel
Watcher.log_watch("hyundai_carcontroller_update_CC", CC)
Watcher.log_watch("hyundai_carcontroller_update_CS", CS)
Watcher.log_watch("hyundai_carcontroller_update_self", self)
self.frame += 1
return new_actuators, can_sends
def create_button_messages(self, CC: car.CarControl, CS: car.CarState, use_clu11: bool, set_speed_in_units = None):
can_sends = []
# Test me.
# can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, 1, Buttons.RES_ACCEL))
# if self.CP.openpilotLongitudinalControl:
# CC.cruiseControl.resume = True
# self.CP.openpilotLongitudinalControl = False
# else:
# CC.cruiseControl.cancel = True
# self.CP.openpilotLongitudinalControl = True
if use_clu11:
if CC.cruiseControl.cancel:
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP.carFingerprint))
elif CC.cruiseControl.resume:
# send resume at a max freq of 10Hz
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
# send 25 messages at a time to increases the likeli M,hood of resume being accepted
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP.carFingerprint)] * 25)
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
else:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# if (self.frame - self.last_button_frame) * DT_CTRL > 3:
# if self.last_resume_frame = self.frame
if CS.oscar_lane_center_btn_pressed:
CS.oscar_lane_center_btn_pressed = False
# CC.cruiseControl.resume = True
CC.cruiseControl.cancel = True
# Test this...
# Also try create_acc_commands
# This attempts to set the speed to
# stopping = CC.actuators.longControlState == LongCtrlState.stopping
# can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, CC.actuators.accel, stopping, CC.cruiseControl.override,
# 40, CS.personality_profile))
# cruise cancel
if CC.cruiseControl.cancel:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
self.last_button_frame = self.frame
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame
# cruise standstill resume
elif CC.cruiseControl.resume:
if (self.frame - self.last_button_frame) * DT_CTRL > 4:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
# for _ in range(20):
nothing = 0
# Nothing for now --?
# oscar - test me
# can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.CANCEL))
# can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.RES_ACCEL))
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
self.last_resume_frame = self.frame
elif set_speed_in_units is not None and not self.CP.openpilotLongitudinalControl and CS.cruiseState.enabled and not CS.out.gasPressed and CS.cruise_buttons == Buttons.NONE:
# if False and not self.CP.openpilotLongitudinalControl and CC.enabled and CC.experimental_mode and CS.cruiseState.available and not CS.out.gasPressed and CS.cruise_buttons == Buttons.NONE:
# # cslcSetSpeed = get_set_speed(self, hud_v_cruise)
cslcSetSpeed = set_speed_in_units
self.cruise_button = get_cslc_button(self, cslcSetSpeed, CS)
if self.cruise_button != Buttons.NONE:
# if self.CP.carFingerprint in LEGACY_SAFETY_MODE_CAR:
# send_freq = 1
# # send resume at a max freq of 10Hz
# if (self.frame - self.last_button_frame) * DT_CTRL > 0.1 * send_freq:
# # send 25 messages at a time to increases the likelihood of cruise buttons being accepted
# can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, self.cruise_button, self.CP.carFingerprint)] * 25)
# if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15 * send_freq:
# self.last_button_frame = self.frame
if self.frame % 2 == 0:
for _ in range(20):
if self.CP.carFingerprint in CANFD_CAR:
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, ((self.frame // 2) + 1) % 0x10, self.cruise_button))
else:
can_sends.extend([hyundaican.create_clu11(self.packer, (self.frame // 2) + 1, CS.clu11, self.cruise_button, self.CP.carFingerprint)] * 25)
self.last_button_frame = self.frame
# can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.SET_DECEL, self.CP.carFingerprint)] * 25)
return can_sends
def get_set_speed(self, hud_v_cruise):
v_cruise_kph = min(hud_v_cruise * CV.MS_TO_KPH, V_CRUISE_MAX)
v_cruise = int(round(v_cruise_kph * CV.KPH_TO_MPH))
v_cruise_slc: int = 0
# v_cruise_slc = params_memory.get_int("CSLCSpeed")
if v_cruise_slc > 0:
v_cruise = v_cruise_slc
return v_cruise
def get_cslc_button(self, cslcSetSpeed, CS, CC):
cruiseBtn = Buttons.NONE
speedSetPoint = int(round(CS.out.cruiseState.speed * CV.MS_TO_MPH))
if cslcSetSpeed < speedSetPoint and speedSetPoint > 25 and CC.enabled and CC.experimental_mode:
cruiseBtn = Buttons.SET_DECEL
elif cslcSetSpeed > speedSetPoint and speedSetPoint < 85 and CC.enabled:
cruiseBtn = Buttons.RES_ACCEL
else:
cruiseBtn = Buttons.SET_DECEL
# cruiseBtn = Buttons.NONE
return cruiseBtn

View File

@@ -0,0 +1,210 @@
from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip
from openpilot.common.realtime import DT_CTRL
from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance
from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR
VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState
# EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second
# All slightly below EPS thresholds to avoid fault
MAX_ANGLE = 85
MAX_ANGLE_FRAMES = 89
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
def process_hud_alert(enabled, fingerprint, hud_control):
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
# initialize to no line visible
# TODO: this is not accurate for all cars
sys_state = 1
if hud_control.leftLaneVisible and hud_control.rightLaneVisible or sys_warning: # HUD alert only display when LKAS status is active
sys_state = 3 if enabled or sys_warning else 4
elif hud_control.leftLaneVisible:
sys_state = 5
elif hud_control.rightLaneVisible:
sys_state = 6
# initialize to no warnings
left_lane_warning = 0
right_lane_warning = 0
if hud_control.leftLaneDepart:
left_lane_warning = 1 if fingerprint in (CAR.GENESIS_G90, CAR.GENESIS_G80) else 2
if hud_control.rightLaneDepart:
right_lane_warning = 1 if fingerprint in (CAR.GENESIS_G90, CAR.GENESIS_G80) else 2
return sys_warning, sys_state, left_lane_warning, right_lane_warning
class CarController:
def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.CAN = CanBus(CP)
self.params = CarControllerParams(CP)
self.packer = CANPacker(dbc_name)
self.angle_limit_counter = 0
self.frame = 0
self.accel_last = 0
self.apply_steer_last = 0
self.car_fingerprint = CP.carFingerprint
self.last_button_frame = 0
def update(self, CC, CS, now_nanos, sport_plus):
actuators = CC.actuators
hud_control = CC.hudControl
# steering torque
new_steer = int(round(actuators.steer * self.params.STEER_MAX))
apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
# >90 degree steering fault prevention
self.angle_limit_counter, apply_steer_req = common_fault_avoidance(abs(CS.out.steeringAngleDeg) >= MAX_ANGLE, CC.latActive,
self.angle_limit_counter, MAX_ANGLE_FRAMES,
MAX_ANGLE_CONSECUTIVE_FRAMES)
if not CC.latActive:
apply_steer = 0
# Hold torque with induced temporary fault when cutting the actuation bit
torque_fault = CC.latActive and not apply_steer_req
self.apply_steer_last = apply_steer
# accel + longitudinal
if sport_plus:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX_PLUS)
else:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
hud_control)
can_sends = []
# *** common hyundai stuff ***
# tester present - w/ no response (keeps relevant ECU disabled)
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and self.CP.openpilotLongitudinalControl:
# for longitudinal control, either radar or ADAS driving ECU
addr, bus = 0x7d0, 0
if self.CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, self.CAN.ECAN
can_sends.append([addr, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", bus])
# for blinkers
if self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.append([0x7b1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", self.CAN.ECAN])
# CAN-FD platforms
if self.CP.carFingerprint in CANFD_CAR:
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
hda2_long = hda2 and self.CP.openpilotLongitudinalControl
# steering control
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_steer))
# prevent LFA from activating on HDA2 by sending "no lane lines detected" to ADAS ECU
if self.frame % 5 == 0 and hda2:
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.hda2_lfa_block_msg,
self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING))
# LFA and HDA icons
if self.frame % 5 == 0 and (not hda2 or hda2_long):
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled))
# blinkers
if hda2 and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, self.frame, CC.leftBlinker, CC.rightBlinker))
if self.CP.openpilotLongitudinalControl:
if hda2:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
if self.frame % 2 == 0:
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
set_speed_in_units, CS.personality_profile))
self.accel_last = accel
else:
# button presses
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
if not self.CP.openpilotLongitudinalControl:
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=True))
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
# TODO: unclear if this is needed
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
hud_control.leadVisible, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, CS.out.cruiseState.available, CS.personality_profile))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
# 5 Hz ACC options
if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.extend(hyundaican.create_acc_opt(self.packer))
# 2 Hz front radar options
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
new_actuators = actuators.copy()
new_actuators.steer = apply_steer / self.params.STEER_MAX
new_actuators.steerOutputCan = apply_steer
new_actuators.accel = accel
self.frame += 1
return new_actuators, can_sends
def create_button_messages(self, CC: car.CarControl, CS: car.CarState, use_clu11: bool):
can_sends = []
if use_clu11:
if CC.cruiseControl.cancel:
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP.carFingerprint))
elif CC.cruiseControl.resume:
# send resume at a max freq of 10Hz
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
# send 25 messages at a time to increases the likelihood of resume being accepted
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP.carFingerprint)] * 25)
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
else:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# cruise cancel
if CC.cruiseControl.cancel:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
self.last_button_frame = self.frame
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame
# cruise standstill resume
elif CC.cruiseControl.resume:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
# TODO: resume for alt button cars
pass
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
return can_sends

View File

@@ -6,18 +6,177 @@ from opendbc.can.packer import CANPacker
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance
from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican from openpilot.selfdrive.car.hyundai import hyundaicanfd, hyundaican
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR, LEGACY_SAFETY_MODE_CAR
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
from openpilot.common.params import Params
from openpilot.common.watcher import Watcher
# DEVELOPOMENT TODO
# - Write a loop that runs and does nothing.
# - Load as many inputs in possible into state variables.
# - Make a state variable tester. It should be a web server running async.
# - Make a button tester, also on web server, to test engaging each button.
# - Write basic interface refactor
# - Write experimental mode speed controller
# - Write resume from stop controller
# - Implement as many extra features as possible (auto hvac, auto sunroof, suggest a break, remind headlights)
VisualAlert = car.CarControl.HUDControl.VisualAlert VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState LongCtrlState = car.CarControl.Actuators.LongControlState
params_memory = Params("/dev/shm/params")
# EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second # EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second
# All slightly below EPS thresholds to avoid fault # All slightly below EPS thresholds to avoid fault
MAX_ANGLE = 85 MAX_ANGLE = 85
MAX_ANGLE_FRAMES = 89 MAX_ANGLE_FRAMES = 89
MAX_ANGLE_CONSECUTIVE_FRAMES = 2 MAX_ANGLE_CONSECUTIVE_FRAMES = 2
# Constants for state arrays.
ENGAGED = 'engaged'
STANDBY = 'standby'
OFF = 'off'
# Input
# Current state of openpilot and its desired commands
openpilot_state = {
'openpilot_ready': False, # Openpilot is enabled in settings
'lateral_active': False, # Lateral control is engaged either full or always on lateral
'openpilot_engaged': False, # Openpilot is full engaged (lateral + cruise control)
'always_on_lateral_active': False, # Cruise control disengaged but lateral still engaged
'experimental_active': False, # Frogpilot has engaged experimental control, so we need to adjust speed and possibly apply brakes (if this is possible)
'experimental_desired_speed': 0, # The speed experimental control wants to set long to
'curviture_angle': 0, # Detected current curviture (ideally within the next 3 or 4 seconds) for taking over LKAS and possibly LONG
'lane_change_enabled': False, # The lane change assist feature is active
'lane_change_active': False, # Executing a lane change assist
'connected_to_internet': False,
'rate_limited_internet': False
}
# Input
# Esoteric things related to the car that support other features than driving. Will be shown in debugger.
car_state = {
'location_lat': 0,
'location_long': 0,
'drive_distance_this_trip': 0,
'drive_distance_today': 0,
'compass_direction': 'N', # Unclear if I will use this
'fuel': 1, # 0-1 fuel tank level
'windows_down': False, # True if any windows / sunroof is open
'climate_control': False,
'climate_control_set_temp': 72,
'cabin_temprature': 50,
'heated_seat_driver_on': False,
'heated_seat_passenger_on': False,
'heated_steering_wheel_on': False,
'fan_seat_driver_on': False,
'fan_seat_passenger_on': False,
'info_panel_item_showing': 0, # If we can capture what instrument panel info is showing, we can change it to speed
'daytime': False,
'headlights': False, # Would be cool to remind to have the headlights turned on if moving
}
# Input
# The current status of cruise control activation
cruise_control_state = {
'set_speed': 0, # Cruise control set speed
'actual_speed': 0, # Spedometer reading
'speed_limit': 0, # Speed limit as reported by car's dashboard
'cruise_control_status': OFF, # or ENGAGED or STANDBY
'brake_pressed': False
}
# Input
# The current state of the car in front of us, if cruise control is engaged but the car is not moving
stationary_state = {
'stationary': False, # When speed reaches 0 we are stationary
'stationary_since': 0, # At what time stationary was achieved
'stationary_has_lead': False, # At the time of becoming stationary, was there a lead veichele?
'stationary_has_lead_distance': 0, # At the time of becoming stationary, what was the distance to the lead veichele?
'current_lead_car_distance': 0, # What is the distance to the lead veichele now?
'lead_vehicle_moving_away': False, # Has logic decided that the current veichele is moving away?
'oem_lanes_detected': False # Is the car reporting it can see lanes? I would like to experiment with engaging stock LKAS.
}
# Input
# What buttons the user is pressing. Can be None, BUTTON_SHORT, or BUTTON_LONG (held for 1/2 second)
cruise_control_buttons_input = {
'engage_cruise_control': None,
'cruise_control_speed_up': None,
'cruise_control_speed_down': None,
'pause_resume_cruise_control': None,
'lane_keep_assist_button': None,
'lane_follow_assist_button': None
}
# Output
# What buttons we wish to simulate pressing.
cruise_control_buttons_output = {
'engage_cruise_control': False,
'cruise_control_speed_up': False,
'cruise_control_speed_down': False,
'pause_resume_cruise_control': False,
'lane_keep_assist_button': False,
'lane_follow_assist_button': False,
}
# Output
# What buttons we wish to simulate pressing.
# I don't know if I will have access to these.
other_buttons_output = {
'info_category': False,
'info_section': False,
'heated_seat_driver': False,
'heated_seat_passenger': False,
'heated_steering_wheel': False,
'fan_seat_driver': False,
'fan_seat_passenger': False,
'sunroof': False,
'hvac_power': False,
'hvac_sync': False,
'hvac_up': False,
'hvac_down': False
}
# What to display on the instrument panel. Sent every frame.
instrument_panel_outputs = {
'lane_keeping_assist_active': False,
'forward_collision_warning_active': False,
'high_beam_assist_active': False,
'forward_collision_avoidance_assist_active': False,
'blind_spot_collision_warning_active': False,
'lane_following_assist_active': False,
'driver_attention_warning_active': False,
'lane_departure_warning_active': False
}
# State
# Variables to save what oscarpilot is doing - behaviors will change depending
# on current state
oscarpilot_state = [
'set_speed': 0, # Current desired cruise speed
'speed_override': False, # Set if moving away from set speed due to experimental
'speed_restore': False, # Set if moving twoards set speed due to reengagement or change speed to speed limit
'lkas_oem': False, # Set if allowing the car oem software to control LKAS
'daytime': False, # Set if it is daytime. Car is more judgmental at night.
]
# Output
# These are messages to the user, and get set to false when processed by openpilot
# Ideally we can set these in the infotainment area as well, like carplay audio selection
oscarpilot_alerts = {
'detected_stop_sign_or_light': False, # Openpilot has seen a stoplight or stopsign
'very_sharp_curve': False, # The upcoming curve is probably too sharp to be handled automatically
'lane_confidence_low': False, # The confidence level that openpilot knows what its doing is low
'heavy_traffic_ahead': False, # Mapping data indicates heavy traffic within 2 miles
'weather_ahead': False, # Mapping data indicates precipitation within 5 miles
'suggest_a_break': False, # Variable will be used to suggest a break (3 hours at day, 1.5 hours at night)
}
# Where is this called? Public? Private? Should we refactor?
def process_hud_alert(enabled, fingerprint, hud_control): def process_hud_alert(enabled, fingerprint, hud_control):
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw)) sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
@@ -41,7 +200,6 @@ def process_hud_alert(enabled, fingerprint, hud_control):
return sys_warning, sys_state, left_lane_warning, right_lane_warning return sys_warning, sys_state, left_lane_warning, right_lane_warning
class CarController: class CarController:
def __init__(self, dbc_name, CP, VM): def __init__(self, dbc_name, CP, VM):
self.CP = CP self.CP = CP
@@ -55,115 +213,36 @@ class CarController:
self.apply_steer_last = 0 self.apply_steer_last = 0
self.car_fingerprint = CP.carFingerprint self.car_fingerprint = CP.carFingerprint
self.last_button_frame = 0 self.last_button_frame = 0
self.last_resume_frame = 0
self.last_debug_frame = 0
self._CC = None
# self._CC.actuators
# self._CC.hudControl
self._CS = None
self._now_nanos = None
self._sport_plus = None
self._actuators = None
self._hud_control = None
def update(self, CC, CS, now_nanos, sport_plus): def update(self, CC, CS, now_nanos, sport_plus):
actuators = CC.actuators self._CC = CC
hud_control = CC.hudControl self._CS = CS
self._now_nanos = now_nanos
self._sport_plus = sport_plus
# steering torque self._apply_steer = None
new_steer = int(round(actuators.steer * self.params.STEER_MAX)) self._accel = None
apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, CS.out.steeringTorque, self.params)
# >90 degree steering fault prevention
self.angle_limit_counter, apply_steer_req = common_fault_avoidance(abs(CS.out.steeringAngleDeg) >= MAX_ANGLE, CC.latActive,
self.angle_limit_counter, MAX_ANGLE_FRAMES,
MAX_ANGLE_CONSECUTIVE_FRAMES)
if not CC.latActive:
apply_steer = 0
# Hold torque with induced temporary fault when cutting the actuation bit
torque_fault = CC.latActive and not apply_steer_req
self.apply_steer_last = apply_steer
# accel + longitudinal
if sport_plus:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX_PLUS)
else:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
hud_control)
can_sends = [] can_sends = []
# *** common hyundai stuff *** # CAN-FD car logic
# tester present - w/ no response (keeps relevant ECU disabled)
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and self.CP.openpilotLongitudinalControl:
# for longitudinal control, either radar or ADAS driving ECU
addr, bus = 0x7d0, 0
if self.CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, self.CAN.ECAN
can_sends.append([addr, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", bus])
# for blinkers
if self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.append([0x7b1, 0, b"\x02\x3E\x80\x00\x00\x00\x00\x00", self.CAN.ECAN])
# CAN-FD platforms
if self.CP.carFingerprint in CANFD_CAR: if self.CP.carFingerprint in CANFD_CAR:
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2 # Steering control for CAN FD
hda2_long = hda2 and self.CP.openpilotLongitudinalControl # can_sends.extend(self._create_common_messages())
can_sends.extend(self._create_steering_messages())
# steering control can_sends.extend(self._create_instrument_messages())
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_steer)) can_sends.extend(self._create_button_messages())
# prevent LFA from activating on HDA2 by sending "no lane lines detected" to ADAS ECU
if self.frame % 5 == 0 and hda2:
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.hda2_lfa_block_msg,
self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING))
# LFA and HDA icons
if self.frame % 5 == 0 and (not hda2 or hda2_long):
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled))
# blinkers
if hda2 and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, self.frame, CC.leftBlinker, CC.rightBlinker))
if self.CP.openpilotLongitudinalControl:
if hda2:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
if self.frame % 2 == 0:
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
set_speed_in_units, CS.personality_profile))
self.accel_last = accel
else:
# button presses
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.car_fingerprint, apply_steer, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
if not self.CP.openpilotLongitudinalControl:
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=True))
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
# TODO: unclear if this is needed
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
hud_control.leadVisible, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, CS.out.cruiseState.available, CS.personality_profile))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
# 5 Hz ACC options
if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.extend(hyundaican.create_acc_opt(self.packer))
# 2 Hz front radar options
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
new_actuators = actuators.copy() new_actuators = actuators.copy()
new_actuators.steer = apply_steer / self.params.STEER_MAX new_actuators.steer = apply_steer / self.params.STEER_MAX
@@ -173,38 +252,126 @@ class CarController:
self.frame += 1 self.frame += 1
return new_actuators, can_sends return new_actuators, can_sends
def create_button_messages(self, CC: car.CarControl, CS: car.CarState, use_clu11: bool): # def _create_common_messages():
can_sends = [] # *** common hyundai stuff ***
if use_clu11:
if CC.cruiseControl.cancel:
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP.carFingerprint))
elif CC.cruiseControl.resume:
# send resume at a max freq of 10Hz
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
# send 25 messages at a time to increases the likelihood of resume being accepted
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP.carFingerprint)] * 25)
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
else:
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# cruise cancel
if CC.cruiseControl.cancel:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
self.last_button_frame = self.frame
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame
# cruise standstill resume def _unprocessed():
elif CC.cruiseControl.resume:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS: # HUD messages
# TODO: resume for alt button cars sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
pass hud_control)
else:
for _ in range(20): can_sends = []
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
# CAN-FD platforms
# if self.CP.carFingerprint in CANFD_CAR:
# # LFA and HDA icons
# if self.frame % 5 == 0 and (not hda2 or hda2_long):
# can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled))
# if self.CP.openpilotLongitudinalControl:
# if hda2:
# can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
# if self.frame % 2 == 0:
# can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
# set_speed_in_units, CS.personality_profile))
# self.accel_last = accel
# else:
# # button presses
# can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
# Mostly unchanged from frogpilot.
def _create_steering_messages():
can_sends = []
# steering torque
new_steer = int(round(actuators.steer * self.params.STEER_MAX))
apply_steer = apply_driver_steer_torque_limits(new_steer, self.apply_steer_last, self._CS.out.steeringTorque, self.params)
# >90 degree steering fault prevention
self.angle_limit_counter, apply_steer_req = common_fault_avoidance(abs(self._CS.out.steeringAngleDeg) >= MAX_ANGLE, self._CC.latActive,
self.angle_limit_counter, MAX_ANGLE_FRAMES,
MAX_ANGLE_CONSECUTIVE_FRAMES)
if not CC.latActive:
apply_steer = 0
# Hold torque with induced temporary fault when cutting the actuation bit
torque_fault = self._CC.latActive and not apply_steer_req
self.apply_steer_last = apply_steer
# accel + longitudinal
if sport_plus:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX_PLUS)
else:
accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX)
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
# steering control
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_steer))
# prevent LFA from activating on HDA2 by sending "no lane lines detected" to ADAS ECU
if self.frame % 5 == 0 and hda2:
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.hda2_lfa_block_msg,
self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING))
# blinkers
# if hda2 and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
# can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, self.frame, CC.leftBlinker, CC.rightBlinker))
self._apply_steer = apply_steer
self._accel = accel
return can_sends return can_sends
# Placeholder for potential instrument panel processing
def _create_instrument_messages(instrument_panel_state):
can_sends = []
# Assume default values or read the initial state
lfa_icon_state = 0
hda_active = 0
hda_icon_state = 0
hda_set_speed = 0 # This would likely need to be more dynamic based on context
# Assign values based on instrument_panel_outputs states
for output in instrument_panel_outputs:
if output['name'] == 'lane_keeping_assist_active':
lfa_icon_state = 2 if output['value'] else 0
elif output['name'] == 'hda_active': # Hypothetical key for example
hda_active = 1 if output['value'] else 0
elif output['name'] == 'hda_icon_state': # Also hypothetical
hda_icon_state = 2 if output['value'] else 0
# Add other conditions based on the actual data structure you expect
# Assume we can bundle all these states into one message as in your example
values = {
"LFA_Icon_State": lfa_icon_state,
"HDA_Active": hda_active,
"HDA_Icon_State": hda_icon_state,
"HDA_VSetReq": hda_set_speed, # This example assumes static speed; adjust if dynamic
}
can_sends.append(packer.make_can_msg("LFAHDA_MFC", 0, values))
return can_sends
# Placeholder for potential button press processing
def _create_button_messages(button_presses):
can_sends = []
for button in cruise_control_buttons:
if button['value']:
# Resetting button state to prevent repeated presses
button['value'] = False
if button['name'] in button_map:
# Mapping each button to a specific CAN message
btn_value = button_map[button['name']]
can_sends.append(create_buttons(packer, CP, CAN, 0, btn_value)) # Counter is example, replace with actual logic if necessary
return can_sends

View File

@@ -0,0 +1,500 @@
from collections import deque
import copy
import math
from cereal import car
from openpilot.common.conversions import Conversions as CV
from opendbc.can.parser import CANParser
from opendbc.can.can_define import CANDefine
from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \
CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController
from openpilot.common.watcher import Watcher
PREV_BUTTON_SAMPLES = 8
CLUSTER_SAMPLE_RATE = 20 # frames
STANDSTILL_THRESHOLD = 12 * 0.03125 * CV.KPH_TO_MS
class CarState(CarStateBase):
def __init__(self, CP):
super().__init__(CP)
can_define = CANDefine(DBC[CP.carFingerprint]["pt"])
self.cruise_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.gear_msg_canfd = "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
"GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \
"GEAR_SHIFTER"
if CP.carFingerprint in CANFD_CAR:
self.shifter_values = can_define.dv[self.gear_msg_canfd]["GEAR"]
elif self.CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"]
elif self.CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
self.shifter_values = can_define.dv["TCU12"]["CUR_GR"]
else: # preferred and elect gear methods use same definition
self.shifter_values = can_define.dv["LVR12"]["CF_Lvr_Gear"]
self.accelerator_msg_canfd = "ACCELERATOR" if CP.carFingerprint in EV_CAR else \
"ACCELERATOR_ALT" if CP.carFingerprint in HYBRID_CAR else \
"ACCELERATOR_BRAKE_ALT"
self.cruise_btns_msg_canfd = "CRUISE_BUTTONS_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else \
"CRUISE_BUTTONS"
self.is_metric = False
self.buttons_counter = 0
self.cruise_info = {}
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
self.cluster_speed = 0
self.cluster_speed_counter = CLUSTER_SAMPLE_RATE
self.params = CarControllerParams(CP)
def update(self, cp, cp_cam):
Watcher.log_watch("hyundai_carstate_update_cp", cp)
Watcher.log_watch("hyundai_carstate_update_cp_cam", cp_cam)
if self.CP.carFingerprint in CANFD_CAR:
return self.update_canfd(cp, cp_cam)
ret = car.CarState.new_message()
cp_cruise = cp_cam if self.CP.carFingerprint in CAMERA_SCC_CAR else cp
self.is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0
speed_conv = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
ret.doorOpen = any([cp.vl["CGW1"]["CF_Gway_DrvDrSw"], cp.vl["CGW1"]["CF_Gway_AstDrSw"],
cp.vl["CGW2"]["CF_Gway_RLDrSw"], cp.vl["CGW2"]["CF_Gway_RRDrSw"]])
ret.seatbeltUnlatched = cp.vl["CGW1"]["CF_Gway_DrvSeatBeltSw"] == 0
ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHL_SPD11"]["WHL_SPD_FL"],
cp.vl["WHL_SPD11"]["WHL_SPD_FR"],
cp.vl["WHL_SPD11"]["WHL_SPD_RL"],
cp.vl["WHL_SPD11"]["WHL_SPD_RR"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
self.cluster_speed_counter += 1
if self.cluster_speed_counter > CLUSTER_SAMPLE_RATE:
self.cluster_speed = cp.vl["CLU15"]["CF_Clu_VehicleSpeed"]
self.cluster_speed_counter = 0
# Mimic how dash converts to imperial.
# Sorento is the only platform where CF_Clu_VehicleSpeed is already imperial when not is_metric
# TODO: CGW_USM1->CF_Gway_DrLockSoundRValue may describe this
if not self.is_metric and self.CP.carFingerprint not in (CAR.KIA_SORENTO,):
self.cluster_speed = math.floor(self.cluster_speed * CV.KPH_TO_MPH + CV.KPH_TO_MPH)
ret.vEgoCluster = self.cluster_speed * speed_conv
ret.steeringAngleDeg = cp.vl["SAS11"]["SAS_Angle"]
ret.steeringRateDeg = cp.vl["SAS11"]["SAS_Speed"]
ret.yawRate = cp.vl["ESP12"]["YAW_RATE"]
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(
50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"])
ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"]
ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0
# cruise state
if self.CP.openpilotLongitudinalControl:
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
ret.cruiseState.available = self.main_enabled
ret.cruiseState.enabled = cp.vl["TCS13"]["ACC_REQ"] == 1
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
else:
ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
ret.cruiseState.standstill = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 4.
ret.cruiseState.nonAdaptive = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
ret.cruiseState.speed = cp_cruise.vl["SCC11"]["VSetDis"] * speed_conv
# TODO: Find brake pressure
ret.brake = 0
ret.brakePressed = cp.vl["TCS13"]["DriverOverride"] == 2 # 2 includes regen braking by user on HEV/EV
ret.brakeHoldActive = cp.vl["TCS15"]["AVH_LAMP"] == 2 # 0 OFF, 1 ERROR, 2 ACTIVE, 3 READY
ret.parkingBrake = cp.vl["TCS13"]["PBRAKE_ACT"] == 1
ret.accFaulted = cp.vl["TCS13"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if self.CP.carFingerprint in (HYBRID_CAR | EV_CAR):
if self.CP.carFingerprint in HYBRID_CAR:
ret.gas = cp.vl["E_EMS11"]["CR_Vcu_AccPedDep_Pos"] / 254.
else:
ret.gas = cp.vl["E_EMS11"]["Accel_Pedal_Pos"] / 254.
ret.gasPressed = ret.gas > 0
else:
ret.gas = cp.vl["EMS12"]["PV_AV_CAN"] / 100.
ret.gasPressed = bool(cp.vl["EMS16"]["CF_Ems_AclAct"])
# Gear Selection via Cluster - For those Kia/Hyundai which are not fully discovered, we can use the Cluster Indicator for Gear Selection,
# as this seems to be standard over all cars, but is not the preferred method.
if self.CP.carFingerprint in (HYBRID_CAR | EV_CAR):
gear = cp.vl["ELECT_GEAR"]["Elect_Gear_Shifter"]
elif self.CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
gear = cp.vl["CLU15"]["CF_Clu_Gear"]
elif self.CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
gear = cp.vl["TCU12"]["CUR_GR"]
else:
gear = cp.vl["LVR12"]["CF_Lvr_Gear"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if not self.CP.openpilotLongitudinalControl:
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1 # sometimes only SCC system shows an FCW
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
ret.stockAeb = aeb_warning and aeb_braking
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0
# save the entire LKAS11 and CLU11
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
self.clu11 = copy.copy(cp.vl["CLU11"])
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
self.prev_cruise_buttons = self.cruise_buttons[-1]
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
self.prev_main_buttons = self.main_buttons[-1]
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
if self.prev_main_buttons == 0 and self.main_buttons[-1] != 0:
self.main_enabled = not self.main_enabled
# BBot functions for lfa and gap buttons - test speed up / down
self.oscar_lane_center_btn_pressed= False
self.oscar_slc_decel = False
self.oscar_slc_accel = False
self.param_memory.put("oscar_debug", "Hello World")
# if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
# self.oscar_lane_center_btn_pressed= True
# lkas_pressed = cp.vl["BCM_PO_11"]["LFA_Pressed"]
# if lkas_pressed and not self.lkas_previously_pressed:
# self.custom_speed_up = True
# self.lkas_previously_pressed = lkas_pressed
# Driving personalities function
# if self.personalities_via_wheel and ret.cruiseState.available:
# # Sync with the onroad UI button
# if self.param_memory.get_bool("PersonalityChangedViaUI"):
# self.personality_profile = self.param.get_int("LongitudinalPersonality")
# self.param_memory.put_bool("PersonalityChangedViaUI", False)
# # Change personality upon steering wheel button press
# if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
# self.param_memory.put_bool("PersonalityChangedViaWheel", True)
# self.personality_profile = (self.previous_personality_profile + 2) % 3
# if self.personality_profile != self.previous_personality_profile and self.personality_profile >= 0:
# self.param.put_int("LongitudinalPersonality", self.personality_profile)
# self.previous_personality_profile = self.personality_profile
# # Toggle Experimental Mode from steering wheel function
# if self.experimental_mode_via_lkas and ret.cruiseState.available:
# lkas_pressed = cp.vl["BCM_PO_11"]["LFA_Pressed"]
# if lkas_pressed and not self.lkas_previously_pressed:
# if self.conditional_experimental_mode:
# # Set "CEStatus" to work with "Conditional Experimental Mode"
# conditional_status = self.param_memory.get_int("CEStatus")
# override_value = 0 if conditional_status in (1, 2, 3, 4) else 1 if conditional_status >= 5 else 2
# self.param_memory.put_int("CEStatus", override_value)
# else:
# experimental_mode = self.param.get_bool("ExperimentalMode")
# # Invert the value of "ExperimentalMode"
# put_bool_nonblocking("ExperimentalMode", not experimental_mode)
# self.lkas_previously_pressed = lkas_pressed
Watcher.log_watch("hyundai_carstate_update_self", self)
return ret
def update_canfd(self, cp, cp_cam):
Watcher.log_watch("hyundai_carstate_update_canfd_cp", cp)
Watcher.log_watch("hyundai_carstate_update_canfd_cp_cam", cp_cam)
ret = car.CarState.new_message()
self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
if self.CP.carFingerprint in (EV_CAR | HYBRID_CAR):
offset = 255. if self.CP.carFingerprint in EV_CAR else 1023.
ret.gas = cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL"] / offset
ret.gasPressed = ret.gas > 1e-5
else:
ret.gasPressed = bool(cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL_PRESSED"])
ret.brakePressed = cp.vl["TCS"]["DriverBraking"] == 1
ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR"] == 1
ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT"] == 0
gear = cp.vl[self.gear_msg_canfd]["GEAR"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
# TODO: figure out positions
ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_1"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_2"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_3"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_4"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEERING_RATE"]
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1
ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"]
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
# TODO: alt signal usage may be described by cp.vl['BLINKERS']['USE_ALT_LAMP']
left_blinker_sig, right_blinker_sig = "LEFT_LAMP", "RIGHT_LAMP"
if self.CP.carFingerprint == CAR.KONA_EV_2ND_GEN:
left_blinker_sig, right_blinker_sig = "LEFT_LAMP_ALT", "RIGHT_LAMP_ALT"
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"][left_blinker_sig],
cp.vl["BLINKERS"][right_blinker_sig])
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] != 0
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0
# cruise state
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
ret.cruiseState.available = self.main_enabled
if self.CP.openpilotLongitudinalControl:
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1
ret.cruiseState.standstill = False
else:
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2)
ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["CRUISE_STANDSTILL"] == 1
ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor
self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"])
# Manual Speed Limit Assist is a feature that replaces non-adaptive cruise control on EV CAN FD platforms.
# It limits the vehicle speed, overridable by pressing the accelerator past a certain point.
# The car will brake, but does not respect positive acceleration commands in this mode
# TODO: find this message on ICE & HYBRID cars + cruise control signals (if exists)
if self.CP.carFingerprint in EV_CAR:
ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
self.prev_cruise_buttons = self.cruise_buttons[-1]
self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
self.prev_main_buttons = self.main_buttons[-1]
self.main_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["ADAPTIVE_CRUISE_MAIN_BTN"])
if self.prev_main_buttons == 0 and self.main_buttons[-1] != 0:
self.main_enabled = not self.main_enabled
self.buttons_counter = cp.vl[self.cruise_btns_msg_canfd]["COUNTER"]
ret.accFaulted = cp.vl["TCS"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if self.CP.flags & HyundaiFlags.CANFD_HDA2:
self.hda2_lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x362"] if self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING
else cp_cam.vl["CAM_0x2a4"])
SpeedLimitController.load_state()
SpeedLimitController.car_speed_limit = self.calculate_speed_limit_canfd(self.CP, cp, cp_cam) * speed_factor
SpeedLimitController.write_car_state()
# self.custom_speed_up = False
self.oscar_lane_center_btn_pressed = False
if ret.cruiseState.available:
if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
self.oscar_lane_center_btn_pressed = True
lkas_pressed = False
try:
lkas_pressed = cp.vl[self.cruise_btns_msg_canfd]["LKAS_BTN"]
except:
nothing = 0
# intentionally cause a failure
if lkas_pressed:
floog=norpdywoop
# if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
# self.oscar_lane_center_btn_pressed= True
# Driving personalities function
# if self.personalities_via_wheel and ret.cruiseState.available:
# # Sync with the onroad UI button
# if self.param_memory.get_bool("PersonalityChangedViaUI"):
# self.personality_profile = self.param.get_int("LongitudinalPersonality")
# self.param_memory.put_bool("PersonalityChangedViaUI", False)
# # Change personality upon steering wheel button press
# if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
# self.param_memory.put_bool("PersonalityChangedViaWheel", True)
# self.personality_profile = (self.previous_personality_profile + 2) % 3
# if self.personality_profile != self.previous_personality_profile and self.personality_profile >= 0:
# self.param.put_int("LongitudinalPersonality", self.personality_profile)
# self.previous_personality_profile = self.personality_profile
# # Toggle Experimental Mode from steering wheel function
# if self.experimental_mode_via_lkas and ret.cruiseState.available:
# lkas_pressed = cp.vl[self.cruise_btns_msg_canfd]["LKAS_BTN"]
# if lkas_pressed and not self.lkas_previously_pressed:
# if self.conditional_experimental_mode:
# # Set "CEStatus" to work with "Conditional Experimental Mode"
# conditional_status = self.param_memory.get_int("CEStatus")
# override_value = 0 if conditional_status in (1, 2, 3, 4) else 1 if conditional_status >= 5 else 2
# self.param_memory.put_int("CEStatus", override_value)
# else:
# experimental_mode = self.param.get_bool("ExperimentalMode")
# # Invert the value of "ExperimentalMode"
# put_bool_nonblocking("ExperimentalMode", not experimental_mode)
# self.lkas_previously_pressed = lkas_pressed
Watcher.log_watch("hyundai_carstate_update_canfd_self", self)
return ret
# BBOT does not work
def calculate_speed_limit_canfd(self, CP, cp, cp_cam):
try:
self._speed_limit_clu = cp.vl["CLUSTER_SPEED_LIMIT"]["SPEED_LIMIT_1"]
return self._speed_limit_clu if self._speed_limit_clu not in (0, 255) else 0
except:
return 0
def get_can_parser(self, CP):
if CP.carFingerprint in CANFD_CAR:
return self.get_can_parser_canfd(CP)
messages = [
# address, frequency
("MDPS12", 50),
("TCS13", 50),
("TCS15", 10),
("CLU11", 50),
("CLU15", 5),
("ESP12", 100),
("CGW1", 10),
("CGW2", 5),
("CGW4", 5),
("WHL_SPD11", 50),
("SAS11", 100),
]
if not CP.openpilotLongitudinalControl and CP.carFingerprint not in CAMERA_SCC_CAR:
messages += [
("SCC11", 50),
("SCC12", 50),
]
if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))
if CP.enableBsm:
messages.append(("LCA11", 50))
if CP.carFingerprint in (HYBRID_CAR | EV_CAR):
messages.append(("E_EMS11", 50))
else:
messages += [
("EMS12", 100),
("EMS16", 100),
]
if CP.carFingerprint in (HYBRID_CAR | EV_CAR):
messages.append(("ELECT_GEAR", 20))
elif CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
pass
elif CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
messages.append(("TCU12", 100))
else:
messages.append(("LVR12", 100))
messages.append(("BCM_PO_11", 50))
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0)
@staticmethod
def get_cam_can_parser(CP):
if CP.carFingerprint in CANFD_CAR:
return CarState.get_cam_can_parser_canfd(CP)
messages = [
("LKAS11", 100)
]
if not CP.openpilotLongitudinalControl and CP.carFingerprint in CAMERA_SCC_CAR:
messages += [
("SCC11", 50),
("SCC12", 50),
]
if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2)
def get_can_parser_canfd(self, CP):
messages = [
(self.gear_msg_canfd, 100),
(self.accelerator_msg_canfd, 100),
("WHEEL_SPEEDS", 100),
("STEERING_SENSORS", 100),
("MDPS", 100),
("TCS", 50),
("CRUISE_BUTTONS_ALT", 50),
("BLINKERS", 4),
("DOORS_SEATBELTS", 4),
]
if CP.carFingerprint in EV_CAR:
messages += [
("MANUAL_SPEED_LIMIT_ASSIST", 10),
]
if not (CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
messages += [
("CRUISE_BUTTONS", 50)
]
if CP.enableBsm:
messages += [
("BLINDSPOTS_REAR_CORNERS", 20),
]
# if CP.nav_msg:
# messages.append(("CLUSTER_SPEED_LIMIT", 10))
if not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and not CP.openpilotLongitudinalControl:
messages += [
("SCC_CONTROL", 50),
]
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).ECAN)
@staticmethod
def get_cam_can_parser_canfd(CP):
messages = []
if CP.flags & HyundaiFlags.CANFD_HDA2:
block_lfa_msg = "CAM_0x362" if CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else "CAM_0x2a4"
messages += [(block_lfa_msg, 20)]
elif CP.flags & HyundaiFlags.CANFD_CAMERA_SCC:
messages += [
("SCC_CONTROL", 50),
]
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).CAM)

View File

@@ -10,6 +10,9 @@ from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \ from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \
CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams CANFD_CAR, EV_CAR, HYBRID_CAR, Buttons, CarControllerParams
from openpilot.selfdrive.car.interfaces import CarStateBase from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController
from openpilot.common.watcher import Watcher
PREV_BUTTON_SAMPLES = 8 PREV_BUTTON_SAMPLES = 8
CLUSTER_SAMPLE_RATE = 20 # frames CLUSTER_SAMPLE_RATE = 20 # frames
@@ -24,370 +27,24 @@ class CarState(CarStateBase):
self.cruise_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) self.cruise_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) self.main_buttons = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.gear_msg_canfd = "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
"GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \
"GEAR_SHIFTER"
if CP.carFingerprint in CANFD_CAR:
self.shifter_values = can_define.dv[self.gear_msg_canfd]["GEAR"]
elif self.CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
self.shifter_values = can_define.dv["CLU15"]["CF_Clu_Gear"]
elif self.CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
self.shifter_values = can_define.dv["TCU12"]["CUR_GR"]
else: # preferred and elect gear methods use same definition
self.shifter_values = can_define.dv["LVR12"]["CF_Lvr_Gear"]
self.accelerator_msg_canfd = "ACCELERATOR" if CP.carFingerprint in EV_CAR else \
"ACCELERATOR_ALT" if CP.carFingerprint in HYBRID_CAR else \
"ACCELERATOR_BRAKE_ALT"
self.cruise_btns_msg_canfd = "CRUISE_BUTTONS_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else \
"CRUISE_BUTTONS"
self.is_metric = False
self.buttons_counter = 0
self.cruise_info = {}
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
self.cluster_speed = 0
self.cluster_speed_counter = CLUSTER_SAMPLE_RATE
self.params = CarControllerParams(CP) self.params = CarControllerParams(CP)
def update(self, cp, cp_cam): def update(self, cp, cp_cam):
Watcher.log_watch("hyundai_carstate_update_cp", cp)
Watcher.log_watch("hyundai_carstate_update_cp_cam", cp_cam)
if self.CP.carFingerprint in CANFD_CAR: if self.CP.carFingerprint in CANFD_CAR:
return self.update_canfd(cp, cp_cam) return self.update_canfd(cp, cp_cam)
ret = car.CarState.new_message() Watcher.log_watch("hyundai_carstate_update_self", self)
cp_cruise = cp_cam if self.CP.carFingerprint in CAMERA_SCC_CAR else cp
self.is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0
speed_conv = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
ret.doorOpen = any([cp.vl["CGW1"]["CF_Gway_DrvDrSw"], cp.vl["CGW1"]["CF_Gway_AstDrSw"],
cp.vl["CGW2"]["CF_Gway_RLDrSw"], cp.vl["CGW2"]["CF_Gway_RRDrSw"]])
ret.seatbeltUnlatched = cp.vl["CGW1"]["CF_Gway_DrvSeatBeltSw"] == 0
ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHL_SPD11"]["WHL_SPD_FL"],
cp.vl["WHL_SPD11"]["WHL_SPD_FR"],
cp.vl["WHL_SPD11"]["WHL_SPD_RL"],
cp.vl["WHL_SPD11"]["WHL_SPD_RR"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
self.cluster_speed_counter += 1
if self.cluster_speed_counter > CLUSTER_SAMPLE_RATE:
self.cluster_speed = cp.vl["CLU15"]["CF_Clu_VehicleSpeed"]
self.cluster_speed_counter = 0
# Mimic how dash converts to imperial.
# Sorento is the only platform where CF_Clu_VehicleSpeed is already imperial when not is_metric
# TODO: CGW_USM1->CF_Gway_DrLockSoundRValue may describe this
if not self.is_metric and self.CP.carFingerprint not in (CAR.KIA_SORENTO,):
self.cluster_speed = math.floor(self.cluster_speed * CV.KPH_TO_MPH + CV.KPH_TO_MPH)
ret.vEgoCluster = self.cluster_speed * speed_conv
ret.steeringAngleDeg = cp.vl["SAS11"]["SAS_Angle"]
ret.steeringRateDeg = cp.vl["SAS11"]["SAS_Speed"]
ret.yawRate = cp.vl["ESP12"]["YAW_RATE"]
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(
50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"])
ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"]
ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0
# cruise state
if self.CP.openpilotLongitudinalControl:
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
ret.cruiseState.available = self.main_enabled
ret.cruiseState.enabled = cp.vl["TCS13"]["ACC_REQ"] == 1
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
else:
ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
ret.cruiseState.standstill = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 4.
ret.cruiseState.nonAdaptive = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
ret.cruiseState.speed = cp_cruise.vl["SCC11"]["VSetDis"] * speed_conv
# TODO: Find brake pressure
ret.brake = 0
ret.brakePressed = cp.vl["TCS13"]["DriverOverride"] == 2 # 2 includes regen braking by user on HEV/EV
ret.brakeHoldActive = cp.vl["TCS15"]["AVH_LAMP"] == 2 # 0 OFF, 1 ERROR, 2 ACTIVE, 3 READY
ret.parkingBrake = cp.vl["TCS13"]["PBRAKE_ACT"] == 1
ret.accFaulted = cp.vl["TCS13"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if self.CP.carFingerprint in (HYBRID_CAR | EV_CAR):
if self.CP.carFingerprint in HYBRID_CAR:
ret.gas = cp.vl["E_EMS11"]["CR_Vcu_AccPedDep_Pos"] / 254.
else:
ret.gas = cp.vl["E_EMS11"]["Accel_Pedal_Pos"] / 254.
ret.gasPressed = ret.gas > 0
else:
ret.gas = cp.vl["EMS12"]["PV_AV_CAN"] / 100.
ret.gasPressed = bool(cp.vl["EMS16"]["CF_Ems_AclAct"])
# Gear Selection via Cluster - For those Kia/Hyundai which are not fully discovered, we can use the Cluster Indicator for Gear Selection,
# as this seems to be standard over all cars, but is not the preferred method.
if self.CP.carFingerprint in (HYBRID_CAR | EV_CAR):
gear = cp.vl["ELECT_GEAR"]["Elect_Gear_Shifter"]
elif self.CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
gear = cp.vl["CLU15"]["CF_Clu_Gear"]
elif self.CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
gear = cp.vl["TCU12"]["CUR_GR"]
else:
gear = cp.vl["LVR12"]["CF_Lvr_Gear"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if not self.CP.openpilotLongitudinalControl:
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1 # sometimes only SCC system shows an FCW
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
ret.stockAeb = aeb_warning and aeb_braking
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0
# save the entire LKAS11 and CLU11
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
self.clu11 = copy.copy(cp.vl["CLU11"])
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
self.prev_cruise_buttons = self.cruise_buttons[-1]
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
self.prev_main_buttons = self.main_buttons[-1]
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
if self.prev_main_buttons == 0 and self.main_buttons[-1] != 0:
self.main_enabled = not self.main_enabled
# Driving personalities function
if self.personalities_via_wheel and ret.cruiseState.available:
# Sync with the onroad UI button
if self.param_memory.get_bool("PersonalityChangedViaUI"):
self.personality_profile = self.param.get_int("LongitudinalPersonality")
self.param_memory.put_bool("PersonalityChangedViaUI", False)
# Change personality upon steering wheel button press
if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
self.param_memory.put_bool("PersonalityChangedViaWheel", True)
self.personality_profile = (self.previous_personality_profile + 2) % 3
if self.personality_profile != self.previous_personality_profile and self.personality_profile >= 0:
self.param.put_int("LongitudinalPersonality", self.personality_profile)
self.previous_personality_profile = self.personality_profile
# Toggle Experimental Mode from steering wheel function
if self.experimental_mode_via_lkas and ret.cruiseState.available:
lkas_pressed = cp.vl["BCM_PO_11"]["LFA_Pressed"]
if lkas_pressed and not self.lkas_previously_pressed:
if self.conditional_experimental_mode:
# Set "CEStatus" to work with "Conditional Experimental Mode"
conditional_status = self.param_memory.get_int("CEStatus")
override_value = 0 if conditional_status in (1, 2, 3, 4) else 1 if conditional_status >= 5 else 2
self.param_memory.put_int("CEStatus", override_value)
else:
experimental_mode = self.param.get_bool("ExperimentalMode")
# Invert the value of "ExperimentalMode"
put_bool_nonblocking("ExperimentalMode", not experimental_mode)
self.lkas_previously_pressed = lkas_pressed
return ret return ret
def update_canfd(self, cp, cp_cam): def update_canfd(self, cp, cp_cam):
ret = car.CarState.new_message() Watcher.log_watch("hyundai_carstate_update_canfd_cp", cp)
Watcher.log_watch("hyundai_carstate_update_canfd_cp_cam", cp_cam)
self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
if self.CP.carFingerprint in (EV_CAR | HYBRID_CAR):
offset = 255. if self.CP.carFingerprint in EV_CAR else 1023.
ret.gas = cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL"] / offset
ret.gasPressed = ret.gas > 1e-5
else:
ret.gasPressed = bool(cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL_PRESSED"])
ret.brakePressed = cp.vl["TCS"]["DriverBraking"] == 1
ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR"] == 1
ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT"] == 0
gear = cp.vl[self.gear_msg_canfd]["GEAR"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
# TODO: figure out positions
ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_1"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_2"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_3"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_4"],
)
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEERING_RATE"]
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1
ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"]
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
# TODO: alt signal usage may be described by cp.vl['BLINKERS']['USE_ALT_LAMP']
left_blinker_sig, right_blinker_sig = "LEFT_LAMP", "RIGHT_LAMP"
if self.CP.carFingerprint == CAR.KONA_EV_2ND_GEN:
left_blinker_sig, right_blinker_sig = "LEFT_LAMP_ALT", "RIGHT_LAMP_ALT"
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"][left_blinker_sig],
cp.vl["BLINKERS"][right_blinker_sig])
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] != 0
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0
# cruise state
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
ret.cruiseState.available = self.main_enabled
if self.CP.openpilotLongitudinalControl:
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1
ret.cruiseState.standstill = False
else:
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2)
ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["CRUISE_STANDSTILL"] == 1
ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor
self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"])
# Manual Speed Limit Assist is a feature that replaces non-adaptive cruise control on EV CAN FD platforms.
# It limits the vehicle speed, overridable by pressing the accelerator past a certain point.
# The car will brake, but does not respect positive acceleration commands in this mode
# TODO: find this message on ICE & HYBRID cars + cruise control signals (if exists)
if self.CP.carFingerprint in EV_CAR:
ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
self.prev_cruise_buttons = self.cruise_buttons[-1]
self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
self.prev_main_buttons = self.main_buttons[-1]
self.main_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["ADAPTIVE_CRUISE_MAIN_BTN"])
if self.prev_main_buttons == 0 and self.main_buttons[-1] != 0:
self.main_enabled = not self.main_enabled
self.buttons_counter = cp.vl[self.cruise_btns_msg_canfd]["COUNTER"]
ret.accFaulted = cp.vl["TCS"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if self.CP.flags & HyundaiFlags.CANFD_HDA2:
self.hda2_lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x362"] if self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING
else cp_cam.vl["CAM_0x2a4"])
# Driving personalities function
if self.personalities_via_wheel and ret.cruiseState.available:
# Sync with the onroad UI button
if self.param_memory.get_bool("PersonalityChangedViaUI"):
self.personality_profile = self.param.get_int("LongitudinalPersonality")
self.param_memory.put_bool("PersonalityChangedViaUI", False)
# Change personality upon steering wheel button press
if self.cruise_buttons[-1] == Buttons.GAP_DIST and self.prev_cruise_buttons == 0:
self.param_memory.put_bool("PersonalityChangedViaWheel", True)
self.personality_profile = (self.previous_personality_profile + 2) % 3
if self.personality_profile != self.previous_personality_profile and self.personality_profile >= 0:
self.param.put_int("LongitudinalPersonality", self.personality_profile)
self.previous_personality_profile = self.personality_profile
# Toggle Experimental Mode from steering wheel function
if self.experimental_mode_via_lkas and ret.cruiseState.available:
lkas_pressed = cp.vl[self.cruise_btns_msg_canfd]["LKAS_BTN"]
if lkas_pressed and not self.lkas_previously_pressed:
if self.conditional_experimental_mode:
# Set "CEStatus" to work with "Conditional Experimental Mode"
conditional_status = self.param_memory.get_int("CEStatus")
override_value = 0 if conditional_status in (1, 2, 3, 4) else 1 if conditional_status >= 5 else 2
self.param_memory.put_int("CEStatus", override_value)
else:
experimental_mode = self.param.get_bool("ExperimentalMode")
# Invert the value of "ExperimentalMode"
put_bool_nonblocking("ExperimentalMode", not experimental_mode)
self.lkas_previously_pressed = lkas_pressed
return ret return ret
def get_can_parser(self, CP): def get_can_parser(self, CP):
if CP.carFingerprint in CANFD_CAR:
return self.get_can_parser_canfd(CP)
messages = [
# address, frequency
("MDPS12", 50),
("TCS13", 50),
("TCS15", 10),
("CLU11", 50),
("CLU15", 5),
("ESP12", 100),
("CGW1", 10),
("CGW2", 5),
("CGW4", 5),
("WHL_SPD11", 50),
("SAS11", 100),
]
if not CP.openpilotLongitudinalControl and CP.carFingerprint not in CAMERA_SCC_CAR:
messages += [
("SCC11", 50),
("SCC12", 50),
]
if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))
if CP.enableBsm:
messages.append(("LCA11", 50))
if CP.carFingerprint in (HYBRID_CAR | EV_CAR):
messages.append(("E_EMS11", 50))
else:
messages += [
("EMS12", 100),
("EMS16", 100),
]
if CP.carFingerprint in (HYBRID_CAR | EV_CAR):
messages.append(("ELECT_GEAR", 20))
elif CP.carFingerprint in CAN_GEARS["use_cluster_gears"]:
pass
elif CP.carFingerprint in CAN_GEARS["use_tcu_gears"]:
messages.append(("TCU12", 100))
else:
messages.append(("LVR12", 100))
messages.append(("BCM_PO_11", 50))
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 0)
@staticmethod
def get_cam_can_parser(CP):
if CP.carFingerprint in CANFD_CAR:
return CarState.get_cam_can_parser_canfd(CP)
messages = [
("LKAS11", 100)
]
if not CP.openpilotLongitudinalControl and CP.carFingerprint in CAMERA_SCC_CAR:
messages += [
("SCC11", 50),
("SCC12", 50),
]
if CP.flags & HyundaiFlags.USE_FCA.value:
messages.append(("FCA11", 50))
return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2)
def get_can_parser_canfd(self, CP):
messages = [ messages = [
(self.gear_msg_canfd, 100), (self.gear_msg_canfd, 100),
(self.accelerator_msg_canfd, 100), (self.accelerator_msg_canfd, 100),
@@ -415,6 +72,9 @@ class CarState(CarStateBase):
("BLINDSPOTS_REAR_CORNERS", 20), ("BLINDSPOTS_REAR_CORNERS", 20),
] ]
# if CP.nav_msg:
# messages.append(("CLUSTER_SPEED_LIMIT", 10))
if not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and not CP.openpilotLongitudinalControl: if not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and not CP.openpilotLongitudinalControl:
messages += [ messages += [
("SCC_CONTROL", 50), ("SCC_CONTROL", 50),
@@ -423,7 +83,7 @@ class CarState(CarStateBase):
return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).ECAN) return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).ECAN)
@staticmethod @staticmethod
def get_cam_can_parser_canfd(CP): def get_cam_can_parser(CP):
messages = [] messages = []
if CP.flags & HyundaiFlags.CANFD_HDA2: if CP.flags & HyundaiFlags.CANFD_HDA2:
block_lfa_msg = "CAM_0x362" if CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else "CAM_0x2a4" block_lfa_msg = "CAM_0x362" if CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else "CAM_0x2a4"

View File

@@ -0,0 +1,224 @@
from openpilot.common.numpy_fast import clip
from openpilot.selfdrive.car import CanBusBase
from openpilot.selfdrive.car.hyundai.values import HyundaiFlags
class CanBus(CanBusBase):
def __init__(self, CP, hda2=None, fingerprint=None) -> None:
super().__init__(CP, fingerprint)
if hda2 is None:
assert CP is not None
hda2 = CP.flags & HyundaiFlags.CANFD_HDA2.value
# On the CAN-FD platforms, the LKAS camera is on both A-CAN and E-CAN. HDA2 cars
# have a different harness than the HDA1 and non-HDA variants in order to split
# a different bus, since the steering is done by different ECUs.
self._a, self._e = 1, 0
if hda2:
self._a, self._e = 0, 1
self._a += self.offset
self._e += self.offset
self._cam = 2 + self.offset
@property
def ECAN(self):
return self._e
@property
def ACAN(self):
return self._a
@property
def CAM(self):
return self._cam
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_steer):
ret = []
values = {
"LKA_MODE": 2,
"LKA_ICON": 2 if enabled else 1 if lat_active else 0,
"TORQUE_REQUEST": apply_steer,
"LKA_ASSIST": 0,
"STEER_REQ": 1 if lat_active else 0,
"STEER_MODE": 0,
"HAS_LANE_SAFETY": 0, # hide LKAS settings
"NEW_SIGNAL_1": 0,
"NEW_SIGNAL_2": 0,
}
if CP.flags & HyundaiFlags.CANFD_HDA2:
hda2_lkas_msg = "LKAS_ALT" if CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else "LKAS"
if CP.openpilotLongitudinalControl:
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
ret.append(packer.make_can_msg(hda2_lkas_msg, CAN.ACAN, values))
else:
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
return ret
def create_suppress_lfa(packer, CAN, hda2_lfa_block_msg, hda2_alt_steering):
suppress_msg = "CAM_0x362" if hda2_alt_steering else "CAM_0x2a4"
msg_bytes = 32 if hda2_alt_steering else 24
values = {f"BYTE{i}": hda2_lfa_block_msg[f"BYTE{i}"] for i in range(3, msg_bytes) if i != 7}
values["COUNTER"] = hda2_lfa_block_msg["COUNTER"]
values["SET_ME_0"] = 0
values["SET_ME_0_2"] = 0
values["LEFT_LANE_LINE"] = 0
values["RIGHT_LANE_LINE"] = 0
return packer.make_can_msg(suppress_msg, CAN.ACAN, values)
def create_buttons(packer, CP, CAN, cnt, btn):
values = {
"COUNTER": cnt,
"SET_ME_1": 1,
"CRUISE_BUTTONS": btn,
}
bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_HDA2 else CAN.CAM
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
# TODO: why do we copy different values here?
if CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value:
values = {s: cruise_info_copy[s] for s in [
"COUNTER",
"CHECKSUM",
"NEW_SIGNAL_1",
"MainMode_ACC",
"ACCMode",
"ZEROS_9",
"CRUISE_STANDSTILL",
"ZEROS_5",
"DISTANCE_SETTING",
"VSetDis",
]}
else:
values = {s: cruise_info_copy[s] for s in [
"COUNTER",
"CHECKSUM",
"ACCMode",
"VSetDis",
"CRUISE_STANDSTILL",
]}
values.update({
"ACCMode": 4,
"aReqRaw": 0.0,
"aReqValue": 0.0,
})
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_lfahda_cluster(packer, CAN, enabled):
values = {
"HDA_ICON": 1 if enabled else 0,
# 0 off, 1 gray, 2 green, 3 blinking (wheel icon)
"LFA_ICON": 0 if enabled else 0, # was green before i think in 2? 3 should be flashing?
# "LFA_ICON": 2 if enabled else 0,
}
return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, personality):
jerk = 5
jn = jerk / 50
if not enabled or gas_override:
a_val, a_raw = 0, 0
else:
a_raw = accel
a_val = clip(accel, accel_last - jn, accel_last + jn)
values = {
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
"MainMode_ACC": 1,
"StopReq": 1 if stopping else 0,
"aReqValue": a_val,
"aReqRaw": a_raw,
"VSetDis": set_speed,
"JerkLowerLimit": jerk if enabled else 1,
"JerkUpperLimit": 3.0,
"ACC_ObjDist": 1,
"ObjValid": 0,
"OBJ_STATUS": 2,
"SET_ME_2": 0x4,
"SET_ME_3": 0x3,
"SET_ME_TMP_64": 0x64,
"DISTANCE_SETTING": personality + 1,
}
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_spas_messages(packer, CAN, frame, left_blink, right_blink):
ret = []
values = {
}
ret.append(packer.make_can_msg("SPAS1", CAN.ECAN, values))
blink = 0
if left_blink:
blink = 3
elif right_blink:
blink = 4
values = {
"BLINKER_CONTROL": blink,
}
ret.append(packer.make_can_msg("SPAS2", CAN.ECAN, values))
return ret
def create_adrv_messages(packer, CAN, frame):
# messages needed to car happy after disabling
# the ADAS Driving ECU to do longitudinal control
ret = []
values = {
}
ret.append(packer.make_can_msg("ADRV_0x51", CAN.ACAN, values))
if frame % 2 == 0:
values = {
'AEB_SETTING': 0x1, # show AEB disabled icon
'SET_ME_2': 0x2,
'SET_ME_FF': 0xff,
'SET_ME_FC': 0xfc,
'SET_ME_9': 0x9,
}
ret.append(packer.make_can_msg("ADRV_0x160", CAN.ECAN, values))
if frame % 5 == 0:
values = {
'SET_ME_1C': 0x1c,
'SET_ME_FF': 0xff,
'SET_ME_TMP_F': 0xf,
'SET_ME_TMP_F_2': 0xf,
}
ret.append(packer.make_can_msg("ADRV_0x1ea", CAN.ECAN, values))
values = {
'SET_ME_E1': 0xe1,
'SET_ME_3A': 0x3a,
}
ret.append(packer.make_can_msg("ADRV_0x200", CAN.ECAN, values))
if frame % 20 == 0:
values = {
'SET_ME_15': 0x15,
}
ret.append(packer.make_can_msg("ADRV_0x345", CAN.ECAN, values))
if frame % 100 == 0:
values = {
'SET_ME_22': 0x22,
'SET_ME_41': 0x41,
}
ret.append(packer.make_can_msg("ADRV_0x1da", CAN.ECAN, values))
return ret

View File

@@ -41,7 +41,7 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_steer):
values = { values = {
"LKA_MODE": 2, "LKA_MODE": 2,
"LKA_ICON": 2 if enabled else 1, "LKA_ICON": 2 if enabled else 1 if lat_active else 0,
"TORQUE_REQUEST": apply_steer, "TORQUE_REQUEST": apply_steer,
"LKA_ASSIST": 0, "LKA_ASSIST": 0,
"STEER_REQ": 1 if lat_active else 0, "STEER_REQ": 1 if lat_active else 0,
@@ -116,7 +116,9 @@ def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
def create_lfahda_cluster(packer, CAN, enabled): def create_lfahda_cluster(packer, CAN, enabled):
values = { values = {
"HDA_ICON": 1 if enabled else 0, "HDA_ICON": 1 if enabled else 0,
"LFA_ICON": 2 if enabled else 0, # 0 off, 1 gray, 2 green, 3 blinking (wheel icon)
"LFA_ICON": 0 if enabled else 0, # was green before i think in 2? 3 should be flashing?
# "LFA_ICON": 2 if enabled else 0,
} }
return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values) return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)
@@ -151,7 +153,6 @@ def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_ov
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values) return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_spas_messages(packer, CAN, frame, left_blink, right_blink): def create_spas_messages(packer, CAN, frame, left_blink, right_blink):
ret = [] ret = []

View File

@@ -9,6 +9,9 @@ from openpilot.selfdrive.car.hyundai.radar_interface import RADAR_START_ADDR
from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car import create_button_events, get_safety_config
from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.disable_ecu import disable_ecu
from openpilot.common.params import Params
params_memory = Params("/dev/shm/params")
Ecu = car.CarParams.Ecu Ecu = car.CarParams.Ecu
SafetyModel = car.CarParams.SafetyModel SafetyModel = car.CarParams.SafetyModel
@@ -35,6 +38,7 @@ class CarInterface(CarInterfaceBase):
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs): def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs):
ret.carName = "hyundai" ret.carName = "hyundai"
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or DBC[ret.carFingerprint]["radar"] is None
params_memory.put_bool("CSLCAvailable", True)
# These cars have been put into dashcam only due to both a lack of users and test coverage. # These cars have been put into dashcam only due to both a lack of users and test coverage.
# These cars likely still work fine. Once a user confirms each car works and a test route is # These cars likely still work fine. Once a user confirms each car works and a test route is
@@ -50,6 +54,7 @@ class CarInterface(CarInterfaceBase):
if hda2: if hda2:
if 0x110 in fingerprint[CAN.CAM]: if 0x110 in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
params_memory.put_bool("CSLCAvailable", False)
else: else:
# non-HDA2 # non-HDA2
if 0x1cf not in fingerprint[CAN.ECAN]: if 0x1cf not in fingerprint[CAN.ECAN]:
@@ -148,6 +153,7 @@ class CarInterface(CarInterfaceBase):
ret.steerRatio = 14.00 * 1.15 ret.steerRatio = 14.00 * 1.15
ret.tireStiffnessFactor = 0.385 ret.tireStiffnessFactor = 0.385
elif candidate in (CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN): elif candidate in (CAR.TUCSON_4TH_GEN, CAR.TUCSON_HYBRID_4TH_GEN):
ret.steerActuatorDelay = 0.1 # Or .15? BBOT test oscarpilot
ret.mass = 1630. # average ret.mass = 1630. # average
ret.wheelbase = 2.756 ret.wheelbase = 2.756
ret.steerRatio = 16. ret.steerRatio = 16.
@@ -291,11 +297,13 @@ class CarInterface(CarInterfaceBase):
ret.longitudinalActuatorDelayUpperBound = 0.5 ret.longitudinalActuatorDelayUpperBound = 0.5
# *** feature detection *** # *** feature detection ***
if candidate in CANFD_CAR: # if candidate in CANFD_CAR:
ret.enableBsm = 0x1e5 in fingerprint[CAN.ECAN] # ret.enableBsm = 0x1e5 in fingerprint[CAN.ECAN]
else: # ret.nav_msg = 0x544 in fingerprint[0]
ret.enableBsm = 0x58b in fingerprint[0] # else:
# ret.enableBsm = 0x58b in fingerprint[0]
# ret.nav_msg = False
# *** panda safety config *** # *** panda safety config ***
ret.safetyConfigs = set_safety_config_hyundai(candidate, CAN, can_fd=(candidate in CANFD_CAR)) ret.safetyConfigs = set_safety_config_hyundai(candidate, CAN, can_fd=(candidate in CANFD_CAR))
@@ -313,6 +321,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_CAMERA_SCC
if ret.openpilotLongitudinalControl: if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG
params_memory.put_bool("CSLCAvailable", False)
if candidate in HYBRID_CAR: if candidate in HYBRID_CAR:
ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_HYBRID_GAS ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_HYBRID_GAS
elif candidate in EV_CAR: elif candidate in EV_CAR:

View File

@@ -29,6 +29,8 @@ from openpilot.selfdrive.controls.lib.alertmanager import AlertManager, set_offr
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
from openpilot.system.hardware import HARDWARE from openpilot.system.hardware import HARDWARE
from openpilot.common.watcher import Watcher
SOFT_DISABLE_TIME = 3 # seconds SOFT_DISABLE_TIME = 3 # seconds
LDW_MIN_SPEED = 31 * CV.MPH_TO_MS LDW_MIN_SPEED = 31 * CV.MPH_TO_MS
LANE_DEPARTURE_THRESHOLD = 0.1 LANE_DEPARTURE_THRESHOLD = 0.1
@@ -131,6 +133,9 @@ class Controls:
self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX self.CP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX
# Test
Watcher.log_watch("always_on_lateral", self.always_on_lateral)
# read params # read params
self.is_metric = self.params.get_bool("IsMetric") self.is_metric = self.params.get_bool("IsMetric")
self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled") self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled")
@@ -605,6 +610,7 @@ class Controls:
self.state = State.enabled self.state = State.enabled
self.current_alert_types.append(ET.ENABLE) self.current_alert_types.append(ET.ENABLE)
self.v_cruise_helper.initialize_v_cruise(CS, self.experimental_mode, self.conditional_experimental_mode) self.v_cruise_helper.initialize_v_cruise(CS, self.experimental_mode, self.conditional_experimental_mode)
# self.v_cruise_helper.initialize_v_cruise(CS, self.frogpilot_variables, self.experimental_mode, self.conditional_experimental_mode)
# Check if openpilot is engaged and actuators are enabled # Check if openpilot is engaged and actuators are enabled
self.enabled = self.state in ENABLED_STATES self.enabled = self.state in ENABLED_STATES
@@ -659,7 +665,7 @@ class Controls:
self.lateral_allowed |= CS.cruiseState.enabled or (CS.cruiseState.available and self.always_on_lateral_main) self.lateral_allowed |= CS.cruiseState.enabled or (CS.cruiseState.available and self.always_on_lateral_main)
self.FPCC.alwaysOnLateral = self.lateral_allowed and driving_gear and signal_check self.FPCC.alwaysOnLateral = self.lateral_allowed and driving_gear and signal_check
if self.FPCC.alwaysOnLateral:/ if self.FPCC.alwaysOnLateral:
self.current_alert_types.append(ET.WARNING) self.current_alert_types.append(ET.WARNING)
# Check which actuators can be enabled # Check which actuators can be enabled
@@ -1002,6 +1008,8 @@ class Controls:
self.average_desired_curvature = self.params.get_bool("AverageCurvature") self.average_desired_curvature = self.params.get_bool("AverageCurvature")
self.conditional_experimental_mode = self.params.get_bool("ConditionalExperimental") self.conditional_experimental_mode = self.params.get_bool("ConditionalExperimental")
# self.frogpilot_variables.CSLC = self.params.get_bool("CSLCEnabled")
# self.frogpilot_variables.CSLCA = self.params.get_bool("CSLCAvailable")
custom_theme = self.params.get_bool("CustomTheme") custom_theme = self.params.get_bool("CustomTheme")
custom_sounds = self.params.get_int("CustomSounds") if custom_theme else 0 custom_sounds = self.params.get_int("CustomSounds") if custom_theme else 0

View File

@@ -92,6 +92,7 @@ class DesireHelper:
self.lane_change_state = LaneChangeState.off self.lane_change_state = LaneChangeState.off
self.lane_change_direction = LaneChangeDirection.none self.lane_change_direction = LaneChangeDirection.none
elif one_blinker and below_lane_change_speed and frogpilot_planner.turn_desires: elif one_blinker and below_lane_change_speed and frogpilot_planner.turn_desires:
# BBOT
self.turn_direction = TurnDirection.turnLeft if carstate.leftBlinker else TurnDirection.turnRight self.turn_direction = TurnDirection.turnLeft if carstate.leftBlinker else TurnDirection.turnRight
# Set the "turn_completed" flag to prevent lane changes after completing a turn # Set the "turn_completed" flag to prevent lane changes after completing a turn
self.turn_completed = True self.turn_completed = True

View File

@@ -138,6 +138,11 @@ class VCruiseHelper:
# initializing is handled by the PCM # initializing is handled by the PCM
if self.CP.pcmCruise: if self.CP.pcmCruise:
return return
# if frogpilot_variables.CSLC:
# self.v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
# self.v_cruise_cluster_kph = self.v_cruise_kph
# return
initial = V_CRUISE_INITIAL_EXPERIMENTAL_MODE if experimental_mode and not conditional_experimental_mode else V_CRUISE_INITIAL initial = V_CRUISE_INITIAL_EXPERIMENTAL_MODE if experimental_mode and not conditional_experimental_mode else V_CRUISE_INITIAL

View File

@@ -231,7 +231,7 @@ def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubM
if "REPLAY" in os.environ: if "REPLAY" in os.environ:
branch = "replay" branch = "replay"
return StartupAlert("Hippity hoppity this is my property", "so I do what I want 🐸", alert_status=AlertStatus.frogpilot) return StartupAlert("Oscarpilot ready", "Be excellent to eachother", alert_status=AlertStatus.frogpilot)
def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert:
return NoEntryAlert(f"Drive above {get_display_speed(CP.minEnableSpeed, metric)} to engage") return NoEntryAlert(f"Drive above {get_display_speed(CP.minEnableSpeed, metric)} to engage")
@@ -373,7 +373,8 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = {
}, },
EventName.startupMaster: { EventName.startupMaster: {
ET.PERMANENT: startup_master_alert, # ET.PERMANENT: startup_master_alert,
ET.PERMANENT: StartupAlert("OscarPilot Ready"),
}, },
# Car is recognized, but marked as dashcam only # Car is recognized, but marked as dashcam only

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -135,7 +135,7 @@ class ConditionalExperimentalMode:
self.lead_detection(radarState) self.lead_detection(radarState)
self.road_curvature(modelData, v_ego) self.road_curvature(modelData, v_ego)
self.slow_lead(mpc, radarState, v_ego) self.slow_lead(mpc, radarState, v_ego)
self.stop_sign_and_light(modelData, v_ego) # self.stop_sign_and_light(modelData, v_ego)
self.v_ego_slowing_down(v_ego) self.v_ego_slowing_down(v_ego)
self.v_lead_slowing_down(mpc, radarState) self.v_lead_slowing_down(mpc, radarState)

View File

@@ -56,6 +56,7 @@ def calculate_lane_width(lane, current_lane, road_edge):
class FrogPilotPlanner: class FrogPilotPlanner:
def __init__(self, params, params_memory): def __init__(self, params, params_memory):
self.params_memory = params_memory
self.cem = ConditionalExperimentalMode() self.cem = ConditionalExperimentalMode()
self.mtsc = MapTurnSpeedController() self.mtsc = MapTurnSpeedController()
@@ -105,6 +106,8 @@ class FrogPilotPlanner:
self.x_desired_trajectory_full = np.interp(ModelConstants.T_IDXS, T_IDXS_MPC, mpc.x_solution) self.x_desired_trajectory_full = np.interp(ModelConstants.T_IDXS, T_IDXS_MPC, mpc.x_solution)
self.x_desired_trajectory = self.x_desired_trajectory_full[:CONTROL_N] self.x_desired_trajectory = self.x_desired_trajectory_full[:CONTROL_N]
# self.params_memory.put_int("CSLCSpeed", int(round(self.v_cruise * CV.MS_TO_MPH)))
def update_v_cruise(self, carState, controlsState, modelData, enabled, v_cruise, v_ego): def update_v_cruise(self, carState, controlsState, modelData, enabled, v_cruise, v_ego):
# Pfeiferj's Map Turn Speed Controller # Pfeiferj's Map Turn Speed Controller
if self.map_turn_speed_controller: if self.map_turn_speed_controller:
@@ -168,7 +171,12 @@ class FrogPilotPlanner:
else: else:
self.vtsc_target = v_cruise self.vtsc_target = v_cruise
# if self.CSLC:
# v_ego_diff = 0
# else:
v_ego_diff = max(carState.vEgoRaw - carState.vEgoCluster, 0) v_ego_diff = max(carState.vEgoRaw - carState.vEgoCluster, 0)
return min(v_cruise, self.mtsc_target, self.slc_target, self.vtsc_target) - v_ego_diff return min(v_cruise, self.mtsc_target, self.slc_target, self.vtsc_target) - v_ego_diff
def publish_lateral(self, sm, pm, DH): def publish_lateral(self, sm, pm, DH):
@@ -206,6 +214,8 @@ class FrogPilotPlanner:
def update_frogpilot_params(self, params, params_memory): def update_frogpilot_params(self, params, params_memory):
self.is_metric = params.get_bool("IsMetric") self.is_metric = params.get_bool("IsMetric")
# self.CSLC = params.get_bool("CSLCEnabled")
self.blindspot_path = params.get_bool("CustomUI") and params.get_bool("BlindSpotPath") self.blindspot_path = params.get_bool("CustomUI") and params.get_bool("BlindSpotPath")
self.conditional_experimental_mode = params.get_bool("ConditionalExperimental") self.conditional_experimental_mode = params.get_bool("ConditionalExperimental")

View File

@@ -27,7 +27,7 @@ def set_model_list_parameter(params):
if previous_model_list != model_list: if previous_model_list != model_list:
# Reset the selected model if the model list changed # Reset the selected model if the model list changed
params.put_int("Model", 0) params.put_int("Model", 3)
params.put("ModelList", model_list) params.put("ModelList", model_list)
params.remove("CalibrationParams"); params.remove("CalibrationParams");
params.remove("LiveTorqueParameters"); params.remove("LiveTorqueParameters");

View File

@@ -179,7 +179,7 @@ private:
background-color: #4a4a4a; background-color: #4a4a4a;
} }
QPushButton:checked:enabled { QPushButton:checked:enabled {
background-color: #33Ab4C; background-color: #0048FF;
} }
QPushButton:disabled { QPushButton:disabled {
color: #33E4E4E4; color: #33E4E4E4;

View File

@@ -404,7 +404,7 @@ QString SelectMaps::activeButtonStyle = R"(
border-width: 0; border-width: 0;
border-radius: 30px; border-radius: 30px;
color: #dddddd; color: #dddddd;
background-color: #33Ab4C; background-color: #0048FF;
)"; )";
QString SelectMaps::normalButtonStyle = R"( QString SelectMaps::normalButtonStyle = R"(

View File

@@ -1,7 +1,7 @@
#include "selfdrive/frogpilot/ui/control_settings.h" #include "selfdrive/frogpilot/ui/control_settings.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPilotListWidget(parent) { FrogPilotControlsPanel::FrogPilotControlsPanel(OscarSettingsWindow *parent) : FrogPilotListWidget(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> controlToggles { const std::vector<std::tuple<QString, QString, QString, QString>> controlToggles {
{"AdjustablePersonalities", "Adjustable Personalities", "Use the 'Distance' button on the steering wheel or the onroad UI to switch between openpilot's driving personalities.\n\n1 bar = Aggressive\n2 bars = Standard\n3 bars = Relaxed", "../frogpilot/assets/toggle_icons/icon_distance.png"}, {"AdjustablePersonalities", "Adjustable Personalities", "Use the 'Distance' button on the steering wheel or the onroad UI to switch between openpilot's driving personalities.\n\n1 bar = Aggressive\n2 bars = Standard\n3 bars = Relaxed", "../frogpilot/assets/toggle_icons/icon_distance.png"},
{"AlwaysOnLateral", "Always on Lateral", "Maintain openpilot lateral control when the brake or gas pedals are used.\n\nDeactivation occurs only through the 'Cruise Control' button.", "../frogpilot/assets/toggle_icons/icon_always_on_lateral.png"}, {"AlwaysOnLateral", "Always on Lateral", "Maintain openpilot lateral control when the brake or gas pedals are used.\n\nDeactivation occurs only through the 'Cruise Control' button.", "../frogpilot/assets/toggle_icons/icon_always_on_lateral.png"},
@@ -10,9 +10,9 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil
{"CECurves", "Curve Detected Ahead", "Switch to 'Experimental Mode' when a curve is detected.", ""}, {"CECurves", "Curve Detected Ahead", "Switch to 'Experimental Mode' when a curve is detected.", ""},
{"CENavigation", "Navigation Based", "Switch to 'Experimental Mode' based on navigation data. (i.e. Intersections, stop signs, etc.)", ""}, {"CENavigation", "Navigation Based", "Switch to 'Experimental Mode' based on navigation data. (i.e. Intersections, stop signs, etc.)", ""},
{"CESlowerLead", "Slower Lead Detected Ahead", "Switch to 'Experimental Mode' when a slower lead vehicle is detected ahead.", ""}, {"CESlowerLead", "Slower Lead Detected Ahead", "Switch to 'Experimental Mode' when a slower lead vehicle is detected ahead.", ""},
{"CEStopLights", "Stop Lights and Stop Signs", "Switch to 'Experimental Mode' when a stop light or stop sign is detected.", ""}, // {"CEStopLights", "Stop Lights and Stop Signs", "Switch to 'Experimental Mode' when a stop light or stop sign is detected.", ""},
{"CESignal", "Turn Signal When Below Highway Speeds", "Switch to 'Experimental Mode' when using turn signals below highway speeds to help assit with turns.", ""}, {"CESignal", "Turn Signal When Below Highway Speeds", "Switch to 'Experimental Mode' when using turn signals below highway speeds to help assit with turns.", ""},
{"CSLCEnabled", "Custom Stock Longitudinal Control", "Use cruise control button spamming to adjust cruise set speed based on MTSC, VTSC, and SLC", "../frogpilot/assets/toggle_icons/icon_custom.png"},
{"CustomPersonalities", "Custom Driving Personalities", "Customize the driving personality profiles to your driving style.", "../frogpilot/assets/toggle_icons/icon_custom.png"}, {"CustomPersonalities", "Custom Driving Personalities", "Customize the driving personality profiles to your driving style.", "../frogpilot/assets/toggle_icons/icon_custom.png"},
{"DeviceShutdown", "Device Shutdown Timer", "Configure the timer for automatic device shutdown when offroad conserving energy and preventing battery drain.", "../frogpilot/assets/toggle_icons/icon_time.png"}, {"DeviceShutdown", "Device Shutdown Timer", "Configure the timer for automatic device shutdown when offroad conserving energy and preventing battery drain.", "../frogpilot/assets/toggle_icons/icon_time.png"},
{"ExperimentalModeActivation", "Experimental Mode Via", "Toggle Experimental Mode by double-clicking the 'Lane Departure'/'LKAS' button or double tapping screen.\n\nOverrides 'Conditional Experimental Mode'.", "../assets/img_experimental_white.svg"}, {"ExperimentalModeActivation", "Experimental Mode Via", "Toggle Experimental Mode by double-clicking the 'Lane Departure'/'LKAS' button or double tapping screen.\n\nOverrides 'Conditional Experimental Mode'.", "../assets/img_experimental_white.svg"},
@@ -414,16 +414,16 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil
QObject::connect(uiState(), &UIState::offroadTransition, this, [this](bool offroad) { QObject::connect(uiState(), &UIState::offroadTransition, this, [this](bool offroad) {
if (!offroad) { if (!offroad) {
updateCarToggles(); updateCarToggles();
} }
}); });
QObject::connect(device(), &Device::interactiveTimeout, this, &FrogPilotControlsPanel::hideSubToggles); QObject::connect(device(), &Device::interactiveTimeout, this, &FrogPilotControlsPanel::hideSubToggles);
QObject::connect(parent, &SettingsWindow::closeParentToggle, this, &FrogPilotControlsPanel::hideSubToggles); QObject::connect(parent, &OscarSettingsWindow::closeParentToggle, this, &FrogPilotControlsPanel::hideSubToggles);
QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotControlsPanel::updateMetric); // QObject::connect(parent, &OscarSettingsWindow::updateMetric, this, &FrogPilotControlsPanel::updateMetric);
hideSubToggles(); hideSubToggles();
updateMetric(); // updateMetric();
} }
void FrogPilotControlsPanel::updateCarToggles() { void FrogPilotControlsPanel::updateCarToggles() {

View File

@@ -0,0 +1,559 @@
#include "selfdrive/frogpilot/ui/control_settings.h"
#include "selfdrive/ui/ui.h"
FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPilotListWidget(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> controlToggles {
{"AdjustablePersonalities", "Adjustable Personalities", "Use the 'Distance' button on the steering wheel or the onroad UI to switch between openpilot's driving personalities.\n\n1 bar = Aggressive\n2 bars = Standard\n3 bars = Relaxed", "../frogpilot/assets/toggle_icons/icon_distance.png"},
{"AlwaysOnLateral", "Always on Lateral", "Maintain openpilot lateral control when the brake or gas pedals are used.\n\nDeactivation occurs only through the 'Cruise Control' button.", "../frogpilot/assets/toggle_icons/icon_always_on_lateral.png"},
{"ConditionalExperimental", "Conditional Experimental Mode", "Automatically switches to 'Experimental Mode' under predefined conditions.", "../frogpilot/assets/toggle_icons/icon_conditional.png"},
{"CECurves", "Curve Detected Ahead", "Switch to 'Experimental Mode' when a curve is detected.", ""},
{"CENavigation", "Navigation Based", "Switch to 'Experimental Mode' based on navigation data. (i.e. Intersections, stop signs, etc.)", ""},
{"CESlowerLead", "Slower Lead Detected Ahead", "Switch to 'Experimental Mode' when a slower lead vehicle is detected ahead.", ""},
// {"CEStopLights", "Stop Lights and Stop Signs", "Switch to 'Experimental Mode' when a stop light or stop sign is detected.", ""},
{"CESignal", "Turn Signal When Below Highway Speeds", "Switch to 'Experimental Mode' when using turn signals below highway speeds to help assit with turns.", ""},
{"CSLCEnabled", "Custom Stock Longitudinal Control", "Use cruise control button spamming to adjust cruise set speed based on MTSC, VTSC, and SLC", "../frogpilot/assets/toggle_icons/icon_custom.png"},
{"CustomPersonalities", "Custom Driving Personalities", "Customize the driving personality profiles to your driving style.", "../frogpilot/assets/toggle_icons/icon_custom.png"},
{"DeviceShutdown", "Device Shutdown Timer", "Configure the timer for automatic device shutdown when offroad conserving energy and preventing battery drain.", "../frogpilot/assets/toggle_icons/icon_time.png"},
{"ExperimentalModeActivation", "Experimental Mode Via", "Toggle Experimental Mode by double-clicking the 'Lane Departure'/'LKAS' button or double tapping screen.\n\nOverrides 'Conditional Experimental Mode'.", "../assets/img_experimental_white.svg"},
{"FireTheBabysitter", "Fire the Babysitter", "Deactivate some of openpilot's 'Babysitter' protocols for more user autonomy.", "../frogpilot/assets/toggle_icons/icon_babysitter.png"},
{"NoLogging", "Disable All Logging", "Turn off all data tracking to enhance privacy or reduce thermal load.\n\nWARNING: This action will prevent drive recording and data cannot be recovered!", ""},
{"MuteDoor", "Mute Door Open Alert", "Disable alerts for open doors.", ""},
{"MuteDM", "Mute Driver Monitoring", "Disable driver monitoring.", ""},
{"MuteOverheated", "Mute Overheated System Alert", "Disable alerts for the device being overheated.", ""},
{"MuteSeatbelt", "Mute Seatbelt Unlatched Alert", "Disable alerts for unlatched seatbelts.", ""},
{"OfflineMode", "Offline Mode", "Allow the device to be offline indefinitely.", ""},
{"LateralTune", "Lateral Tuning", "Modify openpilot's steering behavior.", "../frogpilot/assets/toggle_icons/icon_lateral_tune.png"},
{"AverageCurvature", "Average Desired Curvature", "Use Pfeiferj's distance-based curvature adjustment for improved curve handling.", ""},
{"NNFF", "NNFF - Neural Network Feedforward", "Use Twilsonco's Neural Network Feedforward for enhanced precision in lateral control.", ""},
{"SteerRatio", steerRatioStock != 0 ? QString("Steer Ratio (Default: %1)").arg(steerRatioStock, 0, 'f', 2) : "Steer Ratio", "Set a custom steer ratio for your vehicle controls.", ""},
{"LongitudinalTune", "Longitudinal Tuning", "Modify openpilot's acceleration and braking behavior.", "../frogpilot/assets/toggle_icons/icon_longitudinal_tune.png"},
{"AccelerationProfile", "Acceleration Profile", "Change the acceleration rate to be either sporty or eco-friendly.", ""},
{"AggressiveAcceleration", "Aggressive Acceleration With Lead", "Increase acceleration aggressiveness when following a lead vehicle from a stop.", ""},
{"StoppingDistance", "Increased Stopping Distance", "Increase the stopping distance for a more comfortable stop.", ""},
{"SmoothBraking", "Smoother Braking Behind Lead", "Smoothen out the braking behavior when approaching slower vehicles.", ""},
{"Model", "Model Selector", "Choose your preferred openpilot model.", "../assets/offroad/icon_calibration.png"},
{"MTSCEnabled", "Map Turn Speed Control", "Slow down for anticipated curves detected by your downloaded maps.", "../frogpilot/assets/toggle_icons/icon_speed_map.png"},
{"NudgelessLaneChange", "Nudgeless Lane Change", "Enable lane changes without manual steering input.", "../frogpilot/assets/toggle_icons/icon_lane.png"},
{"LaneChangeTime", "Lane Change Timer", "Specify a delay before executing a nudgeless lane change.", ""},
{"LaneDetection", "Lane Detection", "Block nudgeless lane changes when a lane isn't detected.", ""},
{"OneLaneChange", "One Lane Change Per Signal", "Limit to one nudgeless lane change per turn signal activation.", ""},
{"QOLControls", "Quality of Life", "Miscellaneous quality of life changes to improve your overall openpilot experience.", "../frogpilot/assets/toggle_icons/quality_of_life.png"},
{"DisableOnroadUploads", "Disable Onroad Uploads", "Prevent large data uploads when onroad.", ""},
{"HigherBitrate", "Higher Bitrate Recording", "Increases the quality of the footage uploaded to comma connect.", ""},
{"PauseLateralOnSignal", "Pause Lateral On Turn Signal Below", "Temporarily disable lateral control during turn signal use below the set speed.", ""},
{"ReverseCruise", "Reverse Cruise Increase", "Reverses the 'long press' functionality when increasing the max set speed. Useful to increase the max speed quickly.", ""},
{"SetSpeedOffset", "Set Speed Offset", "Set an offset for your desired set speed.", ""},
{"SpeedLimitController", "Speed Limit Controller", "Automatically adjust vehicle speed to match speed limits using 'Open Street Map's, 'Navigate On openpilot', or your car's dashboard (TSS2 Toyotas only).", "../assets/offroad/icon_speed_limit.png"},
{"Offset1", "Speed Limit Offset (0-34 mph)", "Speed limit offset for speed limits between 0-34 mph.", ""},
{"Offset2", "Speed Limit Offset (35-54 mph)", "Speed limit offset for speed limits between 35-54 mph.", ""},
{"Offset3", "Speed Limit Offset (55-64 mph)", "Speed limit offset for speed limits between 55-64 mph.", ""},
{"Offset4", "Speed Limit Offset (65-99 mph)", "Speed limit offset for speed limits between 65-99 mph.", ""},
{"SLCFallback", "Fallback Method", "Choose your fallback method for when there are no speed limits currently being obtained from Navigation, OSM, and the car's dashboard.", ""},
{"SLCOverride", "Override Method", "Choose your preferred method to override the current speed limit.", ""},
{"SLCPriority", "Priority Order", "Determine the priority order for what speed limits to use.", ""},
{"TurnDesires", "Use Turn Desires", "Use turn desires for enhanced precision in turns below the minimum lane change speed.", "../assets/navigation/direction_continue_right.png"},
{"VisionTurnControl", "Vision Turn Speed Controller", "Slow down for detected road curvature for smoother curve handling.", "../frogpilot/assets/toggle_icons/icon_vtc.png"},
{"CurveSensitivity", "Curve Detection Sensitivity", "Set curve detection sensitivity. Higher values prompt earlier responses, lower values lead to smoother but later reactions.", ""},
{"TurnAggressiveness", "Turn Speed Aggressiveness", "Set turn speed aggressiveness. Higher values result in faster turns, lower values yield gentler turns.", ""},
};
for (const auto &[param, title, desc, icon] : controlToggles) {
ParamControl *toggle;
if (param == "AdjustablePersonalities") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 3, {{0, "None"}, {1, "Steering Wheel"}, {2, "Onroad UI Button"}, {3, "Wheel + Button"}}, this, true);
} else if (param == "AlwaysOnLateral") {
std::vector<QString> aolToggles{"AlwaysOnLateralMain"};
std::vector<QString> aolToggleNames{tr("Enable On Cruise Main")};
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, aolToggles, aolToggleNames);
QObject::connect(static_cast<FrogPilotParamToggleControl*>(toggle), &FrogPilotParamToggleControl::buttonClicked, [this](const bool checked) {
if (checked) {
FrogPilotConfirmationDialog::toggleAlert("WARNING: This is very experimental and isn't guaranteed to work. If you run into any issues, please report it in the FrogPilot Discord!",
"I understand the risks.", this);
}
if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) {
Hardware::reboot();
}
});
} else if (param == "ConditionalExperimental") {
FrogPilotParamManageControl *conditionalExperimentalToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(conditionalExperimentalToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
conditionalSpeedsImperial->setVisible(!isMetric);
conditionalSpeedsMetric->setVisible(isMetric);
for (auto &[key, toggle] : toggles) {
toggle->setVisible(conditionalExperimentalKeys.find(key.c_str()) != conditionalExperimentalKeys.end());
}
});
toggle = conditionalExperimentalToggle;
} else if (param == "CECurves") {
FrogPilotParamValueControl *CESpeedImperial = new FrogPilotParamValueControl("CESpeed", "Below", "Switch to 'Experimental Mode' below this speed in absence of a lead vehicle.", "", 0, 99,
std::map<int, QString>(), this, false, " mph");
FrogPilotParamValueControl *CESpeedLeadImperial = new FrogPilotParamValueControl("CESpeedLead", " w/Lead", "Switch to 'Experimental Mode' below this speed when following a lead vehicle.", "", 0, 99,
std::map<int, QString>(), this, false, " mph");
conditionalSpeedsImperial = new FrogPilotDualParamControl(CESpeedImperial, CESpeedLeadImperial, this);
addItem(conditionalSpeedsImperial);
FrogPilotParamValueControl *CESpeedMetric = new FrogPilotParamValueControl("CESpeed", "Below", "Switch to 'Experimental Mode' below this speed in absence of a lead vehicle.", "", 0, 150,
std::map<int, QString>(), this, false, " kph");
FrogPilotParamValueControl *CESpeedLeadMetric = new FrogPilotParamValueControl("CESpeedLead", " w/Lead", "Switch to 'Experimental Mode' below this speed when following a lead vehicle.", "", 0, 150,
std::map<int, QString>(), this, false, " kph");
conditionalSpeedsMetric = new FrogPilotDualParamControl(CESpeedMetric, CESpeedLeadMetric, this);
addItem(conditionalSpeedsMetric);
std::vector<QString> curveToggles{"CECurvesLead"};
std::vector<QString> curveToggleNames{tr("With Lead")};
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, curveToggles, curveToggleNames);
} else if (param == "CEStopLights") {
std::vector<QString> stopLightToggles{"CEStopLightsLead"};
std::vector<QString> stopLightToggleNames{tr("With Lead")};
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, stopLightToggles, stopLightToggleNames);
} else if (param == "CustomPersonalities") {
FrogPilotParamManageControl *customPersonalitiesToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(customPersonalitiesToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(false);
}
aggressiveProfile->setVisible(true);
standardProfile->setVisible(true);
relaxedProfile->setVisible(true);
});
toggle = customPersonalitiesToggle;
FrogPilotParamValueControl *aggressiveFollow = new FrogPilotParamValueControl("AggressiveFollow", "Follow",
"Set the 'Aggressive' personality' following distance. Represents seconds to follow behind the lead vehicle.\n\nStock: 1.25 seconds.", "../frogpilot/assets/other_images/aggressive.png",
10, 50, std::map<int, QString>(), this, false, " sec", 10);
FrogPilotParamValueControl *aggressiveJerk = new FrogPilotParamValueControl("AggressiveJerk", " Jerk",
"Configure brake/gas pedal responsiveness for the 'Aggressive' personality. Higher values yield a more 'relaxed' response.\n\nStock: 0.5.", "", 1, 50,
std::map<int, QString>(), this, false, "", 10);
aggressiveProfile = new FrogPilotDualParamControl(aggressiveFollow, aggressiveJerk, this, true);
addItem(aggressiveProfile);
FrogPilotParamValueControl *standardFollow = new FrogPilotParamValueControl("StandardFollow", "Follow",
"Set the 'Standard' personality following distance. Represents seconds to follow behind the lead vehicle.\n\nStock: 1.45 seconds.", "../frogpilot/assets/other_images/standard.png",
10, 50, std::map<int, QString>(), this, false, " sec", 10);
FrogPilotParamValueControl *standardJerk = new FrogPilotParamValueControl("StandardJerk", " Jerk",
"Adjust brake/gas pedal responsiveness for the 'Standard' personality. Higher values yield a more 'relaxed' response.\n\nStock: 1.0.", "", 1, 50,
std::map<int, QString>(), this, false, "", 10);
standardProfile = new FrogPilotDualParamControl(standardFollow, standardJerk, this, true);
addItem(standardProfile);
FrogPilotParamValueControl *relaxedFollow = new FrogPilotParamValueControl("RelaxedFollow", "Follow",
"Set the 'Relaxed' personality following distance. Represents seconds to follow behind the lead vehicle.\n\nStock: 1.75 seconds.", "../frogpilot/assets/other_images/relaxed.png",
10, 50, std::map<int, QString>(), this, false, " sec", 10);
FrogPilotParamValueControl *relaxedJerk = new FrogPilotParamValueControl("RelaxedJerk", " Jerk",
"Set brake/gas pedal responsiveness for the 'Relaxed' personality. Higher values yield a more 'relaxed' response.\n\nStock: 1.0.", "", 1, 50,
std::map<int, QString>(), this, false, "", 10);
relaxedProfile = new FrogPilotDualParamControl(relaxedFollow, relaxedJerk, this, true);
addItem(relaxedProfile);
} else if (param == "DeviceShutdown") {
std::map<int, QString> shutdownLabels;
for (int i = 0; i <= 33; ++i) {
shutdownLabels[i] = i == 0 ? "Instant" : i <= 3 ? QString::number(i * 15) + " mins" : QString::number(i - 3) + (i == 4 ? " hour" : " hours");
}
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 33, shutdownLabels, this, false);
} else if (param == "ExperimentalModeActivation") {
std::vector<QString> experimentalModeToggles{"ExperimentalModeViaLKAS", "ExperimentalModeViaScreen"};
std::vector<QString> experimentalModeNames{tr("LKAS Button"), tr("Screen")};
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, experimentalModeToggles, experimentalModeNames);
} else if (param == "FireTheBabysitter") {
FrogPilotParamManageControl *fireTheBabysitterToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(fireTheBabysitterToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(fireTheBabysitterKeys.find(key.c_str()) != fireTheBabysitterKeys.end());
}
});
toggle = fireTheBabysitterToggle;
} else if (param == "LateralTune") {
FrogPilotParamManageControl *lateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(lateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(lateralTuneKeys.find(key.c_str()) != lateralTuneKeys.end());
}
});
toggle = lateralTuneToggle;
} else if (param == "SteerRatio") {
toggle = new FrogPilotParamValueControlFloat(param, title, desc, icon, steerRatioStock * 0.75, steerRatioStock * 1.25, std::map<int, QString>(), this, false, "", 10.0);
} else if (param == "LongitudinalTune") {
FrogPilotParamManageControl *longitudinalTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(longitudinalTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(longitudinalTuneKeys.find(key.c_str()) != longitudinalTuneKeys.end());
}
});
toggle = longitudinalTuneToggle;
} else if (param == "AccelerationProfile") {
std::vector<QString> profileOptions{tr("Standard"), tr("Eco"), tr("Sport"), tr("Sport+")};
FrogPilotButtonParamControl *profileSelection = new FrogPilotButtonParamControl(param, title, desc, icon, profileOptions);
toggle = profileSelection;
QObject::connect(static_cast<FrogPilotButtonParamControl*>(toggle), &FrogPilotButtonParamControl::buttonClicked, [this](int id) {
if (id == 3) {
FrogPilotConfirmationDialog::toggleAlert("WARNING: This maxes out openpilot's acceleration from 2.0 m/s to 4.0 m/s and may cause oscillations when accelerating!",
"I understand the risks.", this);
}
});
} else if (param == "StoppingDistance") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, std::map<int, QString>(), this, false, " feet");
} else if (param == "Model") {
modelSelectorButton = new FrogPilotButtonIconControl(title, tr("SELECT"), desc, icon);
QStringList models = {"New Delhi (Default)", "Blue Diamond V1", "Blue Diamond V2", "Farmville", "New Lemon Pie"};
QObject::connect(modelSelectorButton, &FrogPilotButtonIconControl::clicked, this, [this, models]() {
int currentModel = params.getInt("Model");
QString currentModelLabel = models[currentModel];
QString selection = MultiOptionDialog::getSelection(tr("Select a driving model"), models, currentModelLabel, this);
if (!selection.isEmpty()) {
int selectedModel = models.indexOf(selection);
params.putInt("Model", selectedModel);
params.remove("CalibrationParams");
params.remove("LiveTorqueParameters");
modelSelectorButton->setValue(selection);
if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) {
Hardware::reboot();
}
}
});
modelSelectorButton->setValue(models[params.getInt("Model")]);
addItem(modelSelectorButton);
} else if (param == "NudgelessLaneChange") {
FrogPilotParamManageControl *laneChangeToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(laneChangeToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(laneChangeKeys.find(key.c_str()) != laneChangeKeys.end());
}
});
toggle = laneChangeToggle;
} else if (param == "LaneChangeTime") {
std::map<int, QString> laneChangeTimeLabels;
for (int i = 0; i <= 10; ++i) {
laneChangeTimeLabels[i] = i == 0 ? "Instant" : QString::number(i / 2.0) + " seconds";
}
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, laneChangeTimeLabels, this, false);
} else if (param == "QOLControls") {
FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(qolKeys.find(key.c_str()) != qolKeys.end());
}
});
toggle = qolToggle;
} else if (param == "PauseLateralOnSignal") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, std::map<int, QString>(), this, false, " mph");
} else if (param == "SetSpeedOffset") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, std::map<int, QString>(), this, false, " mph");
} else if (param == "SpeedLimitController") {
FrogPilotParamManageControl *speedLimitControllerToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(speedLimitControllerToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(speedLimitControllerKeys.find(key.c_str()) != speedLimitControllerKeys.end());
}
slscPriorityButton->setVisible(true);
});
toggle = speedLimitControllerToggle;
} else if (param == "Offset1" || param == "Offset2" || param == "Offset3" || param == "Offset4") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, std::map<int, QString>(), this, false, " mph");
} else if (param == "SLCFallback") {
std::vector<QString> fallbackOptions{tr("None"), tr("Experimental Mode"), tr("Previous Limit")};
FrogPilotButtonParamControl *fallbackSelection = new FrogPilotButtonParamControl(param, title, desc, icon, fallbackOptions);
toggle = fallbackSelection;
} else if (param == "SLCOverride") {
std::vector<QString> overrideOptions{tr("None"), tr("Manual Set Speed"), tr("Max Set Speed")};
FrogPilotButtonParamControl *overrideSelection = new FrogPilotButtonParamControl(param, title, desc, icon, overrideOptions);
toggle = overrideSelection;
} else if (param == "SLCPriority") {
const QStringList priorities {
"Navigation, Dashboard, Offline Maps",
"Navigation, Offline Maps, Dashboard",
"Navigation, Offline Maps",
"Navigation, Dashboard",
"Navigation",
"Offline Maps, Dashboard, Navigation",
"Offline Maps, Navigation, Dashboard",
"Offline Maps, Navigation",
"Offline Maps, Dashboard",
"Offline Maps",
"Dashboard, Navigation, Offline Maps",
"Dashboard, Offline Maps, Navigation",
"Dashboard, Offline Maps",
"Dashboard, Navigation",
"Dashboard",
"Highest",
"Lowest",
"",
};
slscPriorityButton = new ButtonControl(title, tr("SELECT"), desc);
QObject::connect(slscPriorityButton, &ButtonControl::clicked, this, [this, priorities]() {
QStringList availablePriorities = {"Dashboard", "Navigation", "Offline Maps", "Highest", "Lowest", "None"};
QStringList selectedPriorities;
int priorityValue = -1;
QStringList priorityPrompts = {tr("Select your primary priority"), tr("Select your secondary priority"), tr("Select your tertiary priority")};
for (int i = 0; i < 3; ++i) {
QString selection = MultiOptionDialog::getSelection(priorityPrompts[i], availablePriorities, "", this);
if (selection.isEmpty()) break;
if (selection == "None") {
priorityValue = 17;
break;
} else if (selection == "Highest") {
priorityValue = 15;
break;
} else if (selection == "Lowest") {
priorityValue = 16;
break;
} else {
selectedPriorities.append(selection);
availablePriorities.removeAll(selection);
availablePriorities.removeAll("Highest");
availablePriorities.removeAll("Lowest");
}
}
if (priorityValue == -1 && !selectedPriorities.isEmpty()) {
QString priorityString = selectedPriorities.join(", ");
priorityValue = priorities.indexOf(priorityString);
}
if (priorityValue != -1) {
slscPriorityButton->setValue(priorities[priorityValue]);
params.putInt("SLCPriority", priorityValue);
updateToggles();
}
});
slscPriorityButton->setValue(priorities[params.getInt("SLCPriority")]);
addItem(slscPriorityButton);
} else if (param == "VisionTurnControl") {
FrogPilotParamManageControl *visionTurnControlToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(visionTurnControlToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(visionTurnControlKeys.find(key.c_str()) != visionTurnControlKeys.end());
}
});
toggle = visionTurnControlToggle;
} else if (param == "CurveSensitivity" || param == "TurnAggressiveness") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 200, std::map<int, QString>(), this, false, "%");
} else {
toggle = new ParamControl(param, title, desc, icon, this);
}
addItem(toggle);
toggles[param.toStdString()] = toggle;
QObject::connect(toggle, &ToggleControl::toggleFlipped, [this]() {
updateToggles();
});
QObject::connect(static_cast<FrogPilotParamValueControl*>(toggle), &FrogPilotParamValueControl::buttonPressed, [this]() {
updateToggles();
});
QObject::connect(toggle, &AbstractControl::showDescriptionEvent, [this]() {
update();
});
QObject::connect(static_cast<FrogPilotParamManageControl*>(toggle), &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
update();
});
}
std::set<std::string> rebootKeys = {"AlwaysOnLateral", "FireTheBabysitter", "HigherBitrate", "MuteDM", "NNFF"};
for (const std::string &key : rebootKeys) {
QObject::connect(toggles[key], &ToggleControl::toggleFlipped, [this]() {
if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) {
Hardware::reboot();
}
});
}
conditionalExperimentalKeys = {"CECurves", "CECurvesLead", "CESlowerLead", "CENavigation", "CEStopLights", "CESignal"};
fireTheBabysitterKeys = {"NoLogging", "MuteDM", "MuteDoor", "MuteOverheated", "MuteSeatbelt", "OfflineMode"};
laneChangeKeys = {"LaneChangeTime", "LaneDetection", "OneLaneChange"};
lateralTuneKeys = {"AverageCurvature", "NNFF", "SteerRatio"};
longitudinalTuneKeys = {"AccelerationProfile", "AggressiveAcceleration", "SmoothBraking", "StoppingDistance"};
mtscKeys = {"MTSCAggressiveness"};
qolKeys = {"DisableOnroadUploads", "HigherBitrate", "PauseLateralOnSignal", "ReverseCruise", "SetSpeedOffset"};
speedLimitControllerKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "SLCFallback", "SLCOverride", "SLCPriority"};
visionTurnControlKeys = {"CurveSensitivity", "TurnAggressiveness"};
QObject::connect(uiState(), &UIState::offroadTransition, this, [this](bool offroad) {
if (!offroad) {
updateCarToggles();
}
});
QObject::connect(device(), &Device::interactiveTimeout, this, &FrogPilotControlsPanel::hideSubToggles);
QObject::connect(parent, &SettingsWindow::closeParentToggle, this, &FrogPilotControlsPanel::hideSubToggles);
QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotControlsPanel::updateMetric);
hideSubToggles();
updateMetric();
}
void FrogPilotControlsPanel::updateCarToggles() {
FrogPilotParamValueControlFloat *steerRatioToggle = static_cast<FrogPilotParamValueControlFloat*>(toggles["SteerRatio"]);
steerRatioStock = params.getFloat("SteerRatioStock");
steerRatioToggle->setTitle(steerRatioStock != 0 ? QString("Steer Ratio (Default: %1)").arg(steerRatioStock, 0, 'f', 2) : QString("Steer Ratio"));
steerRatioToggle->updateControl(steerRatioStock * 0.75, steerRatioStock * 1.25, "", 10.0);
steerRatioToggle->refresh();
}
void FrogPilotControlsPanel::updateToggles() {
std::thread([this]() {
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
}).detach();
}
void FrogPilotControlsPanel::updateMetric() {
bool previousIsMetric = isMetric;
isMetric = params.getBool("IsMetric");
if (isMetric != previousIsMetric) {
double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
double speedConversion = isMetric ? MILE_TO_KM : KM_TO_MILE;
params.putInt("CESpeed", std::nearbyint(params.getInt("CESpeed") * speedConversion));
params.putInt("CESpeedLead", std::nearbyint(params.getInt("CESpeedLead") * speedConversion));
params.putInt("Offset1", std::nearbyint(params.getInt("Offset1") * speedConversion));
params.putInt("Offset2", std::nearbyint(params.getInt("Offset2") * speedConversion));
params.putInt("Offset3", std::nearbyint(params.getInt("Offset3") * speedConversion));
params.putInt("Offset4", std::nearbyint(params.getInt("Offset4") * speedConversion));
params.putInt("PauseLateralOnSignal", std::nearbyint(params.getInt("PauseLateralOnSignal") * speedConversion));
params.putInt("SetSpeedOffset", std::nearbyint(params.getInt("SetSpeedOffset") * speedConversion));
params.putInt("StoppingDistance", std::nearbyint(params.getInt("StoppingDistance") * distanceConversion));
}
FrogPilotParamValueControl *offset1Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset1"]);
FrogPilotParamValueControl *offset2Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset2"]);
FrogPilotParamValueControl *offset3Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset3"]);
FrogPilotParamValueControl *offset4Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset4"]);
FrogPilotParamValueControl *pauseLateralToggle = static_cast<FrogPilotParamValueControl*>(toggles["PauseLateralOnSignal"]);
FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast<FrogPilotParamValueControl*>(toggles["SetSpeedOffset"]);
FrogPilotParamValueControl *stoppingDistanceToggle = static_cast<FrogPilotParamValueControl*>(toggles["StoppingDistance"]);
if (isMetric) {
offset1Toggle->setTitle("Speed Limit Offset (0-34 kph)");
offset2Toggle->setTitle("Speed Limit Offset (35-54 kph)");
offset3Toggle->setTitle("Speed Limit Offset (55-64 kph)");
offset4Toggle->setTitle("Speed Limit Offset (65-99 kph)");
offset1Toggle->setDescription("Set speed limit offset for limits between 0-34 kph.");
offset2Toggle->setDescription("Set speed limit offset for limits between 35-54 kph.");
offset3Toggle->setDescription("Set speed limit offset for limits between 55-64 kph.");
offset4Toggle->setDescription("Set speed limit offset for limits between 65-99 kph.");
offset1Toggle->updateControl(0, 150, " kph");
offset2Toggle->updateControl(0, 150, " kph");
offset3Toggle->updateControl(0, 150, " kph");
offset4Toggle->updateControl(0, 150, " kph");
pauseLateralToggle->updateControl(0, 150, " kph");
setSpeedOffsetToggle->updateControl(0, 150, " kph");
stoppingDistanceToggle->updateControl(0, 5, " meters");
} else {
offset1Toggle->setTitle("Speed Limit Offset (0-34 mph)");
offset2Toggle->setTitle("Speed Limit Offset (35-54 mph)");
offset3Toggle->setTitle("Speed Limit Offset (55-64 mph)");
offset4Toggle->setTitle("Speed Limit Offset (65-99 mph)");
offset1Toggle->setDescription("Set speed limit offset for limits between 0-34 mph.");
offset2Toggle->setDescription("Set speed limit offset for limits between 35-54 mph.");
offset3Toggle->setDescription("Set speed limit offset for limits between 55-64 mph.");
offset4Toggle->setDescription("Set speed limit offset for limits between 65-99 mph.");
offset1Toggle->updateControl(0, 99, " mph");
offset2Toggle->updateControl(0, 99, " mph");
offset3Toggle->updateControl(0, 99, " mph");
offset4Toggle->updateControl(0, 99, " mph");
pauseLateralToggle->updateControl(0, 99, " mph");
setSpeedOffsetToggle->updateControl(0, 99, " mph");
stoppingDistanceToggle->updateControl(0, 10, " feet");
}
offset1Toggle->refresh();
offset2Toggle->refresh();
offset3Toggle->refresh();
offset4Toggle->refresh();
pauseLateralToggle->refresh();
setSpeedOffsetToggle->refresh();
stoppingDistanceToggle->refresh();
previousIsMetric = isMetric;
}
void FrogPilotControlsPanel::parentToggleClicked() {
aggressiveProfile->setVisible(false);
conditionalSpeedsImperial->setVisible(false);
conditionalSpeedsMetric->setVisible(false);
modelSelectorButton->setVisible(false);
slscPriorityButton->setVisible(false);
standardProfile->setVisible(false);
relaxedProfile->setVisible(false);
this->openParentToggle();
}
void FrogPilotControlsPanel::hideSubToggles() {
aggressiveProfile->setVisible(false);
conditionalSpeedsImperial->setVisible(false);
conditionalSpeedsMetric->setVisible(false);
modelSelectorButton->setVisible(true);
slscPriorityButton->setVisible(false);
standardProfile->setVisible(false);
relaxedProfile->setVisible(false);
for (auto &[key, toggle] : toggles) {
bool subToggles = conditionalExperimentalKeys.find(key.c_str()) != conditionalExperimentalKeys.end() ||
fireTheBabysitterKeys.find(key.c_str()) != fireTheBabysitterKeys.end() ||
laneChangeKeys.find(key.c_str()) != laneChangeKeys.end() ||
lateralTuneKeys.find(key.c_str()) != lateralTuneKeys.end() ||
longitudinalTuneKeys.find(key.c_str()) != longitudinalTuneKeys.end() ||
mtscKeys.find(key.c_str()) != mtscKeys.end() ||
qolKeys.find(key.c_str()) != qolKeys.end() ||
speedLimitControllerKeys.find(key.c_str()) != speedLimitControllerKeys.end() ||
visionTurnControlKeys.find(key.c_str()) != visionTurnControlKeys.end();
toggle->setVisible(!subToggles);
}
this->closeParentToggle();
}
void FrogPilotControlsPanel::hideEvent(QHideEvent *event) {
hideSubToggles();
}

View File

@@ -2,6 +2,7 @@
#include <set> #include <set>
#include "selfdrive/oscarpilot/settings/settings.h"
#include "selfdrive/frogpilot/ui/frogpilot_functions.h" #include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/ui/qt/offroad/settings.h" #include "selfdrive/ui/qt/offroad/settings.h"
@@ -9,7 +10,7 @@ class FrogPilotControlsPanel : public FrogPilotListWidget {
Q_OBJECT Q_OBJECT
public: public:
explicit FrogPilotControlsPanel(SettingsWindow *parent); explicit FrogPilotControlsPanel(OscarSettingsWindow *parent);
signals: signals:
void closeParentToggle(); void closeParentToggle();

View File

@@ -0,0 +1,53 @@
#pragma once
#include <set>
#include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/ui/qt/offroad/settings.h"
class FrogPilotControlsPanel : public FrogPilotListWidget {
Q_OBJECT
public:
explicit FrogPilotControlsPanel(SettingsWindow *parent);
signals:
void closeParentToggle();
void openParentToggle();
private:
void hideEvent(QHideEvent *event);
void hideSubToggles();
void parentToggleClicked();
void updateCarToggles();
void updateMetric();
void updateToggles();
ButtonControl *slscPriorityButton;
FrogPilotButtonIconControl *modelSelectorButton;
FrogPilotDualParamControl *aggressiveProfile;
FrogPilotDualParamControl *conditionalSpeedsImperial;
FrogPilotDualParamControl *conditionalSpeedsMetric;
FrogPilotDualParamControl *standardProfile;
FrogPilotDualParamControl *relaxedProfile;
std::set<QString> conditionalExperimentalKeys;
std::set<QString> fireTheBabysitterKeys;
std::set<QString> laneChangeKeys;
std::set<QString> lateralTuneKeys;
std::set<QString> longitudinalTuneKeys;
std::set<QString> mtscKeys;
std::set<QString> qolKeys;
std::set<QString> speedLimitControllerKeys;
std::set<QString> visionTurnControlKeys;
std::map<std::string, ParamControl*> toggles;
Params params;
Params paramsMemory{"/dev/shm/params"};
bool isMetric = params.getBool("IsMetric");
float steerRatioStock = params.getFloat("SteerRatioStock");
};

View File

@@ -45,13 +45,13 @@ void setDefaultParams() {
Params params = Params(); Params params = Params();
bool FrogsGoMoo = params.get("DongleId").substr(0, 3) == "be6"; bool FrogsGoMoo = params.get("DongleId").substr(0, 3) == "be6";
bool brianbot = true; bool brianbot = params.get("DongleId").substr(0, 6) == "90bb71";
std::map<std::string, std::string> defaultValues { std::map<std::string, std::string> defaultValues {
{"AccelerationPath", brianbot ? "1" : "0"}, {"AccelerationPath", brianbot ? "1" : "0"},
{"AccelerationProfile", brianbot ? "2" : "2"}, {"AccelerationProfile", brianbot ? "2" : "2"},
{"AdjacentPath", FrogsGoMoo ? "1" : "0"}, {"AdjacentPath", FrogsGoMoo ? "1" : "0"},
{"AdjustablePersonalities", "3"}, {"AdjustablePersonalities", "0"},
{"AggressiveAcceleration", "1"}, {"AggressiveAcceleration", "1"},
{"AggressiveFollow", FrogsGoMoo ? "10" : "12"}, {"AggressiveFollow", FrogsGoMoo ? "10" : "12"},
{"AggressiveJerk", FrogsGoMoo ? "6" : "5"}, {"AggressiveJerk", FrogsGoMoo ? "6" : "5"},
@@ -67,8 +67,8 @@ void setDefaultParams() {
{"CESlowerLead", "0"}, {"CESlowerLead", "0"},
{"CESpeed", "0"}, {"CESpeed", "0"},
{"CESpeedLead", "0"}, {"CESpeedLead", "0"},
{"CEStopLights", "1"}, {"CEStopLights", "0"},
{"CEStopLightsLead", FrogsGoMoo ? "0" : "1"}, {"CEStopLightsLead", FrogsGoMoo ? "0" : "0"},
{"Compass", FrogsGoMoo ? "1" : "0"}, {"Compass", FrogsGoMoo ? "1" : "0"},
{"ConditionalExperimental", "1"}, {"ConditionalExperimental", "1"},
{"CurveSensitivity", FrogsGoMoo ? "125" : "100"}, {"CurveSensitivity", FrogsGoMoo ? "125" : "100"},
@@ -80,6 +80,7 @@ void setDefaultParams() {
{"CustomTheme", "1"}, {"CustomTheme", "1"},
{"CustomUI", "0"}, {"CustomUI", "0"},
{"DeviceShutdown", "1"}, {"DeviceShutdown", "1"},
{"DisableOnroadUploads", "1"},
{"DriverCamera", "0"}, {"DriverCamera", "0"},
{"DriveStats", "1"}, {"DriveStats", "1"},
{"EVTable", FrogsGoMoo ? "0" : "1"}, {"EVTable", FrogsGoMoo ? "0" : "1"},
@@ -114,14 +115,14 @@ void setDefaultParams() {
{"NoLogging", "1"}, {"NoLogging", "1"},
{"NudgelessLaneChange", "0"}, {"NudgelessLaneChange", "0"},
{"NumericalTemp", FrogsGoMoo ? "1" : "0"}, {"NumericalTemp", FrogsGoMoo ? "1" : "0"},
{"Offset1", "5"}, {"Offset1", "3"},
{"Offset2", FrogsGoMoo ? "7" : "5"}, {"Offset2", FrogsGoMoo ? "7" : "5"},
{"Offset3", "5"}, {"Offset3", "7"},
{"Offset4", FrogsGoMoo ? "20" : "10"}, {"Offset4", FrogsGoMoo ? "20" : "7"},
{"OneLaneChange", "1"}, {"OneLaneChange", "1"},
{"PathEdgeWidth", "20"}, {"PathEdgeWidth", "20"},
{"PathWidth", "61"}, {"PathWidth", "61"},
{"PauseLateralOnSignal", "0"}, {"PauseLateralOnSignal", "20"},
{"PreferredSchedule", "0"}, {"PreferredSchedule", "0"},
{"QOLControls", "1"}, {"QOLControls", "1"},
{"QOLVisuals", "1"}, {"QOLVisuals", "1"},
@@ -150,8 +151,8 @@ void setDefaultParams() {
{"StandardJerk", "10"}, {"StandardJerk", "10"},
{"StoppingDistance", FrogsGoMoo ? "6" : "0"}, {"StoppingDistance", FrogsGoMoo ? "6" : "0"},
{"TSS2Tune", "1"}, {"TSS2Tune", "1"},
{"TurnAggressiveness", FrogsGoMoo ? "150" : "100"}, {"TurnAggressiveness", FrogsGoMoo ? "150" : "100"}, // Test 90?
{"TurnDesires", "1"}, {"TurnDesires", "0"},
{"UnlimitedLength", "1"}, {"UnlimitedLength", "1"},
{"UseSI", FrogsGoMoo ? "1" : "0"}, {"UseSI", FrogsGoMoo ? "1" : "0"},
{"UseVienna", "0"}, {"UseVienna", "0"},

View File

@@ -0,0 +1,177 @@
#include <filesystem>
#include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/ui/ui.h"
bool FrogPilotConfirmationDialog::toggle(const QString &prompt_text, const QString &confirm_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, confirm_text, tr("Reboot Later"), false, parent);
return d.exec();
}
bool FrogPilotConfirmationDialog::toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, button_text, "", false, parent);
return d.exec();
}
bool FrogPilotConfirmationDialog::yesorno(const QString &prompt_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, tr("Yes"), tr("No"), false, parent);
return d.exec();
}
FrogPilotButtonIconControl::FrogPilotButtonIconControl(const QString &title, const QString &text, const QString &desc, const QString &icon, QWidget *parent) : AbstractControl(title, desc, icon, parent) {
btn.setText(text);
btn.setStyleSheet(R"(
QPushButton {
padding: 0;
border-radius: 50px;
font-size: 35px;
font-weight: 500;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)");
btn.setFixedSize(250, 100);
QObject::connect(&btn, &QPushButton::clicked, this, &FrogPilotButtonIconControl::clicked);
hlayout->addWidget(&btn);
}
void setDefaultParams() {
Params params = Params();
bool FrogsGoMoo = params.get("DongleId").substr(0, 3) == "be6";
bool brianbot = params.get("DongleId").substr(0, 6) == "90bb71";
std::map<std::string, std::string> defaultValues {
{"AccelerationPath", brianbot ? "1" : "0"},
{"AccelerationProfile", brianbot ? "2" : "2"},
{"AdjacentPath", FrogsGoMoo ? "1" : "0"},
{"AdjustablePersonalities", "0"},
{"AggressiveAcceleration", "1"},
{"AggressiveFollow", FrogsGoMoo ? "10" : "12"},
{"AggressiveJerk", FrogsGoMoo ? "6" : "5"},
{"AlwaysOnLateral", "1"},
{"AlwaysOnLateralMain", brianbot ? "1" : "0"},
{"AverageCurvature", brianbot ? "1" : "0"},
{"BlindSpotPath", "1"},
{"CameraView", FrogsGoMoo ? "1" : "0"},
{"CECurves", "1"},
{"CECurvesLead", "0"},
{"CENavigation", "1"},
{"CESignal", "1"},
{"CESlowerLead", "0"},
{"CESpeed", "0"},
{"CESpeedLead", "0"},
{"CEStopLights", "0"},
{"CEStopLightsLead", FrogsGoMoo ? "0" : "0"},
{"Compass", FrogsGoMoo ? "1" : "0"},
{"ConditionalExperimental", "1"},
{"CurveSensitivity", FrogsGoMoo ? "125" : "100"},
{"CustomColors", "1"},
{"CustomIcons", "1"},
{"CustomPersonalities", "0"},
{"CustomSignals", "0"},
{"CustomSounds", "1"},
{"CustomTheme", "1"},
{"CustomUI", "0"},
{"DeviceShutdown", "1"},
{"DisableOnroadUploads", "1"},
{"DriverCamera", "0"},
{"DriveStats", "1"},
{"EVTable", FrogsGoMoo ? "0" : "1"},
{"ExperimentalModeActivation", "1"},
{"ExperimentalModeViaLKAS", "0"},
{"ExperimentalModeViaScreen", FrogsGoMoo ? "0" : "1"},
{"Fahrenheit", "0"},
{"FireTheBabysitter", FrogsGoMoo ? "1" : "0"},
{"FullMap", "0"},
{"GasRegenCmd", "0"},
{"GoatScream", "0"},
{"GreenLightAlert", "0"},
{"HideSpeed", "0"},
{"HigherBitrate", "0"},
{"LaneChangeTime", "0"},
{"LaneDetection", "1"},
{"LaneLinesWidth", "4"},
{"LateralTune", "1"},
{"LeadInfo", FrogsGoMoo ? "1" : "0"},
{"LockDoors", "0"},
{"LongitudinalTune", "1"},
{"LongPitch", FrogsGoMoo ? "0" : "1"},
{"LowerVolt", FrogsGoMoo ? "0" : "1"},
{"Model", "3"},
{"ModelUI", "1"},
{"MTSCEnabled", "1"},
{"MuteDM", FrogsGoMoo ? "1" : "1"},
{"MuteDoor", FrogsGoMoo ? "1" : "1"},
{"MuteOverheated", FrogsGoMoo ? "1" : "0"},
{"MuteSeatbelt", FrogsGoMoo ? "1" : "0"},
{"NNFF", FrogsGoMoo ? "1" : "1"},
{"NoLogging", "1"},
{"NudgelessLaneChange", "0"},
{"NumericalTemp", FrogsGoMoo ? "1" : "0"},
{"Offset1", "3"},
{"Offset2", FrogsGoMoo ? "7" : "5"},
{"Offset3", "7"},
{"Offset4", FrogsGoMoo ? "20" : "7"},
{"OneLaneChange", "1"},
{"PathEdgeWidth", "20"},
{"PathWidth", "61"},
{"PauseLateralOnSignal", "20"},
{"PreferredSchedule", "0"},
{"QOLControls", "1"},
{"QOLVisuals", "1"},
{"RandomEvents", FrogsGoMoo ? "1" : "0"},
{"RelaxedFollow", "30"},
{"RelaxedJerk", "50"},
{"ReverseCruise", "0"},
{"RoadEdgesWidth", "2"},
{"RoadNameUI", "1"},
{"RotatingWheel", "1"},
{"ScreenBrightness", "101"},
{"SearchInput", "0"},
{"ShowCPU", FrogsGoMoo ? "1" : "0"},
{"ShowFPS", FrogsGoMoo ? "1" : "0"},
{"ShowGPU", "0"},
{"ShowMemoryUsage", FrogsGoMoo ? "1" : "0"},
{"Sidebar", FrogsGoMoo ? "1" : "0"},
{"SilentMode", "0"},
{"SLCFallback", "2"},
{"SLCOverride", FrogsGoMoo ? "2" : "1"},
{"SLCPriority", "1"},
{"SmoothBraking", "1"},
{"SNGHack", FrogsGoMoo ? "0" : "1"},
{"SpeedLimitController", "1"},
{"StandardFollow", "15"},
{"StandardJerk", "10"},
{"StoppingDistance", FrogsGoMoo ? "6" : "0"},
{"TSS2Tune", "1"},
{"TurnAggressiveness", FrogsGoMoo ? "150" : "100"}, // Test 90?
{"TurnDesires", "0"},
{"UnlimitedLength", "1"},
{"UseSI", FrogsGoMoo ? "1" : "0"},
{"UseVienna", "0"},
{"VisionTurnControl", "1"},
{"WheelIcon", FrogsGoMoo ? "1" : "0"}
};
bool rebootRequired = false;
for (const auto &[key, value] : defaultValues) {
if (params.get(key).empty()) {
params.put(key, value);
rebootRequired = true;
}
}
if (rebootRequired) {
while (!std::filesystem::exists("/data/openpilot/prebuilt")) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
Hardware::reboot();
}
}

View File

@@ -91,7 +91,7 @@ public:
background-color: #4a4a4a; background-color: #4a4a4a;
} }
QPushButton:checked:enabled { QPushButton:checked:enabled {
background-color: #33Ab4C; background-color: #0048FF;
} }
QPushButton:disabled { QPushButton:disabled {
color: #33E4E4E4; color: #33E4E4E4;
@@ -202,7 +202,7 @@ public:
background-color: #4a4a4a; background-color: #4a4a4a;
} }
QPushButton:checked:enabled { QPushButton:checked:enabled {
background-color: #33Ab4C; background-color: #0048FF;
} }
QPushButton:disabled { QPushButton:disabled {
color: #33E4E4E4; color: #33E4E4E4;
@@ -523,7 +523,7 @@ public:
background-color: #4a4a4a; background-color: #4a4a4a;
} }
QPushButton:checked:enabled { QPushButton:checked:enabled {
background-color: #33Ab4C; background-color: #0048FF;
} }
QPushButton:disabled { QPushButton:disabled {
color: #33E4E4E4; color: #33E4E4E4;

View File

@@ -0,0 +1,661 @@
#pragma once
#include "selfdrive/ui/qt/widgets/controls.h"
class FrogPilotConfirmationDialog : public ConfirmationDialog {
Q_OBJECT
public:
explicit FrogPilotConfirmationDialog(const QString &prompt_text, const QString &confirm_text,
const QString &cancel_text, const bool rich, QWidget* parent);
static bool toggle(const QString &prompt_text, const QString &confirm_text, QWidget *parent);
static bool toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent);
static bool yesorno(const QString &prompt_text, QWidget *parent);
};
class FrogPilotListWidget : public QWidget {
Q_OBJECT
public:
explicit FrogPilotListWidget(QWidget *parent = 0) : QWidget(parent), outer_layout(this) {
outer_layout.setMargin(0);
outer_layout.setSpacing(0);
outer_layout.addLayout(&inner_layout);
inner_layout.setMargin(0);
inner_layout.setSpacing(25); // default spacing is 25
outer_layout.addStretch();
}
inline void addItem(QWidget *w) { inner_layout.addWidget(w); }
inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); }
inline void setSpacing(int spacing) { inner_layout.setSpacing(spacing); }
private:
void paintEvent(QPaintEvent *) override {
QPainter p(this);
p.setPen(Qt::gray);
int visibleWidgetCount = 0;
std::vector<QRect> visibleRects;
for (int i = 0; i < inner_layout.count(); ++i) {
QWidget *widget = inner_layout.itemAt(i)->widget();
if (widget && widget->isVisible()) {
visibleWidgetCount++;
visibleRects.push_back(inner_layout.itemAt(i)->geometry());
}
}
for (int i = 0; i < visibleWidgetCount - 1; ++i) {
int bottom = visibleRects[i].bottom() + inner_layout.spacing() / 2;
p.drawLine(visibleRects[i].left() + 40, bottom, visibleRects[i].right() - 40, bottom);
}
}
QVBoxLayout outer_layout;
QVBoxLayout inner_layout;
};
class FrogPilotButtonIconControl : public AbstractControl {
Q_OBJECT
public:
FrogPilotButtonIconControl(const QString &title, const QString &text, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
inline void setText(const QString &text) { btn.setText(text); }
inline QString text() const { return btn.text(); }
signals:
void clicked();
public slots:
void setEnabled(bool enabled) { btn.setEnabled(enabled); }
private:
QPushButton btn;
};
class FrogPilotButtonParamControl : public ParamControl {
Q_OBJECT
public:
FrogPilotButtonParamControl(const QString &param, const QString &title, const QString &desc, const QString &icon,
const std::vector<QString> &button_texts, const int minimum_button_width = 225)
: ParamControl(param, title, desc, icon) {
const QString style = R"(
QPushButton {
border-radius: 50px;
font-size: 40px;
font-weight: 500;
height:100px;
padding: 0 25 0 25;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
QPushButton:checked:enabled {
background-color: #0048FF;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)";
key = param.toStdString();
int value = atoi(params.get(key).c_str());
button_group = new QButtonGroup(this);
button_group->setExclusive(true);
for (size_t i = 0; i < button_texts.size(); i++) {
QPushButton *button = new QPushButton(button_texts[i], this);
button->setCheckable(true);
button->setChecked(i == value);
button->setStyleSheet(style);
button->setMinimumWidth(minimum_button_width);
hlayout->addWidget(button);
button_group->addButton(button, i);
}
QObject::connect(button_group, QOverload<int, bool>::of(&QButtonGroup::buttonToggled), [=](int id, bool checked) {
if (checked) {
params.put(key, std::to_string(id));
refresh();
emit buttonClicked(id);
}
});
toggle.hide();
}
void setEnabled(bool enable) {
for (auto btn : button_group->buttons()) {
btn->setEnabled(enable);
}
}
signals:
void buttonClicked(int id);
private:
std::string key;
Params params;
QButtonGroup *button_group;
};
class FrogPilotParamManageControl : public ParamControl {
Q_OBJECT
public:
FrogPilotParamManageControl(const QString &param, const QString &title, const QString &desc, const QString &icon, QWidget *parent = nullptr)
: ParamControl(param, title, desc, icon, parent),
key(param.toStdString()),
manageButton(new ButtonControl(tr(""), tr("MANAGE"), tr(""))) {
hlayout->insertWidget(hlayout->indexOf(&toggle) - 1, manageButton);
connect(this, &ToggleControl::toggleFlipped, this, [this](bool state) {
refresh();
});
connect(manageButton, &ButtonControl::clicked, this, &FrogPilotParamManageControl::manageButtonClicked);
}
void refresh() {
ParamControl::refresh();
manageButton->setVisible(params.getBool(key));
}
void showEvent(QShowEvent *event) override {
ParamControl::showEvent(event);
refresh();
}
signals:
void manageButtonClicked();
private:
std::string key;
Params params;
ButtonControl *manageButton;
};
class FrogPilotParamToggleControl : public ParamControl {
Q_OBJECT
public:
FrogPilotParamToggleControl(const QString &param, const QString &title, const QString &desc,
const QString &icon, const std::vector<QString> &button_params,
const std::vector<QString> &button_texts, QWidget *parent = nullptr,
const int minimum_button_width = 225)
: ParamControl(param, title, desc, icon, parent) {
connect(this, &ToggleControl::toggleFlipped, this, [this](bool state) {
refreshButtons(state);
});
const QString style = R"(
QPushButton {
border-radius: 50px;
font-size: 40px;
font-weight: 500;
height:100px;
padding: 0 25 0 25;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
QPushButton:checked:enabled {
background-color: #0048FF;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)";
button_group = new QButtonGroup(this);
button_group->setExclusive(false);
std::map<QString, bool> paramState;
for (const QString &button_param : button_params) {
paramState[button_param] = params.getBool(button_param.toStdString());
}
for (int i = 0; i < button_texts.size(); ++i) {
QPushButton *button = new QPushButton(button_texts[i], this);
button->setCheckable(true);
button->setChecked(paramState[button_params[i]]);
button->setStyleSheet(style);
button->setMinimumWidth(minimum_button_width);
button_group->addButton(button, i);
connect(button, &QPushButton::clicked, [this, button_params, i](bool checked) {
params.putBool(button_params[i].toStdString(), checked);
button_group->button(i)->setChecked(checked);
emit buttonClicked(checked);
});
hlayout->insertWidget(hlayout->indexOf(&toggle), button);
}
}
void refreshButtons(bool state) {
for (QAbstractButton *button : button_group->buttons()) {
button->setVisible(state);
}
}
signals:
void buttonClicked(const bool checked);
private:
Params params;
QButtonGroup *button_group;
};
class FrogPilotParamValueControl : public ParamControl {
Q_OBJECT
public:
FrogPilotParamValueControl(const QString &param, const QString &title, const QString &desc, const QString &icon,
const int &minValue, const int &maxValue, const std::map<int, QString> &valueLabels,
QWidget *parent = nullptr, const bool &loop = true, const QString &label = "", const int &division = 1)
: ParamControl(param, title, desc, icon, parent),
minValue(minValue), maxValue(maxValue), valueLabelMappings(valueLabels), loop(loop), labelText(label), division(division) {
key = param.toStdString();
valueLabel = new QLabel(this);
hlayout->addWidget(valueLabel);
QPushButton *decrementButton = createButton("-", this);
QPushButton *incrementButton = createButton("+", this);
hlayout->addWidget(decrementButton);
hlayout->addWidget(incrementButton);
connect(decrementButton, &QPushButton::clicked, this, [=]() {
updateValue(-1);
});
connect(incrementButton, &QPushButton::clicked, this, [=]() {
updateValue(1);
});
toggle.hide();
}
void updateValue(int increment) {
value = value + increment;
if (loop) {
if (value < minValue) value = maxValue;
else if (value > maxValue) value = minValue;
} else {
value = std::max(minValue, std::min(maxValue, value));
}
params.putInt(key, value);
refresh();
emit buttonPressed();
emit valueChanged(value);
}
void refresh() {
value = params.getInt(key);
QString text;
auto it = valueLabelMappings.find(value);
if (division > 1) {
text = QString::number(value / (division * 1.0), 'g');
} else {
text = it != valueLabelMappings.end() ? it->second : QString::number(value);
}
if (!labelText.isEmpty()) {
text += labelText;
}
valueLabel->setText(text);
valueLabel->setStyleSheet("QLabel { color: #E0E879; }");
}
void updateControl(int newMinValue, int newMaxValue, const QString &newLabel, int newDivision = 1) {
minValue = newMinValue;
maxValue = newMaxValue;
labelText = newLabel;
division = newDivision;
}
void showEvent(QShowEvent *event) override {
refresh();
}
signals:
void buttonPressed();
void valueChanged(int value);
private:
bool loop;
int division;
int maxValue;
int minValue;
int value;
QLabel *valueLabel;
QString labelText;
std::map<int, QString> valueLabelMappings;
std::string key;
Params params;
QPushButton *createButton(const QString &text, QWidget *parent) {
QPushButton *button = new QPushButton(text, parent);
button->setFixedSize(150, 100);
button->setAutoRepeat(true);
button->setAutoRepeatInterval(150);
button->setStyleSheet(R"(
QPushButton {
border-radius: 50px;
font-size: 50px;
font-weight: 500;
height: 100px;
padding: 0 25 0 25;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
)");
return button;
}
};
class FrogPilotParamValueControlFloat : public ParamControl {
Q_OBJECT
public:
FrogPilotParamValueControlFloat(const QString &param, const QString &title, const QString &desc, const QString &icon,
const float &minValue, const float &maxValue, const std::map<int, QString> &valueLabels,
QWidget *parent = nullptr, const bool &loop = true, const QString &label = "", const float &division = 1.0f)
: ParamControl(param, title, desc, icon, parent),
minValue(minValue), maxValue(maxValue), valueLabelMappings(valueLabels), loop(loop), labelText(label), division(division) {
key = param.toStdString();
valueLabel = new QLabel(this);
hlayout->addWidget(valueLabel);
QPushButton *decrementButton = createButton("-", this);
QPushButton *incrementButton = createButton("+", this);
hlayout->addWidget(decrementButton);
hlayout->addWidget(incrementButton);
connect(decrementButton, &QPushButton::clicked, this, [=]() {
updateValue(-1.0f);
});
connect(incrementButton, &QPushButton::clicked, this, [=]() {
updateValue(1.0f);
});
toggle.hide();
}
void updateValue(float increment) {
value += increment * 0.1f;
if (loop) {
if (value < minValue) value = maxValue;
else if (value > maxValue) value = minValue;
} else {
value = std::max(minValue, std::min(maxValue, value));
}
params.putFloat(key, value);
refresh();
emit buttonPressed();
emit valueChanged(value);
}
void refresh() {
value = params.getFloat(key);
QString text;
auto it = valueLabelMappings.find(value);
if (division > 0.1f) {
text = QString::number(value, 'f', 1);
} else {
text = it != valueLabelMappings.end() ? it->second : QString::number(value, 'f', 1);
}
if (!labelText.isEmpty()) {
text += labelText;
}
valueLabel->setText(text);
valueLabel->setStyleSheet("QLabel { color: #E0E879; }");
}
void updateControl(float newMinValue, float newMaxValue, const QString &newLabel, float newDivision = 1.0f) {
minValue = newMinValue;
maxValue = newMaxValue;
labelText = newLabel;
division = newDivision;
}
void showEvent(QShowEvent *event) override {
refresh();
}
signals:
void buttonPressed();
void valueChanged(float value);
private:
bool loop;
float division;
float maxValue;
float minValue;
float value;
QLabel *valueLabel;
QString labelText;
std::map<int, QString> valueLabelMappings;
std::string key;
Params params;
QPushButton *createButton(const QString &text, QWidget *parent) {
QPushButton *button = new QPushButton(text, parent);
button->setFixedSize(150, 100);
button->setAutoRepeat(true);
button->setAutoRepeatInterval(150);
button->setStyleSheet(R"(
QPushButton {
border-radius: 50px;
font-size: 50px;
font-weight: 500;
height: 100px;
padding: 0 25 0 25;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
)");
return button;
}
};
class FrogPilotDualParamControl : public QFrame {
Q_OBJECT
public:
FrogPilotDualParamControl(ParamControl *control1, ParamControl *control2, QWidget *parent = nullptr, bool split=false)
: QFrame(parent) {
QHBoxLayout *hlayout = new QHBoxLayout(this);
control1->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
control1->setMaximumWidth(split ? 800 : 700);
control2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
control2->setMaximumWidth(800);
hlayout->addWidget(control1);
hlayout->addWidget(control2);
}
};
class FrogPilotParamValueToggleControl : public ParamControl {
Q_OBJECT
public:
FrogPilotParamValueToggleControl(const QString &param, const QString &title, const QString &desc, const QString &icon,
const int &minValue, const int &maxValue, const std::map<int, QString> &valueLabels,
QWidget *parent = nullptr, const bool &loop = true, const QString &label = "", const int &division = 1,
const std::vector<QString> &button_params = std::vector<QString>(), const std::vector<QString> &button_texts = std::vector<QString>(),
const int minimum_button_width = 225)
: ParamControl(param, title, desc, icon, parent),
minValue(minValue), maxValue(maxValue), valueLabelMappings(valueLabels), loop(loop), labelText(label), division(division) {
key = param.toStdString();
const QString style = R"(
QPushButton {
border-radius: 50px;
font-size: 40px;
font-weight: 500;
height:100px;
padding: 0 25 0 25;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
QPushButton:checked:enabled {
background-color: #0048FF;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)";
button_group = new QButtonGroup(this);
button_group->setExclusive(false);
std::map<QString, bool> paramState;
for (const QString &button_param : button_params) {
paramState[button_param] = params.getBool(button_param.toStdString());
}
for (int i = 0; i < button_texts.size(); ++i) {
QPushButton *button = new QPushButton(button_texts[i], this);
button->setCheckable(true);
button->setChecked(paramState[button_params[i]]);
button->setStyleSheet(style);
button->setMinimumWidth(minimum_button_width);
button_group->addButton(button, i);
connect(button, &QPushButton::clicked, [this, button_params, i](bool checked) {
params.putBool(button_params[i].toStdString(), checked);
button_group->button(i)->setChecked(checked);
});
hlayout->addWidget(button);
}
valueLabel = new QLabel(this);
hlayout->addWidget(valueLabel);
QPushButton *decrementButton = createButton("-", this);
QPushButton *incrementButton = createButton("+", this);
hlayout->addWidget(decrementButton);
hlayout->addWidget(incrementButton);
connect(decrementButton, &QPushButton::clicked, this, [=]() {
updateValue(-1);
});
connect(incrementButton, &QPushButton::clicked, this, [=]() {
updateValue(1);
});
toggle.hide();
}
void updateValue(int increment) {
value = value + increment;
if (loop) {
if (value < minValue) value = maxValue;
else if (value > maxValue) value = minValue;
} else {
value = std::max(minValue, std::min(maxValue, value));
}
params.putInt(key, value);
refresh();
emit buttonPressed();
emit valueChanged(value);
}
void refresh() {
value = params.getInt(key);
QString text;
auto it = valueLabelMappings.find(value);
if (division > 1) {
text = QString::number(value / (division * 1.0), 'g');
} else {
text = it != valueLabelMappings.end() ? it->second : QString::number(value);
}
if (!labelText.isEmpty()) {
text += labelText;
}
valueLabel->setText(text);
valueLabel->setStyleSheet("QLabel { color: #E0E879; }");
}
void updateControl(int newMinValue, int newMaxValue, const QString &newLabel, int newDivision) {
minValue = newMinValue;
maxValue = newMaxValue;
labelText = newLabel;
division = newDivision;
}
void showEvent(QShowEvent *event) override {
refresh();
}
signals:
void buttonPressed();
void valueChanged(int value);
private:
bool loop;
int division;
int maxValue;
int minValue;
int value;
QButtonGroup *button_group;
QLabel *valueLabel;
QString labelText;
std::map<int, QString> valueLabelMappings;
std::string key;
Params params;
QPushButton *createButton(const QString &text, QWidget *parent) {
QPushButton *button = new QPushButton(text, parent);
button->setFixedSize(150, 100);
button->setAutoRepeat(true);
button->setAutoRepeatInterval(150);
button->setStyleSheet(R"(
QPushButton {
border-radius: 50px;
font-size: 50px;
font-weight: 500;
height: 100px;
padding: 0 25 0 25;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
)");
return button;
}
};
void setDefaultParams();

View File

@@ -0,0 +1,185 @@
#include <QDir>
#include <QRegularExpression>
#include <QTextStream>
#include "selfdrive/frogpilot/ui/vehicle_settings.h"
#include "selfdrive/ui/ui.h"
QStringList getCarNames(const QString &carMake) {
QMap<QString, QString> makeMap;
makeMap["acura"] = "honda";
makeMap["audi"] = "volkswagen";
makeMap["buick"] = "gm";
makeMap["cadillac"] = "gm";
makeMap["chevrolet"] = "gm";
makeMap["chrysler"] = "chrysler";
makeMap["dodge"] = "chrysler";
makeMap["ford"] = "ford";
makeMap["gm"] = "gm";
makeMap["gmc"] = "gm";
makeMap["genesis"] = "hyundai";
makeMap["honda"] = "honda";
makeMap["hyundai"] = "hyundai";
makeMap["infiniti"] = "nissan";
makeMap["jeep"] = "chrysler";
makeMap["kia"] = "hyundai";
makeMap["lexus"] = "toyota";
makeMap["lincoln"] = "ford";
makeMap["man"] = "volkswagen";
makeMap["mazda"] = "mazda";
makeMap["nissan"] = "nissan";
makeMap["ram"] = "chrysler";
makeMap["seat"] = "volkswagen";
makeMap["subaru"] = "subaru";
makeMap["tesla"] = "tesla";
makeMap["toyota"] = "toyota";
makeMap["volkswagen"] = "volkswagen";
makeMap["skoda"] = "volkswagen";
QString dirPath = "../../selfdrive/car";
QDir dir(dirPath);
QString targetFolder = makeMap.value(carMake, carMake);
QStringList names;
foreach (const QString &folder, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
if (folder == targetFolder) {
QFile file(dirPath + "/" + folder + "/values.py");
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QRegularExpression regex("class CAR\\(StrEnum\\):([\\s\\S]*?)(?=^\\w)", QRegularExpression::MultilineOption);
QRegularExpressionMatch match = regex.match(QTextStream(&file).readAll());
file.close();
if (match.hasMatch()) {
QRegularExpression nameRegex("=\\s*\"([^\"]+)\"");
QRegularExpressionMatchIterator it = nameRegex.globalMatch(match.captured(1));
while (it.hasNext()) {
names << it.next().captured(1);
}
}
}
}
}
std::sort(names.begin(), names.end());
return names;
}
FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(SettingsWindow *parent) : FrogPilotListWidget(parent) {
selectMakeButton = new ButtonControl(tr("Select Make"), tr("SELECT"));
QObject::connect(selectMakeButton, &ButtonControl::clicked, [this]() {
std::string currentMake = params.get("CarMake");
QStringList makes = {
"Acura", "Audi", "BMW", "Buick", "Cadillac", "Chevrolet", "Chrysler", "Dodge", "Ford", "GM", "GMC",
"Genesis", "Honda", "Hyundai", "Infiniti", "Jeep", "Kia", "Lexus", "Lincoln", "MAN", "Mazda",
"Mercedes", "Nissan", "Ram", "SEAT", "Subaru", "Tesla", "Toyota", "Volkswagen", "Volvo", "Škoda",
};
QString newMakeSelection = MultiOptionDialog::getSelection(tr("Select a Make"), makes, QString::fromStdString(currentMake), this);
if (!newMakeSelection.isEmpty()) {
carMake = newMakeSelection;
params.put("CarMake", carMake.toStdString());
selectMakeButton->setValue(newMakeSelection);
setModels();
}
});
addItem(selectMakeButton);
selectModelButton = new ButtonControl(tr("Select Model"), tr("SELECT"));
QString modelSelection = QString::fromStdString(params.get("CarModel"));
QObject::connect(selectModelButton, &ButtonControl::clicked, [this]() {
std::string currentModel = params.get("CarModel");
QString newModelSelection = MultiOptionDialog::getSelection(tr("Select a Model"), models, QString::fromStdString(currentModel), this);
if (!newModelSelection.isEmpty()) {
params.put("CarModel", newModelSelection.toStdString());
selectModelButton->setValue(newModelSelection);
}
});
selectModelButton->setValue(modelSelection);
addItem(selectModelButton);
selectModelButton->setVisible(false);
std::vector<std::tuple<QString, QString, QString, QString>> vehicleToggles {
{"EVTable", "EV Lookup Tables", "Smoothen out the gas and brake controls for EV vehicles.", ""},
{"GasRegenCmd", "Gas Regen Cmd", "", ""},
{"LongPitch", "Long Pitch Compensation", "Reduce speed and acceleration error for greater passenger comfort and improved vehicle efficiency.", ""},
{"LowerVolt", "Lower Volt Enable Speed", "Lower the Volt's minimum enable speed to enable openpilot at any speed.", ""},
{"LockDoors", "Lock Doors In Drive", "Automatically lock the doors when in drive and unlock when in park.", ""},
{"SNGHack", "Stop and Go Hack", "Enable the 'Stop and Go' hack for vehicles without stock stop and go functionality.", ""},
{"TSS2Tune", "TSS2 Tune", "Tuning profile based on the tuning profile from DragonPilot for TSS2 vehicles.", ""}
};
for (auto &[param, title, desc, icon] : vehicleToggles) {
ParamControl *toggle = new ParamControl(param, title, desc, icon, this);
addItem(toggle);
toggle->setVisible(false);
toggles[param.toStdString()] = toggle;
QObject::connect(toggle, &ToggleControl::toggleFlipped, [this]() {
updateToggles();
});
}
gmKeys = {"EVTable", "GasRegenCmd", "LongPitch", "LowerVolt"};
toyotaKeys = {"LockDoors", "SNGHack", "TSS2Tune"};
std::set<std::string> rebootKeys = {"EVTable", "GasRegenCmd", "LongPitch", "LowerVolt", "TSS2Tune"};
for (const std::string &key : rebootKeys) {
QObject::connect(toggles[key], &ToggleControl::toggleFlipped, [this]() {
if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) {
Hardware::reboot();
}
});
}
QObject::connect(uiState(), &UIState::offroadTransition, this, [this](bool offroad) {
if (!offroad) {
std::thread([this]() {
while (carMake.isEmpty()) {
std::this_thread::sleep_for(std::chrono::seconds(1));
carMake = QString::fromStdString(params.get("CarMake"));
}
setModels();
}).detach();
}
});
carMake = QString::fromStdString(params.get("CarMake"));
if (!carMake.isEmpty()) {
setModels();
}
}
void FrogPilotVehiclesPanel::updateToggles() {
std::thread([this]() {
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
}).detach();
}
void FrogPilotVehiclesPanel::setModels() {
models = getCarNames(carMake.toLower());
setToggles();
}
void FrogPilotVehiclesPanel::setToggles() {
selectMakeButton->setValue(carMake);
selectModelButton->setVisible(!carMake.isEmpty());
bool gm = carMake == "Buick" || carMake == "Cadillac" || carMake == "Chevrolet" || carMake == "GM" || carMake == "GMC";
bool toyota = carMake == "Lexus" || carMake == "Toyota";
for (auto &[key, toggle] : toggles) {
toggle->setVisible(false);
if (gm) {
toggle->setVisible(gmKeys.find(key.c_str()) != gmKeys.end());
} else if (toyota) {
toggle->setVisible(toyotaKeys.find(key.c_str()) != toyotaKeys.end());
}
}
update();
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <set>
#include <QStringList>
#include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/ui/qt/offroad/settings.h"
class FrogPilotVehiclesPanel : public FrogPilotListWidget {
Q_OBJECT
public:
explicit FrogPilotVehiclesPanel(SettingsWindow *parent);
private:
void setModels();
void setToggles();
void updateToggles();
ButtonControl *selectMakeButton;
ButtonControl *selectModelButton;
QString carMake;
QStringList models;
std::map<std::string, ParamControl*> toggles;
std::set<QString> gmKeys;
std::set<QString> toyotaKeys;
Params params;
Params paramsMemory{"/dev/shm/params"};
};

View File

@@ -0,0 +1,242 @@
#include "selfdrive/frogpilot/ui/visual_settings.h"
#include "selfdrive/ui/ui.h"
FrogPilotVisualsPanel::FrogPilotVisualsPanel(SettingsWindow *parent) : FrogPilotListWidget(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> visualToggles {
{"CustomTheme", "Custom Themes", "Enable the ability to use custom themes.", "../frogpilot/assets/wheel_images/frog.png"},
{"CustomColors", "Color Theme", "Switch out the standard openpilot color scheme with a custom color scheme.\n\nWant to submit your own color scheme? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomIcons", "Icon Pack", "Switch out the standard openpilot icons with a set of custom icons.\n\nWant to submit your own icon pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomSignals", "Turn Signals", "Add custom animations for your turn signals for a personal touch!\n\nWant to submit your own turn signal animation? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomSounds", "Sound Pack", "Switch out the standard openpilot sounds with a set of custom sounds.\n\nWant to submit your own sound pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CameraView", "Camera View", "Choose your preferred camera view for the onroad UI. This is a visual change only and doesn't impact openpilot.", "../frogpilot/assets/toggle_icons/icon_camera.png"},
{"Compass", "Compass", "Add a compass to your onroad UI.", "../frogpilot/assets/toggle_icons/icon_compass.png"},
{"CustomUI", "Custom Onroad UI", "Customize the Onroad UI with some additional visual functions.", "../assets/offroad/icon_road.png"},
{"AdjacentPath", "Adjacent Paths", "Display paths to the left and right of your car, visualizing where the model detects lanes.", ""},
{"BlindSpotPath", "Blind Spot Path", "Visualize your blind spots with a red path when another vehicle is detected nearby.", ""},
{"ShowFPS", "FPS Counter", "Display the Frames Per Second (FPS) of your onroad UI for monitoring system performance.", ""},
{"LeadInfo", "Lead Info and Logics", "Get detailed information about the vehicle ahead, including speed and distance, and the logic behind your following distance.", ""},
{"RoadNameUI", "Road Name", "See the name of the road you're on at the bottom of your screen. Sourced from OpenStreetMap.", ""},
{"UseVienna", "Use Vienna Speed Limit Signs", "Use the Vienna (EU) speed limit style signs as opposed to MUTCD (US).", ""},
{"DriverCamera", "Driver Camera On Reverse", "Show the driver's camera feed when you shift to reverse.", "../assets/img_driver_face_static.png"},
{"GreenLightAlert", "Green Light Alert", "Get an alert when a traffic light changes from red to green.", "../frogpilot/assets/toggle_icons/icon_green_light.png"},
{"ModelUI", "Model UI", "Personalize how the model's visualizations appear on your screen.", "../assets/offroad/icon_calibration.png"},
{"AccelerationPath", "Acceleration Path", "Visualize the car's intended acceleration or deceleration with a color-coded path.", ""},
{"LaneLinesWidth", "Lane Lines", "Adjust the visual thickness of lane lines on your display.\n\nDefault matches the MUTCD average of 4 inches.", ""},
{"PathEdgeWidth", "Path Edges", "Adjust the width of the path edges shown on your UI to represent different driving modes and statuses.\n\nDefault is 20% of the total path.\n\nBlue = Navigation\nLight Blue = Always On Lateral\nGreen = Default with 'FrogPilot Colors'\nLight Green = Default with stock colors\nOrange = Experimental Mode Active\nYellow = Conditional Overriden", ""},
{"PathWidth", "Path Width", "Customize the width of the driving path shown on your UI.\n\nDefault matches the width of a 2019 Lexus ES 350.", ""},
{"RoadEdgesWidth", "Road Edges", "Adjust the visual thickness of road edges on your display.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches.", ""},
{"UnlimitedLength", "'Unlimited' Road UI Length", "Extend the display of the path, lane lines, and road edges as far as the system can detect, providing a more expansive view of the road ahead.", ""},
{"QOLVisuals", "Quality of Life", "Miscellaneous quality of life changes to improve your overall openpilot experience.", "../frogpilot/assets/toggle_icons/quality_of_life.png"},
{"DriveStats", "Drive Stats In Home Screen", "Display your device's drive stats in the home screen.", ""},
{"HideSpeed", "Hide Speed", "Hide the speed indicator in the onroad UI.", ""},
{"ShowSLCOffset", "Show Speed Limit Offset", "Show the speed limit offset seperated from the speed limit in the onroad UI when using 'Speed Limit Controller'.", ""},
{"RandomEvents", "Random Events", "Enjoy a bit of unpredictability with random events that can occur during certain driving conditions.", "../frogpilot/assets/toggle_icons/icon_random.png"},
{"ScreenBrightness", "Screen Brightness", "Customize your screen brightness.", "../frogpilot/assets/toggle_icons/icon_light.png"},
{"SilentMode", "Silent Mode", "Mute openpilot sounds for a quieter driving experience.", "../frogpilot/assets/toggle_icons/icon_mute.png"},
{"WheelIcon", "Steering Wheel Icon", "Replace the default steering wheel icon with a custom design, adding a unique touch to your interface.", "../assets/offroad/icon_openpilot.png"},
};
for (const auto &[param, title, desc, icon] : visualToggles) {
ParamControl *toggle;
if (param == "CameraView") {
std::vector<QString> cameraOptions{tr("Auto"), tr("Standard"), tr("Wide"), tr("Driver")};
FrogPilotButtonParamControl *preferredCamera = new FrogPilotButtonParamControl(param, title, desc, icon, cameraOptions);
toggle = preferredCamera;
} else if (param == "CustomTheme") {
FrogPilotParamManageControl *customThemeToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(customThemeToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(customThemeKeys.find(key.c_str()) != customThemeKeys.end());
}
});
toggle = customThemeToggle;
} else if (param == "CustomColors" || param == "CustomIcons" || param == "CustomSignals" || param == "CustomSounds") {
std::vector<QString> themeOptions{tr("Stock"), tr("Frog"), tr("Tesla"), tr("Stalin")};
FrogPilotButtonParamControl *themeSelection = new FrogPilotButtonParamControl(param, title, desc, icon, themeOptions);
toggle = themeSelection;
if (param == "CustomSounds") {
QObject::connect(static_cast<FrogPilotButtonParamControl*>(toggle), &FrogPilotButtonParamControl::buttonClicked, [this](int id) {
if (id == 1) {
if (FrogPilotConfirmationDialog::yesorno("Do you want to enable the bonus 'Goat' sound effect?", this)) {
params.putBool("GoatScream", true);
} else {
params.putBool("GoatScream", false);
}
}
});
}
} else if (param == "CustomUI") {
FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end());
}
});
toggle = customUIToggle;
} else if (param == "LeadInfo") {
std::vector<QString> leadInfoToggles{tr("UseSI")};
std::vector<QString> leadInfoToggleNames{tr("Use SI Values")};
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, leadInfoToggles, leadInfoToggleNames);
} else if (param == "ModelUI") {
FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(modelUIKeys.find(key.c_str()) != modelUIKeys.end());
}
});
toggle = modelUIToggle;
} else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, std::map<int, QString>(), this, false, " inches");
} else if (param == "PathEdgeWidth") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map<int, QString>(), this, false, "%");
} else if (param == "PathWidth") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map<int, QString>(), this, false, " feet", 10);
} else if (param == "QOLVisuals") {
FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(qolKeys.find(key.c_str()) != qolKeys.end());
}
});
toggle = qolToggle;
} else if (param == "ScreenBrightness") {
std::map<int, QString> brightnessLabels;
for (int i = 0; i <= 101; ++i) {
brightnessLabels[i] = i == 0 ? "Screen Off" : i == 101 ? "Auto" : QString::number(i) + "%";
}
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 101, brightnessLabels, this, false);
} else if (param == "WheelIcon") {
std::vector<QString> wheelToggles{"RotatingWheel"};
std::vector<QString> wheelToggleNames{tr("Rotating")};
std::map<int, QString> steeringWheelLabels = {{0, "Stock"}, {1, "Lexus"}, {2, "Toyota"}, {3, "Frog"}, {4, "Rocket"}, {5, "Hyundai"}, {6, "Stalin"}};
toggle = new FrogPilotParamValueToggleControl(param, title, desc, icon, 0, 6, steeringWheelLabels, this, true, "", 1, wheelToggles, wheelToggleNames);
} else {
toggle = new ParamControl(param, title, desc, icon, this);
}
addItem(toggle);
toggles[param.toStdString()] = toggle;
QObject::connect(toggle, &ToggleControl::toggleFlipped, [this]() {
updateToggles();
});
QObject::connect(static_cast<FrogPilotParamValueControl*>(toggle), &FrogPilotParamValueControl::buttonPressed, [this]() {
updateToggles();
});
QObject::connect(toggle, &AbstractControl::showDescriptionEvent, [this]() {
update();
});
QObject::connect(static_cast<FrogPilotParamManageControl*>(toggle), &FrogPilotParamManageControl::manageButtonClicked, [this]() {
update();
});
}
std::set<std::string> rebootKeys = {"DriveStats"};
for (const std::string &key : rebootKeys) {
QObject::connect(toggles[key], &ToggleControl::toggleFlipped, [this]() {
if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) {
Hardware::reboot();
}
});
}
customOnroadUIKeys = {"AdjacentPath", "BlindSpotPath", "ShowFPS", "LeadInfo", "RoadNameUI", "UseVienna"};
customThemeKeys = {"CustomColors", "CustomIcons", "CustomSignals", "CustomSounds"};
modelUIKeys = {"AccelerationPath", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"};
qolKeys = {"DriveStats", "HideSpeed", "ShowSLCOffset"};
QObject::connect(device(), &Device::interactiveTimeout, this, &FrogPilotVisualsPanel::hideSubToggles);
QObject::connect(parent, &SettingsWindow::closeParentToggle, this, &FrogPilotVisualsPanel::hideSubToggles);
QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotVisualsPanel::updateMetric);
hideSubToggles();
updateMetric();
}
void FrogPilotVisualsPanel::updateToggles() {
std::thread([this]() {
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
}).detach();
}
void FrogPilotVisualsPanel::updateMetric() {
bool previousIsMetric = isMetric;
isMetric = params.getBool("IsMetric");
if (isMetric != previousIsMetric) {
double distanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH;
double speedConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
params.putInt("LaneLinesWidth", std::nearbyint(params.getInt("LaneLinesWidth") * distanceConversion));
params.putInt("RoadEdgesWidth", std::nearbyint(params.getInt("RoadEdgesWidth") * distanceConversion));
params.putInt("PathWidth", std::nearbyint(params.getInt("PathWidth") * speedConversion));
}
FrogPilotParamValueControl *laneLinesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["LaneLinesWidth"]);
FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["RoadEdgesWidth"]);
FrogPilotParamValueControl *pathWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["PathWidth"]);
if (isMetric) {
laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the Vienna average of 10 centimeters.");
roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the Vienna average lane line width of 10 centimeters.");
laneLinesWidthToggle->updateControl(0, 60, " centimeters");
roadEdgesWidthToggle->updateControl(0, 60, " centimeters");
pathWidthToggle->updateControl(0, 30, " meters");
} else {
laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the MUTCD average of 4 inches.");
roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches.");
laneLinesWidthToggle->updateControl(0, 24, " inches");
roadEdgesWidthToggle->updateControl(0, 24, " inches");
pathWidthToggle->updateControl(0, 100, " feet");
}
laneLinesWidthToggle->refresh();
roadEdgesWidthToggle->refresh();
previousIsMetric = isMetric;
}
void FrogPilotVisualsPanel::parentToggleClicked() {
this->openParentToggle();
}
void FrogPilotVisualsPanel::hideSubToggles() {
for (auto &[key, toggle] : toggles) {
bool subToggles = modelUIKeys.find(key.c_str()) != modelUIKeys.end() ||
customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end() ||
customThemeKeys.find(key.c_str()) != customThemeKeys.end() ||
qolKeys.find(key.c_str()) != qolKeys.end();
toggle->setVisible(!subToggles);
}
this->closeParentToggle();
}
void FrogPilotVisualsPanel::hideEvent(QHideEvent *event) {
hideSubToggles();
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <set>
#include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/ui/qt/offroad/settings.h"
class FrogPilotVisualsPanel : public FrogPilotListWidget {
Q_OBJECT
public:
explicit FrogPilotVisualsPanel(SettingsWindow *parent);
signals:
void closeParentToggle();
void openParentToggle();
private:
void hideEvent(QHideEvent *event);
void hideSubToggles();
void parentToggleClicked();
void updateMetric();
void updateToggles();
std::set<QString> customOnroadUIKeys;
std::set<QString> customThemeKeys;
std::set<QString> modelUIKeys;
std::set<QString> qolKeys;
std::map<std::string, ParamControl*> toggles;
Params params;
Params paramsMemory{"/dev/shm/params"};
bool isMetric = params.getBool("IsMetric");
};

View File

@@ -19,12 +19,15 @@ class DRIVER_MONITOR_SETTINGS():
def __init__(self): def __init__(self):
self._DT_DMON = DT_DMON self._DT_DMON = DT_DMON
# ref (page15-16): https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:42018X1947&rid=2 # ref (page15-16): https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:42018X1947&rid=2
self._AWARENESS_TIME = 90. # passive wheeltouch total timeout # self._AWARENESS_TIME = 90. # passive wheeltouch total timeout
self._AWARENESS_PRE_TIME_TILL_TERMINAL = 60. # self._AWARENESS_PRE_TIME_TILL_TERMINAL = 60.
self._AWARENESS_PROMPT_TIME_TILL_TERMINAL = 30. # self._AWARENESS_PROMPT_TIME_TILL_TERMINAL = 30.
self._DISTRACTED_TIME = 90. # active monitoring total timeout self._AWARENESS_TIME = 30. # passive wheeltouch total timeout
self._DISTRACTED_PRE_TIME_TILL_TERMINAL = 60. self._AWARENESS_PRE_TIME_TILL_TERMINAL = 15.
self._DISTRACTED_PROMPT_TIME_TILL_TERMINAL = 30. self._AWARENESS_PROMPT_TIME_TILL_TERMINAL = 6.
self._DISTRACTED_TIME = 60. # active monitoring total timeout
self._DISTRACTED_PRE_TIME_TILL_TERMINAL = 15.
self._DISTRACTED_PROMPT_TIME_TILL_TERMINAL = 10.
self._FACE_THRESHOLD = 0.7 self._FACE_THRESHOLD = 0.7
self._EYE_THRESHOLD = 0.65 self._EYE_THRESHOLD = 0.65

View File

@@ -0,0 +1,22 @@
import cereal.messaging as messaging
import numpy as np
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip, interp
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, V_CRUISE_MAX
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC
from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, A_CRUISE_MAX_VALS, A_CRUISE_MAX_BP, get_max_accel
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.frogpilot.functions.conditional_experimental_mode import ConditionalExperimentalMode
from openpilot.selfdrive.frogpilot.functions.map_turn_speed_controller import MapTurnSpeedController
from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController
# Class for intercepting when buttons are pressed, or dispatching buttons to be pressed
class OscarPilotButtons:
def __init__(self, params, params_memory):
self.params_memory = params_memory

View File

@@ -0,0 +1,40 @@
import cereal.messaging as messaging
import numpy as np
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip, interp
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, V_CRUISE_MAX
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC
from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, A_CRUISE_MAX_VALS, A_CRUISE_MAX_BP, get_max_accel
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.frogpilot.functions.conditional_experimental_mode import ConditionalExperimentalMode
from openpilot.selfdrive.frogpilot.functions.map_turn_speed_controller import MapTurnSpeedController
from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController
# class FrogPilotPlanner:
# def __init__(self, params, params_memory):
# self.params_memory = params_memory
# self.cem = ConditionalExperimentalMode()
# self.mtsc = MapTurnSpeedController()
# self.override_slc = False
# self.overridden_speed = 0
# self.mtsc_target = 0
# self.slc_target = 0
# self.v_cruise = 0
# self.vtsc_target = 0
# self.x_desired_trajectory = np.zeros(CONTROL_N)
# self.update_frogpilot_params(params, params_memory)
# def update(self, sm, mpc):
# carState, controlsState, modelData = sm['carState'], sm['controlsState'], sm['modelV2']
# enabled = controlsState.enabled
# v_cruise_kph = min(controlsState.vCruise, V_CRUISE_MAX)
# v_cruise = v_cruise_kph * CV.KPH_TO_MS
# v_ego = carState.vEgo

View File

@@ -0,0 +1 @@
// Should show a UI showing the settings menu for stock frogpilot

View File

View File

@@ -0,0 +1,146 @@
#include "selfdrive/oscarpilot/settings/basic.h"
#include "selfdrive/ui/ui.h"
OscarPilotBasicPanel::OscarPilotBasicPanel(OscarSettingsWindow *parent) : FrogPilotListWidget(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> visualToggles {
// {"HelloWorld", "Hello World", "Hi!", "../frogpilot/assets/wheel_images/frog.png"},
{
"OpenpilotEnabledToggle","Enable OpenPilot",
"Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off.",
"../assets/offroad/icon_openpilot.png",
},
{
"AlwaysOnLateral", "Always on Lateral",
"Maintain openpilot lateral control when the brake or gas pedals are used.\n\nDeactivation occurs only through the 'Cruise Control' button.", "../frogpilot/assets/toggle_icons/icon_always_on_lateral.png"
},
{"VisionTurnControl", "Vision Turn Speed Controller", "Slow down for detected road curvature for smoother curve handling.", "../frogpilot/assets/toggle_icons/icon_vtc.png"},
// {
// "ConditionalExperimental", "Slow for curves",
// "Engages 'experimental mode' when a curve is detected, temporairly reducing max speed.", "../frogpilot/assets/toggle_icons/icon_conditional.png"
// },
// Alert on stopsign / stoplight
{"LaneChangeAssist", "Lane Change Assist", "Automatically change lanes on turn signal and wheel nudge at highway speeds.", "../frogpilot/assets/toggle_icons/icon_lane.png"},
{"FireTheBabysitter", "Custom Driver Monitoring", "Customize the driver awareness alert timeouts.", ""},
// - Hands On Wheel: Always / At Dusk / 2+ hrs driving
{"DashCam", "Dash Cam Recording", "Record video and gps data for all drives automatically.", ""}
};
for (const auto &[param, title, desc, icon] : visualToggles) {
ParamControl *toggle;
toggle = new ParamControl(param, title, desc, icon, this);
addItem(toggle);
toggles[param.toStdString()] = toggle;
QObject::connect(toggle, &ToggleControl::toggleFlipped, [this]() {
updateToggles();
});
QObject::connect(static_cast<FrogPilotParamValueControl*>(toggle), &FrogPilotParamValueControl::buttonPressed, [this]() {
updateToggles();
});
QObject::connect(toggle, &AbstractControl::showDescriptionEvent, [this]() {
update();
});
QObject::connect(static_cast<FrogPilotParamManageControl*>(toggle), &FrogPilotParamManageControl::manageButtonClicked, [this]() {
update();
});
}
// std::set<std::string> rebootKeys = {"DriveStats"};
// for (const std::string &key : rebootKeys) {
// QObject::connect(toggles[key], &ToggleControl::toggleFlipped, [this]() {
// if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) {
// Hardware::reboot();
// }
// });
// }
// power buttons
QHBoxLayout *power_layout = new QHBoxLayout();
power_layout->setSpacing(30);
QPushButton *reboot_btn = new QPushButton(tr("Reboot"));
reboot_btn->setObjectName("reboot_btn");
power_layout->addWidget(reboot_btn);
QObject::connect(reboot_btn, &QPushButton::clicked, this, &OscarPilotBasicPanel::reboot);
QPushButton *poweroff_btn = new QPushButton(tr("Power Off"));
poweroff_btn->setObjectName("poweroff_btn");
power_layout->addWidget(poweroff_btn);
QObject::connect(poweroff_btn, &QPushButton::clicked, this, &OscarPilotBasicPanel::poweroff);
if (!Hardware::PC()) {
connect(uiState(), &UIState::offroadTransition, poweroff_btn, &QPushButton::setVisible);
}
setStyleSheet(R"(
#reboot_btn { height: 120px; border-radius: 15px; background-color: #393939; }
#reboot_btn:pressed { background-color: #4a4a4a; }
#poweroff_btn { height: 120px; border-radius: 15px; background-color: #E22C2C; }
#poweroff_btn:pressed { background-color: #FF2424; }
)");
addItem(power_layout);
}
void OscarPilotBasicPanel::updateToggles() {
std::thread([this]() {
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
}).detach();
}
void OscarPilotBasicPanel::parentToggleClicked() {
this->openParentToggle();
}
void OscarPilotBasicPanel::hideSubToggles() {
// for (auto &[key, toggle] : toggles) {
// bool subToggles = modelUIKeys.find(key.c_str()) != modelUIKeys.end() ||
// customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end() ||
// customThemeKeys.find(key.c_str()) != customThemeKeys.end() ||
// qolKeys.find(key.c_str()) != qolKeys.end();
// toggle->setVisible(!subToggles);
// }
this->closeParentToggle();
}
void OscarPilotBasicPanel::hideEvent(QHideEvent *event) {
hideSubToggles();
}
void OscarPilotBasicPanel::reboot() {
if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reboot?"), tr("Reboot"), this)) {
// Check engaged again in case it changed while the dialog was open
if (!uiState()->engaged()) {
params.putBool("DoReboot", true);
}
}
} else {
ConfirmationDialog::alert(tr("Disengage to Reboot"), this);
}
}
void OscarPilotBasicPanel::poweroff() {
if (!uiState()->engaged()) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to power off?"), tr("Power Off"), this)) {
// Check engaged again in case it changed while the dialog was open
if (!uiState()->engaged()) {
params.putBool("DoShutdown", true);
}
}
} else {
ConfirmationDialog::alert(tr("Disengage to Power Off"), this);
}
}

View File

@@ -0,0 +1,280 @@
#include "selfdrive/oscarpilot/settings/basic.h"
#include "selfdrive/ui/ui.h"
OscarPilotBasicPanel::OscarPilotBasicPanel(OscarSettingsWindow *parent) : FrogPilotListWidget(parent) {
const std::vector<std::tuple<QString, QString, QString, QString>> visualToggles {
{"CustomTheme", "Custom Themes", "Enable the ability to use custom themes.", "../frogpilot/assets/wheel_images/frog.png"},
{"CustomColors", "Color Theme", "Switch out the standard openpilot color scheme with a custom color scheme.\n\nWant to submit your own color scheme? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomIcons", "Icon Pack", "Switch out the standard openpilot icons with a set of custom icons.\n\nWant to submit your own icon pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomSignals", "Turn Signals", "Add custom animations for your turn signals for a personal touch!\n\nWant to submit your own turn signal animation? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CustomSounds", "Sound Pack", "Switch out the standard openpilot sounds with a set of custom sounds.\n\nWant to submit your own sound pack? Post it in the 'feature-request' channel in the FrogPilot Discord!", ""},
{"CameraView", "Camera View", "Choose your preferred camera view for the onroad UI. This is a visual change only and doesn't impact openpilot.", "../frogpilot/assets/toggle_icons/icon_camera.png"},
{"Compass", "Compass", "Add a compass to your onroad UI.", "../frogpilot/assets/toggle_icons/icon_compass.png"},
{"CustomUI", "Custom Onroad UI", "Customize the Onroad UI with some additional visual functions.", "../assets/offroad/icon_road.png"},
{"AdjacentPath", "Adjacent Paths", "Display paths to the left and right of your car, visualizing where the model detects lanes.", ""},
{"BlindSpotPath", "Blind Spot Path", "Visualize your blind spots with a red path when another vehicle is detected nearby.", ""},
{"ShowFPS", "FPS Counter", "Display the Frames Per Second (FPS) of your onroad UI for monitoring system performance.", ""},
{"LeadInfo", "Lead Info and Logics", "Get detailed information about the vehicle ahead, including speed and distance, and the logic behind your following distance.", ""},
{"RoadNameUI", "Road Name", "See the name of the road you're on at the bottom of your screen. Sourced from OpenStreetMap.", ""},
{"UseVienna", "Use Vienna Speed Limit Signs", "Use the Vienna (EU) speed limit style signs as opposed to MUTCD (US).", ""},
{"DriverCamera", "Driver Camera On Reverse", "Show the driver's camera feed when you shift to reverse.", "../assets/img_driver_face_static.png"},
{"GreenLightAlert", "Green Light Alert", "Get an alert when a traffic light changes from red to green.", "../frogpilot/assets/toggle_icons/icon_green_light.png"},
{"ModelUI", "Model UI", "Personalize how the model's visualizations appear on your screen.", "../assets/offroad/icon_calibration.png"},
{"AccelerationPath", "Acceleration Path", "Visualize the car's intended acceleration or deceleration with a color-coded path.", ""},
{"LaneLinesWidth", "Lane Lines", "Adjust the visual thickness of lane lines on your display.\n\nDefault matches the MUTCD average of 4 inches.", ""},
{"PathEdgeWidth", "Path Edges", "Adjust the width of the path edges shown on your UI to represent different driving modes and statuses.\n\nDefault is 20% of the total path.\n\nBlue = Navigation\nLight Blue = Always On Lateral\nGreen = Default with 'FrogPilot Colors'\nLight Green = Default with stock colors\nOrange = Experimental Mode Active\nYellow = Conditional Overriden", ""},
{"PathWidth", "Path Width", "Customize the width of the driving path shown on your UI.\n\nDefault matches the width of a 2019 Lexus ES 350.", ""},
{"RoadEdgesWidth", "Road Edges", "Adjust the visual thickness of road edges on your display.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches.", ""},
{"UnlimitedLength", "'Unlimited' Road UI Length", "Extend the display of the path, lane lines, and road edges as far as the system can detect, providing a more expansive view of the road ahead.", ""},
{"QOLVisuals", "Quality of Life", "Miscellaneous quality of life changes to improve your overall openpilot experience.", "../frogpilot/assets/toggle_icons/quality_of_life.png"},
{"DriveStats", "Drive Stats In Home Screen", "Display your device's drive stats in the home screen.", ""},
{"HideSpeed", "Hide Speed", "Hide the speed indicator in the onroad UI.", ""},
{"ShowSLCOffset", "Show Speed Limit Offset", "Show the speed limit offset seperated from the speed limit in the onroad UI when using 'Speed Limit Controller'.", ""},
{"RandomEvents", "Random Events", "Enjoy a bit of unpredictability with random events that can occur during certain driving conditions.", "../frogpilot/assets/toggle_icons/icon_random.png"},
{"ScreenBrightness", "Screen Brightness", "Customize your screen brightness.", "../frogpilot/assets/toggle_icons/icon_light.png"},
{"SilentMode", "Silent Mode", "Mute openpilot sounds for a quieter driving experience.", "../frogpilot/assets/toggle_icons/icon_mute.png"},
{"WheelIcon", "Steering Wheel Icon", "Replace the default steering wheel icon with a custom design, adding a unique touch to your interface.", "../assets/offroad/icon_openpilot.png"},
};
for (const auto &[param, title, desc, icon] : visualToggles) {
ParamControl *toggle;
if (param == "CameraView") {
std::vector<QString> cameraOptions{tr("Auto"), tr("Standard"), tr("Wide"), tr("Driver")};
FrogPilotButtonParamControl *preferredCamera = new FrogPilotButtonParamControl(param, title, desc, icon, cameraOptions);
toggle = preferredCamera;
} else if (param == "CustomTheme") {
FrogPilotParamManageControl *customThemeToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(customThemeToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(customThemeKeys.find(key.c_str()) != customThemeKeys.end());
}
});
toggle = customThemeToggle;
} else if (param == "CustomColors" || param == "CustomIcons" || param == "CustomSignals" || param == "CustomSounds") {
std::vector<QString> themeOptions{tr("Stock"), tr("Frog"), tr("Tesla"), tr("Stalin")};
FrogPilotButtonParamControl *themeSelection = new FrogPilotButtonParamControl(param, title, desc, icon, themeOptions);
toggle = themeSelection;
if (param == "CustomSounds") {
QObject::connect(static_cast<FrogPilotButtonParamControl*>(toggle), &FrogPilotButtonParamControl::buttonClicked, [this](int id) {
if (id == 1) {
if (FrogPilotConfirmationDialog::yesorno("Do you want to enable the bonus 'Goat' sound effect?", this)) {
params.putBool("GoatScream", true);
} else {
params.putBool("GoatScream", false);
}
}
});
}
} else if (param == "CustomUI") {
FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end());
}
});
toggle = customUIToggle;
} else if (param == "LeadInfo") {
std::vector<QString> leadInfoToggles{tr("UseSI")};
std::vector<QString> leadInfoToggleNames{tr("Use SI Values")};
toggle = new FrogPilotParamToggleControl(param, title, desc, icon, leadInfoToggles, leadInfoToggleNames);
} else if (param == "ModelUI") {
FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(modelUIKeys.find(key.c_str()) != modelUIKeys.end());
}
});
toggle = modelUIToggle;
} else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, std::map<int, QString>(), this, false, " inches");
} else if (param == "PathEdgeWidth") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map<int, QString>(), this, false, "%");
} else if (param == "PathWidth") {
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, std::map<int, QString>(), this, false, " feet", 10);
} else if (param == "QOLVisuals") {
FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon, this);
QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, this, [this]() {
parentToggleClicked();
for (auto &[key, toggle] : toggles) {
toggle->setVisible(qolKeys.find(key.c_str()) != qolKeys.end());
}
});
toggle = qolToggle;
} else if (param == "ScreenBrightness") {
std::map<int, QString> brightnessLabels;
for (int i = 0; i <= 101; ++i) {
brightnessLabels[i] = i == 0 ? "Screen Off" : i == 101 ? "Auto" : QString::number(i) + "%";
}
toggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 101, brightnessLabels, this, false);
} else if (param == "WheelIcon") {
std::vector<QString> wheelToggles{"RotatingWheel"};
std::vector<QString> wheelToggleNames{tr("Rotating")};
std::map<int, QString> steeringWheelLabels = {{0, "Stock"}, {1, "Lexus"}, {2, "Toyota"}, {3, "Frog"}, {4, "Rocket"}, {5, "Hyundai"}, {6, "Stalin"}};
toggle = new FrogPilotParamValueToggleControl(param, title, desc, icon, 0, 6, steeringWheelLabels, this, true, "", 1, wheelToggles, wheelToggleNames);
} else {
toggle = new ParamControl(param, title, desc, icon, this);
}
addItem(toggle);
toggles[param.toStdString()] = toggle;
QObject::connect(toggle, &ToggleControl::toggleFlipped, [this]() {
updateToggles();
});
QObject::connect(static_cast<FrogPilotParamValueControl*>(toggle), &FrogPilotParamValueControl::buttonPressed, [this]() {
updateToggles();
});
QObject::connect(toggle, &AbstractControl::showDescriptionEvent, [this]() {
update();
});
QObject::connect(static_cast<FrogPilotParamManageControl*>(toggle), &FrogPilotParamManageControl::manageButtonClicked, [this]() {
update();
});
}
std::set<std::string> rebootKeys = {"DriveStats"};
for (const std::string &key : rebootKeys) {
QObject::connect(toggles[key], &ToggleControl::toggleFlipped, [this]() {
if (FrogPilotConfirmationDialog::toggle("Reboot required to take effect.", "Reboot Now", this)) {
Hardware::reboot();
}
});
}
customOnroadUIKeys = {"AdjacentPath", "BlindSpotPath", "ShowFPS", "LeadInfo", "RoadNameUI", "UseVienna"};
customThemeKeys = {"CustomColors", "CustomIcons", "CustomSignals", "CustomSounds"};
modelUIKeys = {"AccelerationPath", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"};
qolKeys = {"DriveStats", "HideSpeed", "ShowSLCOffset"};
QObject::connect(device(), &Device::interactiveTimeout, this, &OscarPilotBasicPanel::hideSubToggles);
QObject::connect(parent, &OscarSettingsWindow::closeParentToggle, this, &OscarPilotBasicPanel::hideSubToggles);
// QObject::connect(parent, &OscarSettingsWindow::updateMetric, this, &OscarPilotBasicPanel::updateMetric);
hideSubToggles();
// updateMetric();
}
void OscarPilotBasicPanel::updateToggles() {
std::thread([this]() {
paramsMemory.putBool("FrogPilotTogglesUpdated", true);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
paramsMemory.putBool("FrogPilotTogglesUpdated", false);
}).detach();
}
// void OscarPilotBasicPanel::updateMetric() {
// bool previousIsMetric = isMetric;
// isMetric = params.getBool("IsMetric");
// if (isMetric != previousIsMetric) {
// double distanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH;
// double speedConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
// params.putInt("LaneLinesWidth", std::nearbyint(params.getInt("LaneLinesWidth") * distanceConversion));
// params.putInt("RoadEdgesWidth", std::nearbyint(params.getInt("RoadEdgesWidth") * distanceConversion));
// params.putInt("PathWidth", std::nearbyint(params.getInt("PathWidth") * speedConversion));
// }
// FrogPilotParamValueControl *laneLinesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["LaneLinesWidth"]);
// FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["RoadEdgesWidth"]);
// FrogPilotParamValueControl *pathWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["PathWidth"]);
// if (isMetric) {
// laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the Vienna average of 10 centimeters.");
// roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the Vienna average lane line width of 10 centimeters.");
// laneLinesWidthToggle->updateControl(0, 60, " centimeters");
// roadEdgesWidthToggle->updateControl(0, 60, " centimeters");
// pathWidthToggle->updateControl(0, 30, " meters");
// } else {
// laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the MUTCD average of 4 inches.");
// roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches.");
// laneLinesWidthToggle->updateControl(0, 24, " inches");
// roadEdgesWidthToggle->updateControl(0, 24, " inches");
// pathWidthToggle->updateControl(0, 100, " feet");
// }
// laneLinesWidthToggle->refresh();
// roadEdgesWidthToggle->refresh();
// previousIsMetric = isMetric;
// }
// void OscarPilotBasicPanel::updateMetric() {
// bool previousIsMetric = isMetric;
// isMetric = params.getBool("IsMetric");
// if (isMetric != previousIsMetric) {
// double distanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH;
// double speedConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
// params.putInt("LaneLinesWidth", std::nearbyint(params.getInt("LaneLinesWidth") * distanceConversion));
// params.putInt("RoadEdgesWidth", std::nearbyint(params.getInt("RoadEdgesWidth") * distanceConversion));
// params.putInt("PathWidth", std::nearbyint(params.getInt("PathWidth") * speedConversion));
// }
// FrogPilotParamValueControl *laneLinesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["LaneLinesWidth"]);
// FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["RoadEdgesWidth"]);
// FrogPilotParamValueControl *pathWidthToggle = static_cast<FrogPilotParamValueControl*>(toggles["PathWidth"]);
// if (isMetric) {
// laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the Vienna average of 10 centimeters.");
// roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the Vienna average lane line width of 10 centimeters.");
// laneLinesWidthToggle->updateControl(0, 60, " centimeters");
// roadEdgesWidthToggle->updateControl(0, 60, " centimeters");
// pathWidthToggle->updateControl(0, 30, " meters");
// } else {
// laneLinesWidthToggle->setDescription("Customize the lane line width.\n\nDefault matches the MUTCD average of 4 inches.");
// roadEdgesWidthToggle->setDescription("Customize the road edges width.\n\nDefault is 1/2 of the MUTCD average lane line width of 4 inches.");
// laneLinesWidthToggle->updateControl(0, 24, " inches");
// roadEdgesWidthToggle->updateControl(0, 24, " inches");
// pathWidthToggle->updateControl(0, 100, " feet");
// }
// laneLinesWidthToggle->refresh();
// roadEdgesWidthToggle->refresh();
// previousIsMetric = isMetric;
// }
void OscarPilotBasicPanel::parentToggleClicked() {
this->openParentToggle();
}
void OscarPilotBasicPanel::hideSubToggles() {
for (auto &[key, toggle] : toggles) {
bool subToggles = modelUIKeys.find(key.c_str()) != modelUIKeys.end() ||
customOnroadUIKeys.find(key.c_str()) != customOnroadUIKeys.end() ||
customThemeKeys.find(key.c_str()) != customThemeKeys.end() ||
qolKeys.find(key.c_str()) != qolKeys.end();
toggle->setVisible(!subToggles);
}
this->closeParentToggle();
}
void OscarPilotBasicPanel::hideEvent(QHideEvent *event) {
hideSubToggles();
}

View File

@@ -0,0 +1,34 @@
#pragma once
#include <set>
#include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/oscarpilot/settings/settings.h"
class OscarPilotBasicPanel : public FrogPilotListWidget {
Q_OBJECT
public:
explicit OscarPilotBasicPanel(OscarSettingsWindow *parent);
signals:
void closeParentToggle();
void openParentToggle();
private:
void hideEvent(QHideEvent *event);
void hideSubToggles();
void parentToggleClicked();
void updateToggles();
void poweroff();
void reboot();
std::set<QString> customOnroadUIKeys;
std::map<std::string, ParamControl*> toggles;
Params params;
Params paramsMemory{"/dev/shm/params"};
bool isMetric = params.getBool("IsMetric");
};

View File

@@ -0,0 +1,36 @@
#pragma once
#include <set>
#include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/oscarpilot/settings/settings.h"
class OscarPilotBasicPanel : public FrogPilotListWidget {
Q_OBJECT
public:
explicit OscarPilotBasicPanel(OscarSettingsWindow *parent);
signals:
void closeParentToggle();
void openParentToggle();
private:
void hideEvent(QHideEvent *event);
void hideSubToggles();
void parentToggleClicked();
void updateMetric();
void updateToggles();
std::set<QString> customOnroadUIKeys;
std::set<QString> customThemeKeys;
std::set<QString> modelUIKeys;
std::set<QString> qolKeys;
std::map<std::string, ParamControl*> toggles;
Params params;
Params paramsMemory{"/dev/shm/params"};
bool isMetric = params.getBool("IsMetric");
};

View File

@@ -0,0 +1,177 @@
#include <filesystem>
#include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/ui/ui.h"
bool FrogPilotConfirmationDialog::toggle(const QString &prompt_text, const QString &confirm_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, confirm_text, tr("Reboot Later"), false, parent);
return d.exec();
}
bool FrogPilotConfirmationDialog::toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, button_text, "", false, parent);
return d.exec();
}
bool FrogPilotConfirmationDialog::yesorno(const QString &prompt_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, tr("Yes"), tr("No"), false, parent);
return d.exec();
}
FrogPilotButtonIconControl::FrogPilotButtonIconControl(const QString &title, const QString &text, const QString &desc, const QString &icon, QWidget *parent) : AbstractControl(title, desc, icon, parent) {
btn.setText(text);
btn.setStyleSheet(R"(
QPushButton {
padding: 0;
border-radius: 50px;
font-size: 35px;
font-weight: 500;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)");
btn.setFixedSize(250, 100);
QObject::connect(&btn, &QPushButton::clicked, this, &FrogPilotButtonIconControl::clicked);
hlayout->addWidget(&btn);
}
void setDefaultParams() {
Params params = Params();
bool FrogsGoMoo = params.get("DongleId").substr(0, 3) == "be6";
bool brianbot = params.get("DongleId").substr(0, 6) == "90bb71";
std::map<std::string, std::string> defaultValues {
{"AccelerationPath", brianbot ? "1" : "0"},
{"AccelerationProfile", brianbot ? "2" : "2"},
{"AdjacentPath", FrogsGoMoo ? "1" : "0"},
{"AdjustablePersonalities", "0"},
{"AggressiveAcceleration", "1"},
{"AggressiveFollow", FrogsGoMoo ? "10" : "12"},
{"AggressiveJerk", FrogsGoMoo ? "6" : "5"},
{"AlwaysOnLateral", "1"},
{"AlwaysOnLateralMain", brianbot ? "1" : "0"},
{"AverageCurvature", brianbot ? "1" : "0"},
{"BlindSpotPath", "1"},
{"CameraView", FrogsGoMoo ? "1" : "0"},
{"CECurves", "1"},
{"CECurvesLead", "0"},
{"CENavigation", "1"},
{"CESignal", "1"},
{"CESlowerLead", "0"},
{"CESpeed", "0"},
{"CESpeedLead", "0"},
{"CEStopLights", "0"},
{"CEStopLightsLead", FrogsGoMoo ? "0" : "0"},
{"Compass", FrogsGoMoo ? "1" : "0"},
{"ConditionalExperimental", "1"},
{"CurveSensitivity", FrogsGoMoo ? "125" : "100"},
{"CustomColors", "1"},
{"CustomIcons", "1"},
{"CustomPersonalities", "0"},
{"CustomSignals", "0"},
{"CustomSounds", "1"},
{"CustomTheme", "1"},
{"CustomUI", "0"},
{"DeviceShutdown", "1"},
{"DisableOnroadUploads", "1"},
{"DriverCamera", "0"},
{"DriveStats", "1"},
{"EVTable", FrogsGoMoo ? "0" : "1"},
{"ExperimentalModeActivation", "1"},
{"ExperimentalModeViaLKAS", "0"},
{"ExperimentalModeViaScreen", FrogsGoMoo ? "0" : "1"},
{"Fahrenheit", "0"},
{"FireTheBabysitter", FrogsGoMoo ? "1" : "0"},
{"FullMap", "0"},
{"GasRegenCmd", "0"},
{"GoatScream", "0"},
{"GreenLightAlert", "0"},
{"HideSpeed", "0"},
{"HigherBitrate", "0"},
{"LaneChangeTime", "0"},
{"LaneDetection", "1"},
{"LaneLinesWidth", "4"},
{"LateralTune", "1"},
{"LeadInfo", FrogsGoMoo ? "1" : "0"},
{"LockDoors", "0"},
{"LongitudinalTune", "1"},
{"LongPitch", FrogsGoMoo ? "0" : "1"},
{"LowerVolt", FrogsGoMoo ? "0" : "1"},
{"Model", "3"},
{"ModelUI", "1"},
{"MTSCEnabled", "1"},
{"MuteDM", FrogsGoMoo ? "1" : "1"},
{"MuteDoor", FrogsGoMoo ? "1" : "1"},
{"MuteOverheated", FrogsGoMoo ? "1" : "0"},
{"MuteSeatbelt", FrogsGoMoo ? "1" : "0"},
{"NNFF", FrogsGoMoo ? "1" : "1"},
{"NoLogging", "1"},
{"NudgelessLaneChange", "0"},
{"NumericalTemp", FrogsGoMoo ? "1" : "0"},
{"Offset1", "3"},
{"Offset2", FrogsGoMoo ? "7" : "5"},
{"Offset3", "7"},
{"Offset4", FrogsGoMoo ? "20" : "7"},
{"OneLaneChange", "1"},
{"PathEdgeWidth", "20"},
{"PathWidth", "61"},
{"PauseLateralOnSignal", "20"},
{"PreferredSchedule", "0"},
{"QOLControls", "1"},
{"QOLVisuals", "1"},
{"RandomEvents", FrogsGoMoo ? "1" : "0"},
{"RelaxedFollow", "30"},
{"RelaxedJerk", "50"},
{"ReverseCruise", "0"},
{"RoadEdgesWidth", "2"},
{"RoadNameUI", "1"},
{"RotatingWheel", "1"},
{"ScreenBrightness", "101"},
{"SearchInput", "0"},
{"ShowCPU", FrogsGoMoo ? "1" : "0"},
{"ShowFPS", FrogsGoMoo ? "1" : "0"},
{"ShowGPU", "0"},
{"ShowMemoryUsage", FrogsGoMoo ? "1" : "0"},
{"Sidebar", FrogsGoMoo ? "1" : "0"},
{"SilentMode", "0"},
{"SLCFallback", "2"},
{"SLCOverride", FrogsGoMoo ? "2" : "1"},
{"SLCPriority", "1"},
{"SmoothBraking", "1"},
{"SNGHack", FrogsGoMoo ? "0" : "1"},
{"SpeedLimitController", "1"},
{"StandardFollow", "15"},
{"StandardJerk", "10"},
{"StoppingDistance", FrogsGoMoo ? "6" : "0"},
{"TSS2Tune", "1"},
{"TurnAggressiveness", FrogsGoMoo ? "150" : "100"}, // Test 90?
{"TurnDesires", "0"},
{"UnlimitedLength", "1"},
{"UseSI", FrogsGoMoo ? "1" : "0"},
{"UseVienna", "0"},
{"VisionTurnControl", "1"},
{"WheelIcon", FrogsGoMoo ? "1" : "0"}
};
bool rebootRequired = false;
for (const auto &[key, value] : defaultValues) {
if (params.get(key).empty()) {
params.put(key, value);
rebootRequired = true;
}
}
if (rebootRequired) {
while (!std::filesystem::exists("/data/openpilot/prebuilt")) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
Hardware::reboot();
}
}

View File

View File

@@ -0,0 +1,38 @@
Device
Network & Services
- Standard Wifi
- Metered Wifi
- SSH
- Web companion
- Route logging
- Dashcam upload
- Live driver monitoring / fleet mgr
- Weather
- Display alerts
- API key
- Random facts
- Get random fact about area driver is in (spoken, ai)
Basic
- Enable openpilot / Dashcam only
- Always on Lateral: True / False
- Longitudial Control scheme: Stock / Stock+SLC / OpenPilot
- Experimental Mode: never / on curves / always
- Longitudial Control scheme: Stock / Stock+SLC / Experimental
- Speed Limit offset customization:
- Lane Change Assist: Disabled / Nudge Wheel
- Driver Attention Monitoring: Strict / Relaxed
- Hands On Wheel: Always / At Dusk / 2+ hrs driving
Advanced
Navigation
Customization
Extras
- Window down notification
- Set climate on drive mode
Fleet Mgr Notes:
- Seperate fork for fleet mgr
- Self drive preferences
- Distance traveled
- SLC
- Driver camera

View File

@@ -0,0 +1,20 @@
Goals:
- slow down on experimental mode
- LKAS button - should toggle between drive, weather, weather radar. Hold for screen off
- car dashboard should have enough indicators that it is able to represent drive state without screen on
- set speed limit on engage cruise control
qol:
- customize babysitter - driver awareness timeouts daytime, nighttime, require steering wheel
- set climate on start, roll up windows on stop
- upload park location on stop
- low bandwidth / high bandwidth wifi, upload recordings on high bandwidth
- list drives w/ image on stop
- stop light / stop sign warning alert
release:
- reenable training screen
- - disregard for my device id
- cleanup code
- rename to diamondpilot

View File

@@ -0,0 +1,176 @@
#include "selfdrive/ui/qt/offroad/settings.h"
#include <cassert>
#include <cmath>
#include <string>
#include <tuple>
#include <vector>
#include <QDebug>
#include <QScrollBar>
#include "selfdrive/ui/qt/network/networking.h"
#include "common/params.h"
#include "common/watchdog.h"
#include "common/util.h"
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
#include "selfdrive/ui/qt/widgets/ssh_keys.h"
#include "selfdrive/ui/qt/widgets/toggle.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
#include "selfdrive/frogpilot/ui/control_settings.h"
#include "selfdrive/oscarpilot/settings/basic.h"
#include "selfdrive/frogpilot/ui/visual_settings.h"
#include "selfdrive/oscarpilot/settings/settings.h"
void OscarSettingsWindow::showEvent(QShowEvent *event) {
setCurrentPanel(0);
}
void OscarSettingsWindow::setCurrentPanel(int index, const QString &param) {
panel_widget->setCurrentIndex(index);
nav_btns->buttons()[index]->setChecked(true);
if (!param.isEmpty()) {
emit expandToggleDescription(param);
}
}
OscarSettingsWindow::OscarSettingsWindow(QWidget *parent) : QFrame(parent) {
// setup two main layouts
sidebar_widget = new QWidget;
QVBoxLayout *sidebar_layout = new QVBoxLayout(sidebar_widget);
sidebar_layout->setMargin(0);
panel_widget = new QStackedWidget();
// close button
QPushButton *close_btn = new QPushButton(tr("← Back"));
close_btn->setStyleSheet(R"(
QPushButton {
font-size: 50px;
padding-bottom: 0px;
border 1px grey solid;
border-radius: 25px;
background-color: #292929;
font-weight: 500;
}
QPushButton:pressed {
background-color: #3B3B3B;
}
)");
close_btn->setFixedSize(300, 125);
sidebar_layout->addSpacing(10);
sidebar_layout->addWidget(close_btn, 0, Qt::AlignRight);
QObject::connect(close_btn, &QPushButton::clicked, [this]() {
if (frogPilotTogglesOpen) {
frogPilotTogglesOpen = false;
this->closeParentToggle();
} else {
this->closeSettings();
}
});
FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
// DevicePanel *device = new DevicePanel(this);
// QObject::connect(device, &DevicePanel::reviewTrainingGuide, this, &SettingsWindow::reviewTrainingGuide);
// QObject::connect(device, &DevicePanel::showDriverView, this, &SettingsWindow::showDriverView);
QList<QPair<QString, QWidget *>> panels = {
{tr("Basic"), new OscarPilotBasicPanel(this)},
{tr("Advanced"), frogpilotControls},
{tr("Network"), new Networking(this)},
{tr("Software"), new SoftwarePanel(this)},
// {tr("Device"), new DevicePanel(this)},
// FrogPilotControlsPanel
// {tr("OscarCloud"), basic},
// {tr("Logging"), basic}, // Log / Upload driver cam, Routes
// {tr("System"), new OscarPilotBasicPanel(this)}, // Debugging
// {tr("Status"), basic}, // Report on stuff like connectivity, free space, detected features
// {tr("Extra"), basic}, // Custom cloud services, QOL automations
};
nav_btns = new QButtonGroup(this);
for (auto &[name, panel] : panels) {
QPushButton *btn = new QPushButton(name);
btn->setCheckable(true);
btn->setChecked(nav_btns->buttons().size() == 0);
btn->setStyleSheet(R"(
QPushButton {
color: grey;
border: none;
background: none;
font-size: 65px;
font-weight: 500;
}
QPushButton:checked {
color: white;
}
QPushButton:pressed {
color: #ADADAD;
}
)");
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
nav_btns->addButton(btn);
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins
panel->setContentsMargins(lr_margin, 25, lr_margin, 25);
ScrollView *panel_frame = new ScrollView(panel, this);
panel_widget->addWidget(panel_frame);
if (name == tr("Controls") || name == tr("Visuals")) {
QScrollBar *scrollbar = panel_frame->verticalScrollBar();
QObject::connect(scrollbar, &QScrollBar::valueChanged, this, [this](int value) {
if (!frogPilotTogglesOpen) {
previousScrollPosition = value;
}
});
QObject::connect(scrollbar, &QScrollBar::rangeChanged, this, [this, panel_frame]() {
panel_frame->restorePosition(previousScrollPosition);
});
}
QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() {
previousScrollPosition = 0;
btn->setChecked(true);
panel_widget->setCurrentWidget(w);
});
}
sidebar_layout->setContentsMargins(50, 50, 100, 50);
// main settings layout, sidebar + main panel
QHBoxLayout *main_layout = new QHBoxLayout(this);
sidebar_widget->setFixedWidth(500);
main_layout->addWidget(sidebar_widget);
main_layout->addWidget(panel_widget);
setStyleSheet(R"(
* {
color: white;
font-size: 50px;
}
OscarSettingsWindow {
background-color: black;
}
QStackedWidget, ScrollView {
background-color: #292929;
border-radius: 30px;
}
)");
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include <map>
#include <string>
#include <QButtonGroup>
#include <QFrame>
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QWidget>
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
// ********** settings window + top-level panels **********
class OscarSettingsWindow : public QFrame {
Q_OBJECT
public:
explicit OscarSettingsWindow(QWidget *parent = 0);
void setCurrentPanel(int index, const QString &param = "");
protected:
void showEvent(QShowEvent *event) override;
signals:
void closeSettings();
void showDriverView();
void expandToggleDescription(const QString &param);
// FrogPilot signals
void closeParentToggle();
private:
QWidget *sidebar_widget;
QButtonGroup *nav_btns;
QStackedWidget *panel_widget;
// FrogPilot variables
bool frogPilotTogglesOpen;
int previousScrollPosition;
};

View File

@@ -0,0 +1,42 @@
#include <filesystem>
#include "selfdrive/frogpilot/ui/frogpilot_functions.h"
#include "selfdrive/ui/ui.h"
bool FrogPilotConfirmationDialog::toggle(const QString &prompt_text, const QString &confirm_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, confirm_text, tr("Reboot Later"), false, parent);
return d.exec();
}
bool FrogPilotConfirmationDialog::toggleAlert(const QString &prompt_text, const QString &button_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, button_text, "", false, parent);
return d.exec();
}
bool FrogPilotConfirmationDialog::yesorno(const QString &prompt_text, QWidget *parent) {
ConfirmationDialog d = ConfirmationDialog(prompt_text, tr("Yes"), tr("No"), false, parent);
return d.exec();
}
FrogPilotButtonIconControl::FrogPilotButtonIconControl(const QString &title, const QString &text, const QString &desc, const QString &icon, QWidget *parent) : AbstractControl(title, desc, icon, parent) {
btn.setText(text);
btn.setStyleSheet(R"(
QPushButton {
padding: 0;
border-radius: 50px;
font-size: 35px;
font-weight: 500;
color: #E4E4E4;
background-color: #393939;
}
QPushButton:pressed {
background-color: #4a4a4a;
}
QPushButton:disabled {
color: #33E4E4E4;
}
)");
btn.setFixedSize(250, 100);
QObject::connect(&btn, &QPushButton::clicked, this, &FrogPilotButtonIconControl::clicked);
hlayout->addWidget(&btn);
}

View File

@@ -0,0 +1,16 @@
const button_style = R"(
QPushButton {
color: grey;
border: none;
background: none;
font-size: 65px;
font-weight: 500;
}
QPushButton:checked {
color: white;
}
QPushButton:pressed {
color: #ADADAD;
}
)";

View File

View File

View File

View File

View File

@@ -27,7 +27,9 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc", "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc",
"../frogpilot/ui/frogpilot_functions.cc", "../frogpilot/navigation/ui/navigation_settings.cc", "../frogpilot/ui/frogpilot_functions.cc", "../frogpilot/navigation/ui/navigation_settings.cc",
"../frogpilot/ui/control_settings.cc", "../frogpilot/ui/vehicle_settings.cc", "../frogpilot/ui/control_settings.cc", "../frogpilot/ui/vehicle_settings.cc",
"../frogpilot/ui/visual_settings.cc"] "../frogpilot/ui/visual_settings.cc",
"../oscarpilot/settings/settings.cc", "../oscarpilot/settings/basic.cc",
]
qt_env['CPPDEFINES'] = [] qt_env['CPPDEFINES'] = []
if maps: if maps:

View File

@@ -3,159 +3,54 @@
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
#include <QPainter> #include <QPainter>
#include <QStackedLayout> #include <QStackedLayout>
#include <QApplication>
#include <QGridLayout>
#include <QString>
#include <QTransform>
#include <QPixmap>
#include "common/params.h" #include "common/params.h"
#include "common/timing.h" #include "common/timing.h"
RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) { #include "system/hardware/hw.h"
setCheckable(true); #include "selfdrive/ui/qt/qt_window.h"
setChecked(false); #include "selfdrive/ui/qt/util.h"
setFixedSize(148, 148);
QObject::connect(this, &QPushButton::toggled, [=]() { BodyWindow::BodyWindow(QWidget *parent) : QWidget(parent) {
setEnabled(false); QGridLayout *layout = new QGridLayout(this);
}); layout->setSpacing(0);
} layout->setMargin(200);
void RecordButton::paintEvent(QPaintEvent *event) { setAttribute(Qt::WA_OpaquePaintEvent);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPoint center(width() / 2, height() / 2); setStyleSheet(R"(
BodyWindow {
QColor bg(isChecked() ? "#FFFFFF" : "#737373"); background-color: blue;
QColor accent(isChecked() ? "#FF0000" : "#FFFFFF"); }
if (!isEnabled()) { )");
bg = QColor("#404040");
accent = QColor("#FFFFFF");
}
if (isDown()) {
accent.setAlphaF(0.7);
}
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(center, 74, 74);
p.setPen(QPen(accent, 6));
p.setBrush(Qt::NoBrush);
p.drawEllipse(center, 42, 42);
p.setPen(Qt::NoPen);
p.setBrush(accent);
p.drawEllipse(center, 22, 22);
}
BodyWindow::BodyWindow(QWidget *parent) : fuel_filter(1.0, 5., 1. / UI_FREQ), QWidget(parent) {
QStackedLayout *layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
QWidget *w = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout(w);
vlayout->setMargin(45);
layout->addWidget(w);
// face
face = new QLabel();
face->setAlignment(Qt::AlignCenter);
layout->addWidget(face);
awake = new QMovie("../assets/body/awake.gif", {}, this);
awake->setCacheMode(QMovie::CacheAll);
sleep = new QMovie("../assets/body/sleep.gif", {}, this);
sleep->setCacheMode(QMovie::CacheAll);
// record button
btn = new RecordButton(this);
vlayout->addWidget(btn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(btn, &QPushButton::clicked, [=](bool checked) {
btn->setEnabled(false);
Params().putBool("DisableLogging", !checked);
last_button = nanos_since_boot();
});
w->raise();
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState); QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
} }
void BodyWindow::paintEvent(QPaintEvent *event) { void BodyWindow::paintEvent(QPaintEvent *event) {
QPainter p(this); QPainter painter(this);
p.setRenderHint(QPainter::Antialiasing);
p.fillRect(rect(), QColor(0, 0, 0)); QPixmap comma_img = loadPixmap("../assets/oscarpilot_ready.png");
// battery outline + detail // Calculate the top-left position to center the image in the window.
p.translate(width() - 136, 16); int x = (this->width() - comma_img.width()) / 2;
const QColor gray = QColor("#737373"); int y = (this->height() - comma_img.height()) / 2;
p.setBrush(Qt::NoBrush);
p.setPen(QPen(gray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
p.drawRoundedRect(2, 2, 78, 36, 8, 8);
p.setPen(Qt::NoPen); // Draw the pixmap at the calculated position.
p.setBrush(gray); painter.drawPixmap(x, y, comma_img);
p.drawRoundedRect(84, 12, 6, 16, 4, 4); }
p.drawRect(84, 12, 3, 16);
// battery level
double fuel = std::clamp(fuel_filter.x(), 0.2f, 1.0f);
const int m = 5; // manual margin since we can't do an inner border
p.setPen(Qt::NoPen);
p.setBrush(fuel > 0.25 ? QColor("#32D74B") : QColor("#FF453A"));
p.drawRoundedRect(2 + m, 2 + m, (78 - 2*m)*fuel, 36 - 2*m, 4, 4);
// charging status void BodyWindow::updateState(const UIState &s) {
if (charging) {
p.setPen(Qt::NoPen);
p.setBrush(Qt::white);
const QPolygonF charger({
QPointF(12.31, 0),
QPointF(12.31, 16.92),
QPointF(18.46, 16.92),
QPointF(6.15, 40),
QPointF(6.15, 23.08),
QPointF(0, 23.08),
});
p.drawPolygon(charger.translated(98, 0));
}
} }
void BodyWindow::offroadTransition(bool offroad) { void BodyWindow::offroadTransition(bool offroad) {
btn->setChecked(true);
btn->setEnabled(true);
fuel_filter.reset(1.0);
}
void BodyWindow::updateState(const UIState &s) {
if (!isVisible()) {
return;
}
const SubMaster &sm = *(s.sm);
auto cs = sm["carState"].getCarState();
charging = cs.getCharging();
fuel_filter.update(cs.getFuelGauge());
// TODO: use carState.standstill when that's fixed
const bool standstill = std::abs(cs.getVEgo()) < 0.01;
QMovie *m = standstill ? sleep : awake;
if (m != face->movie()) {
face->setMovie(m);
face->movie()->start();
}
// update record button state
if (sm.updated("managerState") && (sm.rcv_time("managerState") - last_button)*1e-9 > 0.5) {
for (auto proc : sm["managerState"].getManagerState().getProcesses()) {
if (proc.getName() == "loggerd") {
btn->setEnabled(true);
btn->setChecked(proc.getRunning());
}
}
}
update();
} }

60
selfdrive/ui/qt/body.good Normal file
View File

@@ -0,0 +1,60 @@
#include "selfdrive/ui/qt/body.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QStackedLayout>
#include <QApplication>
#include <QGridLayout>
#include <QString>
#include <QTransform>
#include <QPixmap>
#include "common/params.h"
#include "common/timing.h"
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
void LogoWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
}
BodyWindow::BodyWindow(QWidget *parent) : QWidget(parent) {
QGridLayout *layout = new QGridLayout(this);
layout->setSpacing(0);
layout->setMargin(200);
setAttribute(Qt::WA_OpaquePaintEvent);
setStyleSheet(R"(
BodyWindow {
background-color: blue;
}
)");
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
}
void BodyWindow::paintEvent(QPaintEvent *event) {
QPainter painter(this);
QPixmap comma_img = loadPixmap("../assets/oscarpilot_ready.png");
// Calculate the top-left position to center the image in the window.
int x = (this->width() - comma_img.width()) / 2;
int y = (this->height() - comma_img.height()) / 2;
// Draw the pixmap at the calculated position.
painter.drawPixmap(x, y, comma_img);
}
void BodyWindow::updateState(const UIState &s) {
}
void BodyWindow::offroadTransition(bool offroad) {
}

View File

@@ -3,35 +3,21 @@
#include <QMovie> #include <QMovie>
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
#include <QPixmap>
#include <QProgressBar>
#include <QSocketNotifier>
#include <QVariantAnimation>
#include <QWidget>
#include "common/util.h" #include "common/util.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
class RecordButton : public QPushButton {
Q_OBJECT
public:
RecordButton(QWidget* parent = 0);
private:
void paintEvent(QPaintEvent*) override;
};
class BodyWindow : public QWidget { class BodyWindow : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
BodyWindow(QWidget* parent = 0); BodyWindow(QWidget* parent = 0);
private: private:
bool charging = false;
uint64_t last_button = 0;
FirstOrderFilter fuel_filter;
QLabel *face;
QMovie *awake, *sleep;
RecordButton *btn;
void paintEvent(QPaintEvent*) override; void paintEvent(QPaintEvent*) override;
private slots: private slots:
void updateState(const UIState &s); void updateState(const UIState &s);
void offroadTransition(bool onroad); void offroadTransition(bool onroad);

View File

@@ -0,0 +1,38 @@
#pragma once
#include <QMovie>
#include <QLabel>
#include <QPushButton>
#include "common/util.h"
#include "selfdrive/ui/ui.h"
class RecordButton : public QPushButton {
Q_OBJECT
public:
RecordButton(QWidget* parent = 0);
private:
void paintEvent(QPaintEvent*) override;
};
class BodyWindow : public QWidget {
Q_OBJECT
public:
BodyWindow(QWidget* parent = 0);
private:
bool charging = false;
uint64_t last_button = 0;
FirstOrderFilter fuel_filter;
QLabel *face;
QMovie *awake, *sleep;
RecordButton *btn;
void paintEvent(QPaintEvent*) override;
private slots:
void updateState(const UIState &s);
void offroadTransition(bool onroad);
};

161
selfdrive/ui/qt/body.org Normal file
View File

@@ -0,0 +1,161 @@
#include "selfdrive/ui/qt/body.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QStackedLayout>
#include "common/params.h"
#include "common/timing.h"
RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) {
setCheckable(true);
setChecked(false);
setFixedSize(148, 148);
QObject::connect(this, &QPushButton::toggled, [=]() {
setEnabled(false);
});
}
void RecordButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPoint center(width() / 2, height() / 2);
QColor bg(isChecked() ? "#FFFFFF" : "#737373");
QColor accent(isChecked() ? "#FF0000" : "#FFFFFF");
if (!isEnabled()) {
bg = QColor("#404040");
accent = QColor("#FFFFFF");
}
if (isDown()) {
accent.setAlphaF(0.7);
}
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(center, 74, 74);
p.setPen(QPen(accent, 6));
p.setBrush(Qt::NoBrush);
p.drawEllipse(center, 42, 42);
p.setPen(Qt::NoPen);
p.setBrush(accent);
p.drawEllipse(center, 22, 22);
}
BodyWindow::BodyWindow(QWidget *parent) : fuel_filter(1.0, 5., 1. / UI_FREQ), QWidget(parent) {
QStackedLayout *layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
QWidget *w = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout(w);
vlayout->setMargin(45);
layout->addWidget(w);
// face
face = new QLabel();
face->setAlignment(Qt::AlignCenter);
layout->addWidget(face);
awake = new QMovie("../assets/body/awake.gif", {}, this);
awake->setCacheMode(QMovie::CacheAll);
sleep = new QMovie("../assets/body/sleep.gif", {}, this);
sleep->setCacheMode(QMovie::CacheAll);
// record button
btn = new RecordButton(this);
vlayout->addWidget(btn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(btn, &QPushButton::clicked, [=](bool checked) {
btn->setEnabled(false);
Params().putBool("DisableLogging", !checked);
last_button = nanos_since_boot();
});
w->raise();
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
}
void BodyWindow::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.fillRect(rect(), QColor(0, 0, 0));
// battery outline + detail
p.translate(width() - 136, 16);
const QColor gray = QColor("#737373");
p.setBrush(Qt::NoBrush);
p.setPen(QPen(gray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
p.drawRoundedRect(2, 2, 78, 36, 8, 8);
p.setPen(Qt::NoPen);
p.setBrush(gray);
p.drawRoundedRect(84, 12, 6, 16, 4, 4);
p.drawRect(84, 12, 3, 16);
// battery level
double fuel = std::clamp(fuel_filter.x(), 0.2f, 1.0f);
const int m = 5; // manual margin since we can't do an inner border
p.setPen(Qt::NoPen);
p.setBrush(fuel > 0.25 ? QColor("#32D74B") : QColor("#FF453A"));
p.drawRoundedRect(2 + m, 2 + m, (78 - 2*m)*fuel, 36 - 2*m, 4, 4);
// charging status
if (charging) {
p.setPen(Qt::NoPen);
p.setBrush(Qt::white);
const QPolygonF charger({
QPointF(12.31, 0),
QPointF(12.31, 16.92),
QPointF(18.46, 16.92),
QPointF(6.15, 40),
QPointF(6.15, 23.08),
QPointF(0, 23.08),
});
p.drawPolygon(charger.translated(98, 0));
}
}
void BodyWindow::offroadTransition(bool offroad) {
btn->setChecked(true);
btn->setEnabled(true);
fuel_filter.reset(1.0);
}
void BodyWindow::updateState(const UIState &s) {
if (!isVisible()) {
return;
}
const SubMaster &sm = *(s.sm);
auto cs = sm["carState"].getCarState();
charging = cs.getCharging();
fuel_filter.update(cs.getFuelGauge());
// TODO: use carState.standstill when that's fixed
const bool standstill = std::abs(cs.getVEgo()) < 0.01;
QMovie *m = standstill ? sleep : awake;
if (m != face->movie()) {
face->setMovie(m);
face->movie()->start();
}
// update record button state
if (sm.updated("managerState") && (sm.rcv_time("managerState") - last_button)*1e-9 > 0.5) {
for (auto proc : sm["managerState"].getManagerState().getProcesses()) {
if (proc.getName() == "loggerd") {
btn->setEnabled(true);
btn->setChecked(proc.getRunning());
}
}
}
update();
}

View File

@@ -0,0 +1,52 @@
#include "selfdrive/ui/qt/body.h"
#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QStackedLayout>
#include <QApplication>
#include <QGridLayout>
#include <QString>
#include <QTransform>
#include <QPixmap>
#include <QWebEngineView> // Include the QWebEngineView header
#include "common/params.h"
#include "common/timing.h"
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/util.h"
void LogoWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
}
BodyWindow::BodyWindow(QWidget *parent) : QWidget(parent) {
// Create a QWebEngineView
QWebEngineView *view = new QWebEngineView(this);
view->setUrl(QUrl("http://www.fark.com/")); // Set the URL to fark.com
// Filler
QGridLayout *layout = new QGridLayout(this);
layout->setSpacing(0);
layout->setMargin(0); // Set margin to 0 to fill the entire window
layout->addWidget(view, 0, 0); // Add the view to the layout
setAttribute(Qt::WA_OpaquePaintEvent);
setStyleSheet(R"(
BodyWindow {
background-color: blue;
}
)");
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
}
void BodyWindow::updateState(const UIState &s) {
}
void BodyWindow::offroadTransition(bool offroad) {
}

View File

@@ -45,7 +45,7 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
}); });
slayout->addWidget(driver_view); slayout->addWidget(driver_view);
setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_NoSystemBackground);
QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState); // QObject::connect(uiState(), &UIState::uiUpdate, this, &HomeWindow::updateState);
QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition); QObject::connect(uiState(), &UIState::offroadTransition, this, &HomeWindow::offroadTransition);
QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition); QObject::connect(uiState(), &UIState::offroadTransition, sidebar, &Sidebar::offroadTransition);
} }
@@ -70,9 +70,9 @@ void HomeWindow::updateState(const UIState &s) {
void HomeWindow::offroadTransition(bool offroad) { void HomeWindow::offroadTransition(bool offroad) {
body->setEnabled(false); body->setEnabled(false);
sidebar->setVisible(offroad); sidebar->setVisible(false);
if (offroad) { if (offroad) {
slayout->setCurrentWidget(home); slayout->setCurrentWidget(body);
} else { } else {
slayout->setCurrentWidget(onroad); slayout->setCurrentWidget(onroad);
} }
@@ -83,15 +83,20 @@ void HomeWindow::showDriverView(bool show) {
emit closeSettings(); emit closeSettings();
slayout->setCurrentWidget(driver_view); slayout->setCurrentWidget(driver_view);
} else { } else {
slayout->setCurrentWidget(home); slayout->setCurrentWidget(body);
} }
sidebar->setVisible(show == false); sidebar->setVisible(false);
} }
void HomeWindow::mousePressEvent(QMouseEvent* e) { void HomeWindow::mousePressEvent(QMouseEvent* e) {
if (body->isVisible()) {
showSidebar(true);
slayout->setCurrentWidget(home);
} else {
// Handle sidebar collapsing // Handle sidebar collapsing
if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) { if ((onroad->isVisible() || body->isVisible()) && (!sidebar->isVisible() || e->x() > sidebar->width())) {
sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible()); sidebar->setVisible(!sidebar->isVisible() && !onroad->isMapVisible());
}
} }
} }

View File

@@ -183,8 +183,8 @@ void DeclinePage::showEvent(QShowEvent *event) {
void OnboardingWindow::updateActiveScreen() { void OnboardingWindow::updateActiveScreen() {
if (!accepted_terms) { if (!accepted_terms) {
setCurrentIndex(0); setCurrentIndex(0);
} else if (!training_done && !params.getBool("Passive")) { // } else if (!training_done && !params.getBool("Passive")) {
setCurrentIndex(1); // setCurrentIndex(1);
} else { } else {
emit onboardingDone(); emit onboardingDone();
} }
@@ -230,5 +230,11 @@ OnboardingWindow::OnboardingWindow(QWidget *parent) : QStackedWidget(parent) {
background-color: #4F4F4F; background-color: #4F4F4F;
} }
)"); )");
// # Oscar sez
Params().put("HasAcceptedTerms", current_terms_version);
Params().put("CompletedTrainingVersion", current_training_version);
accepted_terms = true;
emit onboardingDone();
updateActiveScreen(); updateActiveScreen();
} }

View File

@@ -435,9 +435,9 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription); QObject::connect(this, &SettingsWindow::expandToggleDescription, toggles, &TogglesPanel::expandToggleDescription);
QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric); QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric);
FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this); // FrogPilotControlsPanel *frogpilotControls = new FrogPilotControlsPanel(this);
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;}); // QObject::connect(frogpilotControls, &FrogPilotControlsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;}); // QObject::connect(frogpilotControls, &FrogPilotControlsPanel::openParentToggle, this, [this]() {frogPilotTogglesOpen = true;});
FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this); FrogPilotVisualsPanel *frogpilotVisuals = new FrogPilotVisualsPanel(this);
QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;}); QObject::connect(frogpilotVisuals, &FrogPilotVisualsPanel::closeParentToggle, this, [this]() {frogPilotTogglesOpen = false;});
@@ -448,7 +448,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
{tr("Network"), new Networking(this)}, {tr("Network"), new Networking(this)},
{tr("Toggles"), toggles}, {tr("Toggles"), toggles},
{tr("Software"), new SoftwarePanel(this)}, {tr("Software"), new SoftwarePanel(this)},
{tr("Controls"), frogpilotControls}, // {tr("Controls"), frogpilotControls},
{tr("Navigation"), new FrogPilotNavigationPanel(this)}, {tr("Navigation"), new FrogPilotNavigationPanel(this)},
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)}, {tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
{tr("Visuals"), frogpilotVisuals}, {tr("Visuals"), frogpilotVisuals},

View File

@@ -29,20 +29,20 @@ static void drawIcon(QPainter &p, const QPoint &center, const QPixmap &img, cons
p.setOpacity(1.0); p.setOpacity(1.0);
} }
static void drawIconRotate(QPainter &p, const QPoint &center, const QPixmap &img, const QBrush &bg, float opacity, const int angle) { // static void drawIconRotate(QPainter &p, const QPoint &center, const QPixmap &img, const QBrush &bg, float opacity, const int angle) {
p.setRenderHint(QPainter::Antialiasing); // p.setRenderHint(QPainter::Antialiasing);
p.setOpacity(1.0); // bg dictates opacity of ellipse // p.setOpacity(1.0); // bg dictates opacity of ellipse
p.setPen(Qt::NoPen); // p.setPen(Qt::NoPen);
p.setBrush(bg); // p.setBrush(bg);
p.drawEllipse(center, btn_size / 2, btn_size / 2); // p.drawEllipse(center, btn_size / 2, btn_size / 2);
p.save(); // p.save();
p.translate(center); // p.translate(center);
p.rotate(-angle); // p.rotate(-angle);
p.setOpacity(opacity); // p.setOpacity(opacity);
p.drawPixmap(-QPoint(img.width() / 2, img.height() / 2), img); // p.drawPixmap(-QPoint(img.width() / 2, img.height() / 2), img);
p.setOpacity(1.0); // p.setOpacity(1.0);
p.restore(); // p.restore();
} // }
OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->scene) { OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->scene) {
QVBoxLayout *main_layout = new QVBoxLayout(this); QVBoxLayout *main_layout = new QVBoxLayout(this);
@@ -427,24 +427,26 @@ void ExperimentalButton::updateState(const UIState &s, bool leadInfo) {
void ExperimentalButton::paintEvent(QPaintEvent *event) { void ExperimentalButton::paintEvent(QPaintEvent *event) {
QPainter p(this); QPainter p(this);
// Custom steering wheel icon // Steering wheel icon disabled
engage_img = wheelImages[wheelIcon];
QPixmap img = wheelIcon ? engage_img : (experimental_mode ? experimental_img : engage_img);
QColor background_color = wheelIcon && !isDown() && engageable ? // // Custom steering wheel icon
(scene.always_on_lateral_active ? QColor(10, 186, 181, 255) : // engage_img = wheelImages[wheelIcon];
(scene.conditional_status == 1 ? QColor(255, 246, 0, 255) : // QPixmap img = wheelIcon ? engage_img : (experimental_mode ? experimental_img : engage_img);
(experimental_mode ? QColor(218, 111, 37, 241) :
(scene.navigate_on_openpilot ? QColor(49, 161, 238, 255) : QColor(0, 0, 0, 166))))) :
QColor(0, 0, 0, 166);
if (!scene.show_driver_camera) { // QColor background_color = wheelIcon && !isDown() && engageable ?
if (rotatingWheel || firefoxRandomEventTriggered) { // (scene.always_on_lateral_active ? QColor(10, 186, 181, 255) :
drawIconRotate(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || (!engageable && !scene.always_on_lateral_active)) ? 0.6 : 1.0, steeringAngleDeg); // (scene.conditional_status == 1 ? QColor(255, 246, 0, 255) :
} else { // (experimental_mode ? QColor(218, 111, 37, 241) :
drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || (!engageable && !scene.always_on_lateral_active)) ? 0.6 : 1.0); // (scene.navigate_on_openpilot ? QColor(49, 161, 238, 255) : QColor(0, 0, 0, 166))))) :
} // QColor(0, 0, 0, 166);
}
// if (!scene.show_driver_camera) {
// if (rotatingWheel || firefoxRandomEventTriggered) {
// drawIconRotate(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || (!engageable && !scene.always_on_lateral_active)) ? 0.6 : 1.0, steeringAngleDeg);
// } else {
// drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || (!engageable && !scene.always_on_lateral_active)) ? 0.6 : 1.0);
// }
// }
} }
@@ -475,11 +477,12 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par
// Neokii screen recorder // Neokii screen recorder
QHBoxLayout *top_right_layout = new QHBoxLayout(); QHBoxLayout *top_right_layout = new QHBoxLayout();
top_right_layout->setSpacing(0); top_right_layout->setSpacing(0);
recorder_btn = new ScreenRecorder(this);
top_right_layout->addWidget(recorder_btn);
experimental_btn = new ExperimentalButton(this); // recorder_btn = new ScreenRecorder(this);
top_right_layout->addWidget(experimental_btn); // top_right_layout->addWidget(recorder_btn);
// experimental_btn = new ExperimentalButton(this);
// top_right_layout->addWidget(experimental_btn);
main_layout->addLayout(top_right_layout, 0); main_layout->addLayout(top_right_layout, 0);
main_layout->setAlignment(top_right_layout, Qt::AlignTop | Qt::AlignRight); main_layout->setAlignment(top_right_layout, Qt::AlignTop | Qt::AlignRight);
@@ -1592,32 +1595,35 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
p.setOpacity(1.0); p.setOpacity(1.0);
p.drawRoundedRect(statusBarRect, 30, 30); p.drawRoundedRect(statusBarRect, 30, 30);
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString(); QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
// // QString roadName = QString::fromStdString(paramsMemory.get("oscar_debug"));
QMap<int, QString> conditionalStatusMap = { // QMap<int, QString> conditionalStatusMap = {
{0, "Conditional Experimental Mode ready"}, // {0, "Conditional Experimental Mode ready"},
{1, "Conditional Experimental overridden"}, // {1, "Conditional Experimental overridden"},
{2, "Experimental Mode manually activated"}, // {2, "Experimental Mode manually activated"},
{3, "Conditional Experimental overridden"}, // {3, "Conditional Experimental overridden"},
{4, "Experimental Mode manually activated"}, // {4, "Experimental Mode manually activated"},
{5, "Experimental Mode activated for navigation" + (mapOpen ? "" : QString(" instructions input"))}, // {5, "Experimental Mode activated for navigation" + (mapOpen ? "" : QString(" instructions input"))},
{6, "Experimental Mode activated due to" + (mapOpen ? "SLC" : QString(" no speed limit set"))}, // {6, "Experimental Mode activated due to" + (mapOpen ? "SLC" : QString(" no speed limit set"))},
{7, "Experimental Mode activated due to" + (mapOpen ? " speed" : " speed being less than " + QString::number(conditionalSpeedLead) + (is_metric ? " kph" : " mph"))}, // {7, "Experimental Mode activated due to" + (mapOpen ? " speed" : " speed being less than " + QString::number(conditionalSpeedLead) + (is_metric ? " kph" : " mph"))},
{8, "Experimental Mode activated due to" + (mapOpen ? " speed" : " speed being less than " + QString::number(conditionalSpeed) + (is_metric ? " kph" : " mph"))}, // {8, "Experimental Mode activated due to" + (mapOpen ? " speed" : " speed being less than " + QString::number(conditionalSpeed) + (is_metric ? " kph" : " mph"))},
{9, "Experimental Mode activated for slower lead"}, // {9, "Experimental Mode activated for slower lead"},
{10, "Experimental Mode activated for turn" + (mapOpen ? "" : QString(" / lane change"))}, // {10, "Experimental Mode activated for turn" + (mapOpen ? "" : QString(" / lane change"))},
{11, "Experimental Mode activated for curve"}, // {11, "Experimental Mode activated for curve"},
{12, "Experimental Mode activated for stop" + (mapOpen ? "" : QString(" sign / stop light"))}, // {12, "Experimental Mode activated for stop" + (mapOpen ? "" : QString(" sign / stop light"))},
}; // };
QString screenSuffix = ". Double tap the screen to revert"; QString screenSuffix = ". Double tap the screen to revert";
QString wheelSuffix = ". Double press the \"LKAS\" button to revert"; QString wheelSuffix = ". Double press the \"LKAS\" button to revert";
if (alwaysOnLateral) { // if (alwaysOnLateral) {
newStatus = QString("Always On Lateral active") + (mapOpen ? "" : ". Press the \"Cruise Control\" button to disable"); // newStatus = QString("Always On Lateral active") + (mapOpen ? "" : ". Press the \"Cruise Control\" button to disable");
} else if (conditionalExperimental) { // } else if (conditionalExperimental) {
newStatus = conditionalStatusMap.contains(conditionalStatus) && status != STATUS_DISENGAGED ? conditionalStatusMap[conditionalStatus] : conditionalStatusMap[0]; // newStatus = conditionalStatusMap.contains(conditionalStatus) && status != STATUS_DISENGAGED ? conditionalStatusMap[conditionalStatus] : conditionalStatusMap[0];
} // }
newStatus = QString::fromStdString(paramsMemory.get("oscar_debug"));
// Check if status has changed or if the road name is empty // Check if status has changed or if the road name is empty
if (newStatus != lastShownStatus || roadName.isEmpty()) { if (newStatus != lastShownStatus || roadName.isEmpty()) {

View File

@@ -54,7 +54,12 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
background-color: #3049F4; background-color: #3049F4;
} }
)"); )");
QObject::connect(install, &QPushButton::clicked, this, &Updater::installUpdate);
QObject::connect(connect, &QPushButton::clicked, [=]() {
countdownTimer->stop();
setCurrentWidget(wifi);
});
hlayout->addWidget(install); hlayout->addWidget(install);
} }
@@ -114,6 +119,20 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
addWidget(wifi); addWidget(wifi);
addWidget(progress); addWidget(progress);
// Initialize the countdown timer and value
countdownValue = 5; // 5 seconds countdown
countdownTimer = new QTimer(this);
countdownTimer->setInterval(1000); // 1 second interval
// Connect the timer's timeout signal to update the countdown and button text
QObject::connect(countdownTimer, &QTimer::timeout, this, &Updater::updateCountdown);
// Start the countdown
countdownTimer->start();
// Set initial button text
install->setText(tr("Install (5)"));
setStyleSheet(R"( setStyleSheet(R"(
* { * {
color: white; color: white;
@@ -144,6 +163,16 @@ Updater::Updater(const QString &updater_path, const QString &manifest_path, QWid
)"); )");
} }
void Updater::updateCountdown() {
countdownValue--;
if (countdownValue > 0) {
install->setText(tr("Install (%1)").arg(countdownValue));
} else {
countdownTimer->stop();
installUpdate(); // Assuming this is the method that starts the update
}
}
void Updater::installUpdate() { void Updater::installUpdate() {
setCurrentWidget(progress); setCurrentWidget(progress);
QObject::connect(&proc, &QProcess::readyReadStandardOutput, this, &Updater::readProgress); QObject::connect(&proc, &QProcess::readyReadStandardOutput, this, &Updater::readProgress);

View File

@@ -26,4 +26,7 @@ private:
QProgressBar *bar; QProgressBar *bar;
QPushButton *reboot; QPushButton *reboot;
QWidget *prompt, *wifi, *progress; QWidget *prompt, *wifi, *progress;
QTimer *countdownTimer;
int countdownValue;
}; };

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