From 045ece356d897906d077651f4f50cf8e9f6e7210 Mon Sep 17 00:00:00 2001 From: discountchubbs Date: Thu, 9 Jan 2025 16:16:30 -0800 Subject: [PATCH] Chubbspilot ChubbsPilot ChubbsPilot auto merge-resolver Update model_manager.py patch # 2 Port 2025 IONIQ 6 Patch Update long_mpc.py Update interface.py fixing testing bugs Hyundai Sonata 2019 --- .gitignore | 1 + .vscode/settings.json | 68 ++- README.md | 63 +-- build_chubbspilot.sh | 349 +++++++++++++ cereal/car.capnp | 5 + cereal/custom.capnp | 2 +- common/params.cc | 490 +++++++++--------- common/params.h | 7 +- common/params_pyx.pyx | 1 - common/util.cc | 14 + common/util.h | 2 + merge-resolver.sh | 33 ++ opendbc/hyundai_canfd.dbc | 112 ++++ opendbc/hyundai_kia_generic.dbc | 2 + panda/board/main.c | 2 +- panda/board/safety.h | 14 +- panda/board/safety/safety_hyundai.h | 39 ++ panda/board/safety/safety_hyundai_canfd.h | 15 +- panda/board/safety/safety_hyundai_common.h | 15 + panda/board/safety_declarations.h | 2 + panda/python/__init__.py | 1 + panda/tests/safety/test_hyundai_canfd.py | 4 +- selfdrive/car/__init__.py | 20 + selfdrive/car/car_helpers.py | 2 + selfdrive/car/card.py | 47 +- selfdrive/car/fingerprints.py | 2 + selfdrive/car/hyundai/carcontroller.py | 70 ++- selfdrive/car/hyundai/carstate.py | 94 +++- selfdrive/car/hyundai/enable_radar_tracks.py | 47 ++ selfdrive/car/hyundai/fingerprinting.py | 37 ++ selfdrive/car/hyundai/fingerprints.py | 225 +++++++- selfdrive/car/hyundai/hkg_additions.py | 174 +++++++ selfdrive/car/hyundai/hyundaican.py | 57 +- selfdrive/car/hyundai/hyundaicanfd.py | 130 ++++- selfdrive/car/hyundai/interface.py | 161 +++++- selfdrive/car/hyundai/radar_interface.py | 92 +++- selfdrive/car/hyundai/values.py | 125 ++++- selfdrive/car/interfaces.py | 17 +- selfdrive/car/torque_data/override.toml | 3 + selfdrive/car/torque_data/substitute.toml | 9 + selfdrive/car/toyota/carcontroller.py | 38 +- selfdrive/car/toyota/interface.py | 27 +- selfdrive/classic_modeld/classic_modeld.py | 60 +-- selfdrive/controls/controlsd.py | 21 +- selfdrive/controls/lib/drive_helpers.py | 4 +- selfdrive/controls/lib/events.py | 5 + selfdrive/controls/lib/latcontrol_pid.py | 4 +- selfdrive/controls/lib/latcontrol_torque.py | 2 +- selfdrive/controls/lib/longcontrol.py | 27 +- .../lib/longitudinal_mpc_lib/long_mpc.py | 4 +- .../controls/lib/longitudinal_planner.py | 10 +- selfdrive/controls/lib/pid.py | 16 +- .../debug/hyundai_enable_radar_points.py | 4 + .../frogpilot/assets/download_functions.py | 23 +- .../model_metadata/supercombo_metadata_v1.pkl | Bin 0 -> 734 bytes .../model_metadata/supercombo_metadata_v2.pkl | Bin 0 -> 687 bytes .../model_metadata/supercombo_metadata_v3.pkl | Bin 0 -> 708 bytes .../model_metadata/supercombo_metadata_v4.pkl | Bin 0 -> 710 bytes .../model_metadata/supercombo_metadata_v5.pkl | Bin 0 -> 686 bytes .../model_metadata/supercombo_metadata_v6.pkl | Bin 0 -> 594 bytes selfdrive/frogpilot/assets/theme_manager.py | 80 +-- .../assets/toggle_icons/icon_brake.png | Bin 0 -> 1749 bytes .../frogpilot/controls/frogpilot_planner.py | 2 +- .../lib/conditional_experimental_mode.py | 5 +- .../controls/lib/frogpilot_vcruise.py | 2 + .../controls/lib/map_turn_speed_controller.py | 11 +- .../controls/lib/speed_limit_controller.py | 19 +- .../frogpilot/fleetmanager/fleet_manager.py | 16 + selfdrive/frogpilot/fleetmanager/helpers.py | 124 ++--- .../fleetmanager/templates/tools.html | 137 ++++- selfdrive/frogpilot/frogpilot_functions.py | 18 +- selfdrive/frogpilot/frogpilot_process.py | 104 +--- selfdrive/frogpilot/frogpilot_utilities.py | 163 +++++- selfdrive/frogpilot/frogpilot_variables.py | 122 +++-- selfdrive/frogpilot/navigation/mapd.py | 117 +++-- .../frogpilot/navigation/ui/maps_settings.cc | 415 ++++++--------- .../frogpilot/navigation/ui/maps_settings.h | 58 +-- .../navigation/ui/navigation_functions.cc | 28 +- .../navigation/ui/navigation_functions.h | 86 +-- .../navigation/ui/primeless_settings.cc | 229 +++++--- .../navigation/ui/primeless_settings.h | 25 +- .../frogpilot/ui/qt/offroad/data_settings.cc | 75 +-- .../frogpilot/ui/qt/offroad/device_settings.h | 8 +- .../ui/qt/offroad/frogpilot_settings.cc | 33 +- .../ui/qt/offroad/frogpilot_settings.h | 13 +- .../ui/qt/offroad/lateral_settings.cc | 17 +- .../ui/qt/offroad/lateral_settings.h | 25 +- .../ui/qt/offroad/longitudinal_settings.cc | 18 +- .../ui/qt/offroad/longitudinal_settings.h | 25 +- .../frogpilot/ui/qt/offroad/model_settings.cc | 3 +- .../frogpilot/ui/qt/offroad/model_settings.h | 31 +- .../frogpilot/ui/qt/offroad/sounds_settings.h | 12 +- .../frogpilot/ui/qt/offroad/theme_settings.cc | 3 + .../frogpilot/ui/qt/offroad/theme_settings.h | 44 +- .../frogpilot/ui/qt/offroad/utilities.cc | 11 +- selfdrive/frogpilot/ui/qt/offroad/utilities.h | 2 - .../ui/qt/offroad/vehicle_settings.cc | 13 +- .../ui/qt/offroad/vehicle_settings.h | 48 +- .../ui/qt/offroad/visual_settings.cc | 19 +- .../frogpilot/ui/qt/offroad/visual_settings.h | 23 +- selfdrive/locationd/torqued.py | 3 + selfdrive/modeld/fill_model_msg.py | 6 +- selfdrive/modeld/modeld.py | 56 +- selfdrive/modeld/parse_model_outputs.py | 4 +- selfdrive/monitoring/dmonitoringd.py | 7 +- selfdrive/ui/qt/home.cc | 6 +- selfdrive/ui/qt/offroad/settings.cc | 4 +- selfdrive/ui/qt/offroad/settings.h | 4 +- selfdrive/ui/qt/offroad/software_settings.cc | 7 +- selfdrive/ui/qt/onroad/annotated_camera.cc | 23 +- selfdrive/ui/qt/onroad/annotated_camera.h | 3 + selfdrive/ui/qt/onroad/onroad_home.cc | 15 +- selfdrive/ui/qt/sidebar.cc | 2 +- selfdrive/ui/ui.cc | 7 +- selfdrive/ui/ui.h | 4 +- sync_chubbspilot.sh | 349 +++++++++++++ system/loggerd/uploader.py | 8 +- system/manager/process.py | 4 +- system/manager/process_config.py | 7 +- system/sentry.py | 22 +- system/updated/updated.py | 2 +- 121 files changed, 3872 insertions(+), 1653 deletions(-) create mode 100755 build_chubbspilot.sh create mode 100644 merge-resolver.sh mode change 100755 => 100644 selfdrive/car/card.py create mode 100644 selfdrive/car/hyundai/enable_radar_tracks.py create mode 100644 selfdrive/car/hyundai/fingerprinting.py create mode 100644 selfdrive/car/hyundai/hkg_additions.py mode change 100755 => 100644 selfdrive/controls/lib/events.py create mode 100644 selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v1.pkl create mode 100644 selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v2.pkl create mode 100644 selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v3.pkl create mode 100644 selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v4.pkl create mode 100644 selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v5.pkl create mode 100644 selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v6.pkl create mode 100644 selfdrive/frogpilot/assets/toggle_icons/icon_brake.png create mode 100755 sync_chubbspilot.sh mode change 100755 => 100644 system/updated/updated.py diff --git a/.gitignore b/.gitignore index 370335e6a05af2..9f3e97541c7b72 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,4 @@ build/ poetry.toml Pipfile +merge-resolver.sh diff --git a/.vscode/settings.json b/.vscode/settings.json index f0731c362d9a5c..5f146299c5d12f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,5 +23,71 @@ "system/**", "third_party/**", "tools/**", - ] + ], + "files.associations": { + "*.capnp": "cython", + "__locale": "cpp", + "__bit_reference": "cpp", + "__hash_table": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__verbose_abort": "cpp", + "array": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "execution": "cpp", + "memory": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "print": "cpp", + "queue": "cpp", + "ratio": "cpp", + "regex": "cpp", + "set": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "string_view": "cpp", + "tuple": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "variant": "cpp", + "vector": "cpp", + "algorithm": "cpp" + } } diff --git a/README.md b/README.md index 3364f2de0f7b68..893a067b233c40 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,23 @@ What is openpilot? -What is FrogPilot? 🐸 +What is ChubbsPilot ------ -FrogPilot is a fully open-sourced fork of openpilot, featuring clear and concise commits striving to be a resource for the openpilot developer community. It thrives on contributions from both users and developers, focusing on a collaborative, community-led approach to deliver an advanced openpilot experience for everyone! +ChubbsPilot is an open-sourced fork of openpilot, featuring clear commits striving to be a resource for the openpilot developer community. It thrives on contributions from both users and developers, focusing on a collaborative, community-led approach to deliver an advanced openpilot experience for everyone! ------ -FrogPilot was last updated on: +ChubbsPilot was last updated on: **January 18th, 2025** Features ------ -FrogPilot offers a wide range of customizable features that are easily toggled on or off to suit your preferences. Whether you want a completely stock openpilot experience, or want to add some fun and personal touches, FrogPilot has you covered! Some of the features include: +ChubbsPilot offers a wide range of customizable features that are easily toggled on or off to suit your preferences. Whether you want a completely stock openpilot experience, or want to add some fun and personal touches, ChubbsPilot has you covered! Some of the features include: ------ -⚡ **Advanced Customizations:** + **Advanced Customizations:** - "Alert Volume Controller" to set the volume level for each of of openpilot's sounds - Customize the following distance and jerk values for each personality profile @@ -38,17 +38,15 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - Increase the max set speed by a custom interval (i.e. 2, 3, 4, 5, 6, etc. instead of just 1) - Select between past, present, and future openpilot driving models ------ -🎨 **Custom Themes:** + **Custom Themes:** - 🐸 Frog theme (with a bonus 🐐 sound effect) - Russia / Joseph Stalin theme - 🔌 Tesla theme - Holiday themes! Minor holidays last a day, while major holidays (Easter, Halloween, Thanksgiving, Christmas) last a week - Random events triggered by specific actions while driving with openpilot - - - 📢 Want to add a theme? Request one in the "feature-request" channel in the FrogPilot Discord! ------ -🚀 **Conditional Experimental Mode:** + **Conditional Experimental Mode:** - Auto-activates "Experimental Mode" under several conditions, including: - Approaching intersections and turns while using navigation @@ -57,18 +55,18 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - Driving below a set speed - Turn signal activation below 55mph for turn assistance ------ -📊 **Developer UI:** + **Developer UI:** - Display various driving logics such as the distance, speed, and the desired following distance to your lead vehicle - Lane measuring of the adjacent lanes for lane detection - Tap the "VEHICLE ONLINE"/"CPU"/"GPU" gauge to toggle between CPU and GPU monitoring - Tap the "CONNECT ONLINE"/"MEMORY"/"LEFT"/"USED" gauge to toggle between RAM and storage monitoring ------ -🛠 **Device Management:** +**Device Management:** - Adjustable screen brightness for both onroad and offroad states - Adjustable screen timeout times for both onroad and offroad states - - Backup and restore previous versions of FrogPilot + - Backup and restore previous versions of ChubbsPilot - Backup and restore previous versions of toggle configurations - Battery level threshold to automatically shut the device down after you car's battery falls below a set voltage limit when offroad - Delete stored driving data for increased privacy/space via the "Device" panel @@ -79,9 +77,9 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - "Standby Mode" that wakes the screen up between engagement states or when important alerts are triggered - Timer to automatically shut down after going offroad ------ -🚖 **Lateral Adjustments:** + **Lateral Adjustments:** - - Activate lateral control by simply pressing the "Cruise Control" button + - Activate lateral control by simply pressing the "Cruise Control" button, or LKAS button. - Force comma's auto tuning for unsupported vehicles - Lateral control won't disengage on gas or brake - Nudgeless lane changes with lane detection to prevent driving into curbs or going offroad @@ -91,7 +89,7 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - Precise turns by using turn desires when below the minimum lane change speed - [Twilsonco's NNFF](https://github.com/twilsonco/openpilot) for smoother steering control ------ -🚘 **Longitudinal Adjustments:** + **Longitudinal Adjustments:** - Aggressive acceleration when following a lead vehicle from a stop - "Map Turn Speed Controller" to slow down for curves based on stored map data @@ -104,8 +102,9 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - Tweak the lead detection threshold to detect leads sooner for smoother braking on stopped/slower vehicles - "Vision Turn Speed Controller" for smoother handling of curves - With additional toggles to fine tune the speed aggressiveness and curve detection sensitivity + - "Chubbs Tuning" HKG specific longitudinal tune, featuring over 500 lines of code to significantly improve braking experience with ChubbsPilot. ------ -🗺️ **Navigation:** + **Navigation:** - 3D buildings - Custom map styles @@ -115,9 +114,7 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - Offline maps - "Open Street Maps" integration for speed limit control and road name view ------ -🎮 **Onroad UI:** - - - Compass that rotates according to the direction you're driving + **Onroad UI:** - FPS counter in the screen's border - Hide various UI elements on the screen for a cleaner UI - Pedals on the onroad UI indicate when the gas/brake pedals are being used @@ -138,7 +135,7 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - 📢 Request your own steering wheel icon in the "feature-request" channel! - Steering wheel in the onroad UI rotates alongside your physical steering wheel ------ -🚙 **Vehicle Specific Additions:** + **Vehicle Specific Additions:** - Automatic/manual fingerprint selection to force a selected fingerprint - Custom longitudinal tunings for GM EVs and trucks for smoother gas and brake control @@ -153,7 +150,7 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - "Stop and Go" hack for Toyota's without stop and go functionality - ZSS support for the Toyota Prius and Sienna ------ -🚦 **Quality of Life Features:** + **Quality of Life Features:** - Automatic updates for a completely "set and forget" experience - Camera view selection @@ -165,34 +162,17 @@ FrogPilot offers a wide range of customizable features that are easily toggled o - Retain tethering status between reboots - Screenrecorder - Toggle "Experimental Mode" via the "Lane Departure Alert" button, holding down the "Distance" button for 0.5+ seconds, or by double tapping the screen + - Toggle "Brake Signal" to turn the current speed value to red when the vehicles brake lights are on. How to Install ------ -Easiest way to install FrogPilot is via this URL at the installation screen: +Easiest way to install ChubbsPilot is via this URL at the installation screen: ``` -frogpilot.download +Installer.comma.ai/discountchubbs/FrogPilot ``` -DO NOT install the "FrogPilot-Development" branch. I'm constantly breaking things on there, so unless you don't want to use openpilot, NEVER install it! - -![](https://i.imgur.com/swr0kqJ.png) - -Bug reports / Feature Requests ------- - -If you encounter any issues or bugs while using FrogPilot, or if you have any suggestions for new features or improvements, please don't hesitate to post about it on the Discord! I'm always looking for ways to improve the fork and provide a better experience for everyone! - -To report a bug or request a new feature, make a post in the #bug-reports or #feature-requests channel respectively on the FrogPilot Discord. Please provide as much detail as possible about the issue you're experiencing or the feature you'd like to see added. Photos, videos, log files, or other relevant information are very helpful! - -I will do my best to respond to bug reports and feature requests in a timely manner, but please understand that I may not be able to address every request immediately. Your feedback and suggestions are valuable, and I appreciate your help in making FrogPilot the best it can be! - -Discord ------- - -[Join the FrogPilot Community Discord!](https://discord.gg/frogpilot) - Credits ------ @@ -208,6 +188,7 @@ Credits * [Pfeiferj](https://github.com/pfeiferj) * [ServerDummy](https://github.com/ServerDummy) * [Twilsonco](https://github.com/twilsonco) +* [FrogsGoMoo](https://github.com/frogai) Licensing ------ diff --git a/build_chubbspilot.sh b/build_chubbspilot.sh new file mode 100755 index 00000000000000..430bb683b10454 --- /dev/null +++ b/build_chubbspilot.sh @@ -0,0 +1,349 @@ +#!/bin/bash + +set -x + +# Function to determine the local network range +get_network_range() { + local ip + ip=$(ipconfig getifaddr en0 2>/dev/null || ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1 | head -n1) + if [[ -z "$ip" ]]; then + echo "ERROR: Unable to determine local IP address." + exit 1 + fi + # Convert IP to subnet (e.g., 192.168.1.0/24) + local subnet + subnet=$(echo "$ip" | awk -F. '{print $1 "." $2 "." $3 ".0/24"}') + echo "$subnet" +} + +# Function to detect device IP using nmap +get_device_ip_nmap() { + local network_range + network_range=$(get_network_range) + echo "Scanning network range: $network_range" + # Scan for devices with SSH ports 22 or 8022 open + nmap -p 22,8022 --open -oG - "$network_range" | awk '/22\/open|8022\/open/{print $2}' +} + +# Automatically detect DEVICE_IP +detect_device_ip() { + local possible_ips + possible_ips=$(get_device_ip_nmap) + local device_ip="" + for ip in $possible_ips; do + if ssh -o ConnectTimeout=2 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$ip" 'exit' 2>/dev/null; then + device_ip="$ip" + break + fi + done + + if [[ -z "$device_ip" ]]; then + echo "ERROR: Failed to detect device IP via nmap." + exit 1 + fi + + echo "$device_ip" +} + +# Configuration +DEVICE_USER="comma" +DEVICE_IP=$(detect_device_ip) + +# Remove existing SSH keys for DEVICE_IP +ssh-keygen -R "$DEVICE_IP" > /dev/null 2>&1 + +echo "Detected Device IP: $DEVICE_IP" + +# Function to get the current git branch on the SSH device +get_device_branch() { + ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'cd /data/openpilot && git rev-parse --abbrev-ref HEAD' +} + +# Function to check if prebuilt exists +check_prebuilt() { + # Check for the 'prebuilt' file + PREBUILT_FILE=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'test -f /data/openpilot/prebuilt && echo "yes" || echo "no"') + + # Additionally, check if any .so files exist as an indicator + ESSENTIAL_SO=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'find /data/openpilot -type f -name "*.so" | grep -q . && echo "yes" || echo "no"') + + if [[ "$PREBUILT_FILE" == "yes" || "$ESSENTIAL_SO" == "yes" ]]; then + echo "yes" + else + echo "no" + fi +} + +# Function to perform compilation +compile_device() { + echo "Performing device compilation..." + + echo "Connecting to device at $DEVICE_IP..." + OUTPUT=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" << 'ENDSSH' + set +x # Disable debug mode for cleaner output + set -e + cd /data/openpilot + + # Setup environment + export PATH="/data/data/com.termux/files/usr/bin:$PATH" + export LD_LIBRARY_PATH="/usr/lib/aarch64-linux-gnu:/usr/local/lib:$LD_LIBRARY_PATH" + + # Clean and compile + echo "Cleaning previous compilation..." + if ! scons -c; then + echo "ERROR: Clean failed" + exit 1 + fi + + echo "Starting compilation..." + if ! scons -j$(nproc); then + echo "ERROR: Compilation failed" + exit 1 + fi + + # Wait for compilation to complete + while pgrep -f "scons" > /dev/null; do + echo "Waiting for compilation to finish..." + sleep 5 + done + + # Final check if compilation succeeded + if ! scons --check; then + echo "ERROR: Compilation failed during final check" + exit 1 + fi + + echo "Compilation completed successfully" + + # Add compiled files + git add -f . + git commit -m "compile" --allow-empty + + # Delete all .cc files except specified ones + find . -type f -name '*.cc' ! \( -name 'main.cc' -o -name 'map_renderer.cc' -o -name 'transform.cc' \) -delete + + # Delete all .h files except specified ones + find . -type f -name '*.h' ! \( -name 'sensor.h' -o -name 'version.h' -o -name 'map_renderer.h' -o -name 'ox03c10_registers.h' -o -name 'ar0231_cl.h' -o -name 'os04c10_cl.h' -o -name 'ox03c10_cl.h' -o -name 'os04c10_registers.h' -o -name 'ar0231_registers.h' \) -delete + + # Remove other unwanted file types + find . -name '*.o' -delete + find . -name '*.os' -delete + find . -name '*.pyc' -delete + find . -name 'moc_*' -delete + find . -name '__pycache__' -delete + find . -name '*.a' -delete + + # Remove build system artifacts + rm -rf .sconsign.dblite Jenkinsfile release/ + + # Remove generated directories + rm -rf selfdrive/ubloxd/generated cereal/gen/cpp + + # Remove remaining MPC-generated files + rm -rf selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/main_* + rm -rf selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/main_* + + # Additional Deletions + # Remove specific directories except .github/workflows and delete body/ + find .github -mindepth 1 ! -path './.github/workflows*' -delete + rm -rf .vscode .devcontainer debug \ + tools/cabana tools/camerastream tools/car_porting tools/joystick \ + tools/latencylogger tools/lib/tests tools/plotjuggler tools/profiling \ + tools/replay tools/scripts tools/serial tools/sim tools/ssh \ + tools/ubuntu_setup tools/webcam body/ + + # Remove Docker-related files + rm -f Dockerfile* .dockerignore + + # Remove test files and directories + find . -type f -iname '*test*' -delete + find . -type d -iname '*test*' -exec rm -rf {} + + + # Remove all files named LICENSE (case-insensitive) + find . -type f -iname 'license*' -delete + + # Remove teleoprtc files + find . -type f -iname '*teleoprtc*' -delete + + # Remove configuration files + rm -f .clang-tidy .editorconfig + + # Remove debug-related files and directories + find . -type f -iname '*debug*' -delete + find . -type d -iname '*debug*' -exec rm -rf {} + + + touch prebuilt + + # Amend with cleaned state + git add -f . + git commit --amend -m "Prebuilt" --allow-empty + + # Output hash + echo "HASH=$(git rev-parse HEAD)" +ENDSSH + ) + # Check SSH exit status + SSH_STATUS=$? + if [ $SSH_STATUS -ne 0 ]; then + echo "ERROR: Compilation SSH session failed with status $SSH_STATUS" >&2 + echo "Output was:" >&2 + echo "$OUTPUT" >&2 + exit 1 + fi + + # Extract hash + COMMIT_HASH=$(echo "$OUTPUT" | grep "^HASH=" | cut -d= -f2) + + if ! [[ $COMMIT_HASH =~ ^[0-9a-f]{40}$ ]]; then + echo "ERROR: Invalid commit hash: $COMMIT_HASH" >&2 + echo "Full output was:" >&2 + echo "$OUTPUT" >&2 + exit 1 + fi + + echo "$COMMIT_HASH" +} + +# Function to skip compilation and clean +skip_compile_device() { + echo "Proceeding without recompilation..." >&2 # Redirect to stderr + # Disable set -x for this function to prevent debug logs from polluting OUTPUT + set +x + OUTPUT=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" << 'ENDSSH' + set +x # Disable debug mode + set -e + cd /data/openpilot + + # Add compiled files + git add -f . + git commit -m "compile" --allow-empty + + # Delete all .cc files except specified ones + find . -type f -name '*.cc' ! \( -name 'main.cc' -o -name 'map_renderer.cc' -o -name 'transform.cc' \) -delete + + # Delete all .h files except specified ones + find . -type f -name '*.h' ! \( -name 'sensor.h' -o -name 'version.h' -o -name 'map_renderer.h' -o -name 'ox03c10_registers.h' -o -name 'ar0231_cl.h' -o -name 'os04c10_cl.h' -o -name 'ox03c10_cl.h' -o -name 'os04c10_registers.h' -o -name 'ar0231_registers.h' \) -delete + + # Remove other unwanted file types + find . -name '*.o' -delete + find . -name '*.os' -delete + find . -name '*.pyc' -delete + find . -name 'moc_*' -delete + find . -name '__pycache__' -delete + find . -name '*.a' -delete + + # Remove build system artifacts + rm -rf .sconsign.dblite Jenkinsfile release/ + + # Remove generated directories + rm -rf selfdrive/ubloxd/generated cereal/gen/cpp + + # Remove remaining MPC-generated files + rm -rf selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/main_* + rm -rf selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/main_* + + # Additional Deletions + # Remove specific directories except .github/workflows and delete body/ + find .github -mindepth 1 ! -path './.github/workflows*' -delete + rm -rf .vscode .devcontainer debug \ + tools/cabana tools/camerastream tools/car_porting tools/joystick \ + tools/latencylogger tools/lib/tests tools/plotjuggler tools/profiling \ + tools/replay tools/scripts tools/serial tools/sim tools/ssh \ + tools/ubuntu_setup tools/webcam body/ + + # Remove Docker-related files + rm -f Dockerfile* .dockerignore + + # Remove test files and directories + find . -type f -iname '*test*' -delete + find . -type d -iname '*test*' -exec rm -rf {} + + + # Remove all files named LICENSE (case-insensitive) + find . -type f -iname 'license*' -delete + + # Remove teleoprtc files + find . -type f -iname '*teleoprtc*' -delete + + # Remove configuration files + rm -f .clang-tidy .editorconfig + + # Remove debug-related files and directories + find . -type f -iname '*debug*' -delete + find . -type d -iname '*debug*' -exec rm -rf {} + + + touch prebuilt + + # Amend with cleaned state + git add -f . + git commit --amend -m "Prebuilt" --allow-empty + + # Output hash + echo "HASH=$(git rev-parse HEAD)" +ENDSSH + ) + set -x # Re-enable debug mode + + SSH_STATUS=$? + if [ $SSH_STATUS -ne 0 ]; then + echo "ERROR: Commit and clean SSH session failed with status $SSH_STATUS" >&2 + echo "Output was:" >&2 + echo "$OUTPUT" >&2 + exit 1 + fi + + # Extract hash + COMMIT_HASH=$(echo "$OUTPUT" | grep "^HASH=" | cut -d= -f2) + + if ! [[ $COMMIT_HASH =~ ^[0-9a-f]{40}$ ]]; then + echo "ERROR: Invalid commit hash: $COMMIT_HASH" >&2 + echo "Full output was:" >&2 + echo "$OUTPUT" >&2 + exit 1 + fi + + echo "$COMMIT_HASH" +} + +# Function to handle workspace operations +workspace_operations() { + local device_commit=$1 + echo "=== Starting workspace operations ===" + export GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" + + if ! git remote | grep -q "device"; then + git remote add device ssh://"${DEVICE_USER}"@"${DEVICE_IP}":/data/openpilot + fi + + git fetch device "$BRANCH_NAME" --force + + git checkout "$BRANCH_NAME" || { echo "ERROR: Failed to checkout branch '$BRANCH_NAME'"; exit 1; } + git cherry-pick --no-commit --strategy=recursive -X theirs "$device_commit" || { echo "ERROR: Cherry-pick failed"; exit 1; } + git add -f . + git commit -C "$device_commit" || { echo "ERROR: Commit failed"; exit 1; } + + # Force push to origin + git push --force origin "$BRANCH_NAME" || { echo "ERROR: Force push to origin failed"; exit 1; } +} + +# Main execution +echo "Starting automated sync process..." + +PREBUILT=$(check_prebuilt) + +if [[ "$PREBUILT" == "yes" ]]; then + # Automatically skip recompilation without user confirmation + COMMIT_HASH=$(skip_compile_device) || exit 1 +else + # Perform compilation if not prebuilt + COMMIT_HASH=$(compile_device) || exit 1 +fi + +# Set BRANCH_NAME based on the device's current branch +BRANCH_NAME=$(get_device_branch) +if [[ -z "$BRANCH_NAME" ]]; then + echo "ERROR: Failed to retrieve the device's current branch." + exit 1 +fi + +workspace_operations "$COMMIT_HASH" +echo "Sync process completed." diff --git a/cereal/car.capnp b/cereal/car.capnp index 9f35053388ef45..f3d17c15e0070b 100644 --- a/cereal/car.capnp +++ b/cereal/car.capnp @@ -117,6 +117,7 @@ struct CarEvent @0x9b1657f34caf3ad3 { paramsdTemporaryError @50; paramsdPermanentError @119; actuatorsApiUnavailable @120; + hyundaiRadarTracksAvailable @151; # Use ID 151 instead of 142 # FrogPilot Events accel30 @122; @@ -198,6 +199,7 @@ struct CarState { yawRate @22 :Float32; # best estimate of yaw rate standstill @18 :Bool; wheelSpeeds @2 :WheelSpeeds; + vCruiseCluster @51 :Float32; # set speed to display in the UI # gas pedal, 0.0-1.0 gas @3 :Float32; # this is user pedal only @@ -417,6 +419,8 @@ struct CarControl { rightLaneDepart @8: Bool; leftLaneDepart @9: Bool; leadDistanceBars @10: Int8; # 1-3: 1 is closest, 3 is farthest. some ports may utilize 2-4 bars instead + leadDistance @11: Float32; + leadRelSpeed @12: Float32; enum VisualAlert { # these are the choices from the Honda @@ -491,6 +495,7 @@ struct CarParams { enableDsu @5 :Bool; # driving support unit enableBsm @56 :Bool; # blind spot monitoring flags @64 :UInt32; # flags for car specific quirks + fpFlags @76 :UInt32; # flags for car specific quirks that are fingerprint specific experimentalLongitudinalAvailable @71 :Bool; minEnableSpeed @7 :Float32; diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 26d2c5bc1f7ce3..107aceac78abfb 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -26,7 +26,7 @@ struct FrogPilotCarState @0xaedffd8f31e7b55d { } } - alwaysOnLateralDisabled @0 :Bool; + alwaysOnLateralEnabled @0 :Bool; brakeLights @1 :Bool; dashboardSpeedLimit @2 :Float32; distanceLongPressed @3 :Bool; diff --git a/common/params.cc b/common/params.cc index 199b4cc43471b8..97e913a7b5f342 100644 --- a/common/params.cc +++ b/common/params.cc @@ -89,7 +89,7 @@ class FileLock { std::unordered_map keys = { {"AccessToken", CLEAR_ON_MANAGER_START | DONT_LOG}, - {"AlwaysOnDM", PERSISTENT | FROGPILOT_STORAGE}, + {"AlwaysOnDM", PERSISTENT}, {"ApiCache_Device", PERSISTENT}, {"ApiCache_NavDestinations", PERSISTENT}, {"AssistNowToken", PERSISTENT}, @@ -113,13 +113,13 @@ std::unordered_map keys = { {"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, {"DisablePowerDown", PERSISTENT}, {"DisableUpdates", PERSISTENT}, - {"DisengageOnAccelerator", PERSISTENT | FROGPILOT_STORAGE}, + {"DisengageOnAccelerator", PERSISTENT}, {"DmModelInitialized", CLEAR_ON_ONROAD_TRANSITION}, {"DongleId", PERSISTENT}, {"DoReboot", CLEAR_ON_MANAGER_START}, {"DoShutdown", CLEAR_ON_MANAGER_START}, {"DoUninstall", CLEAR_ON_MANAGER_START}, - {"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY | FROGPILOT_STORAGE}, + {"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY}, {"ExperimentalMode", PERSISTENT}, {"ExperimentalModeConfirmed", PERSISTENT}, {"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, @@ -132,16 +132,19 @@ std::unordered_map keys = { {"GithubUsername", PERSISTENT}, {"GitRemote", PERSISTENT}, {"GsmApn", PERSISTENT}, - {"GsmMetered", PERSISTENT | FROGPILOT_STORAGE}, - {"GsmRoaming", PERSISTENT | FROGPILOT_STORAGE}, + {"GsmMetered", PERSISTENT}, + {"GsmRoaming", PERSISTENT}, {"HardwareSerial", PERSISTENT}, {"HasAcceptedTerms", PERSISTENT}, + {"HyundaiRadarTracksAvailable", PERSISTENT}, + {"HyundaiRadarTracksAvailableCache", PERSISTENT}, + {"HyundaiRadarTracksAvailablePersistent", PERSISTENT}, {"IMEI", PERSISTENT}, {"InstallDate", PERSISTENT}, {"IsDriverViewEnabled", CLEAR_ON_MANAGER_START}, {"IsEngaged", PERSISTENT}, - {"IsLdwEnabled", PERSISTENT | FROGPILOT_STORAGE}, - {"IsMetric", PERSISTENT | FROGPILOT_STORAGE}, + {"IsLdwEnabled", PERSISTENT}, + {"IsMetric", PERSISTENT}, {"IsOffroad", CLEAR_ON_MANAGER_START}, {"IsOnroad", PERSISTENT}, {"IsRhdDetected", PERSISTENT}, @@ -163,7 +166,7 @@ std::unordered_map keys = { {"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"NavDestinationWaypoints", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"NavPastDestinations", PERSISTENT}, - {"NavSettingLeftSide", PERSISTENT | FROGPILOT_STORAGE}, + {"NavSettingLeftSide", PERSISTENT}, {"NavSettingTime24h", PERSISTENT}, {"NetworkMetered", PERSISTENT}, {"ObdMultiplexingChanged", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, @@ -180,18 +183,18 @@ std::unordered_map keys = { {"Offroad_TemperatureTooHigh", CLEAR_ON_MANAGER_START}, {"Offroad_UnofficialHardware", CLEAR_ON_MANAGER_START}, {"Offroad_UpdateFailed", CLEAR_ON_MANAGER_START}, - {"OpenpilotEnabledToggle", PERSISTENT | FROGPILOT_STORAGE}, + {"OpenpilotEnabledToggle", PERSISTENT}, {"PandaHeartbeatLost", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"PandaSomResetTriggered", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"PandaSignatures", CLEAR_ON_MANAGER_START}, {"PrimeType", PERSISTENT}, - {"RecordFront", PERSISTENT | FROGPILOT_STORAGE}, + {"RecordFront", PERSISTENT}, {"RecordFrontLock", PERSISTENT}, // for the internal fleet {"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, {"RouteCount", PERSISTENT}, {"SecOCKey", PERSISTENT | DONT_LOG}, {"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, - {"SshEnabled", PERSISTENT | FROGPILOT_STORAGE}, + {"SshEnabled", PERSISTENT}, {"TermsVersion", PERSISTENT}, {"Timezone", PERSISTENT}, {"TrainingVersion", PERSISTENT}, @@ -210,86 +213,87 @@ std::unordered_map keys = { {"Version", PERSISTENT}, // FrogPilot parameters - {"AccelerationPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"AccelerationProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"AdjacentLeadsUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"AdjacentPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"AdjacentPathMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"AdvancedCustomUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"AdvancedLateralTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"AccelerationPath", PERSISTENT | FROGPILOT_VISUALS}, + {"AccelerationProfile", PERSISTENT | FROGPILOT_CONTROLS}, + {"AdjacentLeadsUI", PERSISTENT | FROGPILOT_CONTROLS}, + {"AdjacentPath", PERSISTENT | FROGPILOT_VISUALS}, + {"AdjacentPathMetrics", PERSISTENT | FROGPILOT_VISUALS}, + {"AdvancedCustomUI", PERSISTENT | FROGPILOT_VISUALS}, + {"AdvancedLateralTune", PERSISTENT | FROGPILOT_CONTROLS}, {"AggressiveFollow", PERSISTENT | FROGPILOT_CONTROLS}, {"AggressiveJerkAcceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"AggressiveJerkDanger", PERSISTENT | FROGPILOT_CONTROLS}, {"AggressiveJerkDeceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"AggressiveJerkSpeed", PERSISTENT | FROGPILOT_CONTROLS}, {"AggressiveJerkSpeedDecrease", PERSISTENT | FROGPILOT_CONTROLS}, - {"AggressivePersonalityProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"AlertVolumeControl", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"AlwaysOnLateral", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"AlwaysOnLateralLKAS", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"AlwaysOnLateralMain", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"AggressivePersonalityProfile", PERSISTENT | FROGPILOT_CONTROLS}, + {"AlertVolumeControl", PERSISTENT | FROGPILOT_VISUALS}, + {"AlwaysOnLateral", PERSISTENT | FROGPILOT_CONTROLS}, + {"AlwaysOnLateralLKAS", PERSISTENT | FROGPILOT_CONTROLS}, + {"AlwaysOnLateralMain", PERSISTENT | FROGPILOT_CONTROLS}, {"AMapKey1", PERSISTENT}, {"AMapKey2", PERSISTENT}, {"ApiCache_DriveStats", PERSISTENT}, - {"AutomaticallyUpdateModels", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"AutomaticUpdates", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_OTHER}, + {"AutomaticallyUpdateModels", PERSISTENT | FROGPILOT_CONTROLS}, + {"AutomaticUpdates", PERSISTENT | FROGPILOT_OTHER}, {"AvailableModels", PERSISTENT}, {"AvailableModelNames", PERSISTENT}, - {"BigMap", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"BigMap", PERSISTENT | FROGPILOT_VISUALS}, {"BlacklistedModels", PERSISTENT | FROGPILOT_CONTROLS}, - {"BlindSpotMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"BlindSpotPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"BorderMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CameraView", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CancelModelDownload", PERSISTENT}, - {"CancelThemeDownload", PERSISTENT}, + {"BlindSpotMetrics", PERSISTENT | FROGPILOT_VISUALS}, + {"BlindSpotPath", PERSISTENT | FROGPILOT_VISUALS}, + {"BorderMetrics", PERSISTENT | FROGPILOT_VISUALS}, + {"BrakeSignal", PERSISTENT | FROGPILOT_VISUALS}, + {"CameraView", PERSISTENT | FROGPILOT_VISUALS}, + {"CancelModelDownload", CLEAR_ON_MANAGER_START}, + {"CancelThemeDownload", CLEAR_ON_MANAGER_START}, {"CarMake", PERSISTENT}, {"CarModel", PERSISTENT}, {"CarModelName", PERSISTENT}, - {"CECurves", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CECurvesLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CELead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CEModelStopTime", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CENavigation", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CENavigationIntersections", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CENavigationLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CENavigationTurns", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"CECurves", PERSISTENT | FROGPILOT_CONTROLS}, + {"CECurvesLead", PERSISTENT | FROGPILOT_CONTROLS}, + {"CELead", PERSISTENT | FROGPILOT_CONTROLS}, + {"CEModelStopTime", PERSISTENT | FROGPILOT_CONTROLS}, + {"CENavigation", PERSISTENT | FROGPILOT_CONTROLS}, + {"CENavigationIntersections", PERSISTENT | FROGPILOT_CONTROLS}, + {"CENavigationLead", PERSISTENT | FROGPILOT_CONTROLS}, + {"CENavigationTurns", PERSISTENT | FROGPILOT_CONTROLS}, {"CertifiedHerbalistDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"CertifiedHerbalistScore", PERSISTENT | FROGPILOT_CONTROLS}, - {"CESignalSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CESignalLaneDetection", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CESlowerLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CESpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CESpeedLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"CESignalSpeed", PERSISTENT | FROGPILOT_CONTROLS}, + {"CESignalLaneDetection", PERSISTENT | FROGPILOT_CONTROLS}, + {"CESlowerLead", PERSISTENT | FROGPILOT_CONTROLS}, + {"CESpeed", PERSISTENT | FROGPILOT_CONTROLS}, + {"CESpeedLead", PERSISTENT | FROGPILOT_CONTROLS}, {"CEStatus", CLEAR_ON_OFFROAD_TRANSITION}, - {"CEStoppedLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ClusterOffset", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"ColorToDownload", PERSISTENT}, - {"Compass", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ConditionalExperimental", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CrosstrekTorque", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, + {"CEStoppedLead", PERSISTENT | FROGPILOT_CONTROLS}, + {"ClusterOffset", PERSISTENT | FROGPILOT_VEHICLES}, + {"ColorToDownload", CLEAR_ON_MANAGER_START}, + {"Compass", PERSISTENT | FROGPILOT_VISUALS}, + {"ConditionalExperimental", PERSISTENT | FROGPILOT_CONTROLS}, + {"CrosstrekTorque", PERSISTENT | FROGPILOT_VEHICLES}, {"CurveSensitivity", PERSISTENT | FROGPILOT_CONTROLS}, - {"CurveSpeedControl", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CustomAlerts", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CustomColors", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CustomCruise", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CustomCruiseLong", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CustomDistanceIcons", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CustomIcons", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CustomPersonalities", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"CustomSignals", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CustomSounds", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CustomUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"DecelerationProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"DeveloperUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"DeviceManagement", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"DeviceShutdown", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"DisableOnroadUploads", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"DisableOpenpilotLongitudinal", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, + {"CurveSpeedControl", PERSISTENT | FROGPILOT_CONTROLS}, + {"CustomAlerts", PERSISTENT | FROGPILOT_VISUALS}, + {"CustomColors", PERSISTENT | FROGPILOT_VISUALS}, + {"CustomCruise", PERSISTENT | FROGPILOT_CONTROLS}, + {"CustomCruiseLong", PERSISTENT | FROGPILOT_CONTROLS}, + {"CustomDistanceIcons", PERSISTENT | FROGPILOT_CONTROLS}, + {"CustomIcons", PERSISTENT | FROGPILOT_VISUALS}, + {"CustomPersonalities", PERSISTENT | FROGPILOT_CONTROLS}, + {"CustomSignals", PERSISTENT | FROGPILOT_VISUALS}, + {"CustomSounds", PERSISTENT | FROGPILOT_VISUALS}, + {"CustomUI", PERSISTENT | FROGPILOT_VISUALS}, + {"DecelerationProfile", PERSISTENT | FROGPILOT_CONTROLS}, + {"DeveloperUI", PERSISTENT | FROGPILOT_VISUALS}, + {"DeviceManagement", PERSISTENT | FROGPILOT_CONTROLS}, + {"DeviceShutdown", PERSISTENT | FROGPILOT_CONTROLS}, + {"DisableOnroadUploads", PERSISTENT | FROGPILOT_CONTROLS}, + {"DisableOpenpilotLongitudinal", PERSISTENT | FROGPILOT_VEHICLES}, {"DissolvedOxygenDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"DissolvedOxygenScore", PERSISTENT | FROGPILOT_CONTROLS}, - {"DistanceIconToDownload", PERSISTENT}, - {"DisengageVolume", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"DistanceIconToDownload", CLEAR_ON_MANAGER_START}, + {"DisengageVolume", PERSISTENT | FROGPILOT_VISUALS}, {"DoToggleReset", PERSISTENT}, {"DownloadableColors", PERSISTENT}, {"DownloadableDistanceIcons", PERSISTENT}, @@ -297,146 +301,150 @@ std::unordered_map keys = { {"DownloadableSignals", PERSISTENT}, {"DownloadableSounds", PERSISTENT}, {"DownloadableWheels", PERSISTENT}, - {"DownloadAllModels", PERSISTENT}, + {"DownloadAllModels", CLEAR_ON_MANAGER_START}, {"DragonRiderDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"DragonRiderScore", PERSISTENT | FROGPILOT_CONTROLS}, - {"DriverCamera", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"DriverCamera", PERSISTENT | FROGPILOT_VISUALS}, {"DuckAmigoDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"DuckAmigoScore", PERSISTENT | FROGPILOT_CONTROLS}, - {"DynamicPathWidth", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"DynamicPedalsOnUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"DynamicPathWidth", PERSISTENT | FROGPILOT_VISUALS}, + {"DynamicPedalsOnUI", PERSISTENT | FROGPILOT_VISUALS}, {"EngageVolume", PERSISTENT | FROGPILOT_VISUALS}, - {"ExperimentalGMTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"ExperimentalModeActivation", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"ExperimentalGMTune", PERSISTENT | FROGPILOT_VEHICLES}, + {"ExperimentalModeActivation", PERSISTENT | FROGPILOT_CONTROLS}, {"ExperimentalModels", PERSISTENT}, - {"ExperimentalModeViaDistance", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ExperimentalModeViaLKAS", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ExperimentalModeViaTap", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"Fahrenheit", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"ExperimentalModeViaDistance", PERSISTENT | FROGPILOT_CONTROLS}, + {"ExperimentalModeViaLKAS", PERSISTENT | FROGPILOT_CONTROLS}, + {"ExperimentalModeViaTap", PERSISTENT | FROGPILOT_CONTROLS}, + {"Fahrenheit", PERSISTENT | FROGPILOT_VISUALS}, {"FingerprintLogged", CLEAR_ON_MANAGER_START}, - {"ForceAutoTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ForceAutoTuneOff", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ForceFingerprint", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"ForceMPHDashboard", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ForceOffroad", PERSISTENT}, - {"ForceOnroad", PERSISTENT}, - {"ForceStandstill", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ForceStops", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"FPSCounter", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"FlashPanda", CLEAR_ON_MANAGER_START}, + {"ForceAutoTune", PERSISTENT | FROGPILOT_CONTROLS}, + {"ForceAutoTuneOff", PERSISTENT | FROGPILOT_CONTROLS}, + {"ForceFingerprint", PERSISTENT | FROGPILOT_VEHICLES}, + {"ForceMPHDashboard", PERSISTENT | FROGPILOT_CONTROLS}, + {"ForceOffroad", CLEAR_ON_MANAGER_START}, + {"ForceOnroad", CLEAR_ON_MANAGER_START}, + {"ForceStandstill", PERSISTENT | FROGPILOT_CONTROLS}, + {"ForceStops", PERSISTENT | FROGPILOT_CONTROLS}, + {"FPSCounter", PERSISTENT | FROGPILOT_VISUALS}, {"FrogPilotDrives", PERSISTENT | FROGPILOT_TRACKING}, {"FrogPilotKilometers", PERSISTENT | FROGPILOT_TRACKING}, {"FrogPilotMinutes", PERSISTENT | FROGPILOT_TRACKING}, {"FrogPilotToggles", CLEAR_ON_MANAGER_START}, - {"FrogPilotTogglesUpdated", PERSISTENT}, + {"FrogPilotTogglesUpdated", CLEAR_ON_MANAGER_START}, {"FrogPilotTuningLevels", CLEAR_ON_MANAGER_START}, - {"FrogsGoMoosTweak", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"FullMap", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"GasRegenCmd", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, + {"FrogsGoMoosTweak", PERSISTENT | FROGPILOT_VEHICLES}, + {"FullMap", PERSISTENT | FROGPILOT_VISUALS}, + {"GasRegenCmd", PERSISTENT | FROGPILOT_VEHICLES}, {"GMapKey", PERSISTENT}, - {"GoatScream", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"GreenLightAlert", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HideAlerts", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HideCSCUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"HideLeadMarker", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HideMapIcon", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HideMaxSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HideSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HideSpeedLimit", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HolidayThemes", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HumanAcceleration", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"HumanFollowing", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"IconToDownload", PERSISTENT}, - {"IncreasedStoppedDistance", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"IncreaseThermalLimits", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"JerkInfo", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"LaneChangeCustomizations", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"LaneChangeTime", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"LaneDetectionWidth", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"LaneLinesWidth", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"GoatScream", PERSISTENT | FROGPILOT_VISUALS}, + {"GreenLightAlert", PERSISTENT | FROGPILOT_VISUALS}, + {"HideAlerts", PERSISTENT | FROGPILOT_VISUALS}, + {"HideCSCUI", PERSISTENT | FROGPILOT_CONTROLS}, + {"HideLeadMarker", PERSISTENT | FROGPILOT_VISUALS}, + {"HideMapIcon", PERSISTENT | FROGPILOT_VISUALS}, + {"HideMaxSpeed", PERSISTENT | FROGPILOT_VISUALS}, + {"HideSpeed", PERSISTENT | FROGPILOT_VISUALS}, + {"HideSpeedLimit", PERSISTENT | FROGPILOT_VISUALS}, + {"HolidayThemes", PERSISTENT | FROGPILOT_VISUALS}, + {"HumanAcceleration", PERSISTENT | FROGPILOT_CONTROLS}, + {"HumanFollowing", PERSISTENT | FROGPILOT_CONTROLS}, + {"HyundaiRadarTracks", PERSISTENT | FROGPILOT_VEHICLES}, + {"HKGtuning", PERSISTENT | FROGPILOT_VEHICLES}, + {"HatTrick", PERSISTENT | FROGPILOT_VEHICLES}, + {"IconToDownload", CLEAR_ON_MANAGER_START}, + {"IncreasedStoppedDistance", PERSISTENT | FROGPILOT_CONTROLS}, + {"IncreaseThermalLimits", PERSISTENT | FROGPILOT_CONTROLS}, + {"JerkInfo", PERSISTENT | FROGPILOT_VISUALS}, + {"LaneChangeCustomizations", PERSISTENT | FROGPILOT_CONTROLS}, + {"LaneChangeTime", PERSISTENT | FROGPILOT_CONTROLS}, + {"LaneDetectionWidth", PERSISTENT | FROGPILOT_CONTROLS}, + {"LaneLinesWidth", PERSISTENT | FROGPILOT_VISUALS}, {"LastMapsUpdate", PERSISTENT | FROGPILOT_OTHER}, - {"LateralMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"LateralTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"LeadDepartingAlert", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"LateralMetrics", PERSISTENT | FROGPILOT_VISUALS}, + {"LateralTune", PERSISTENT | FROGPILOT_CONTROLS}, + {"LeadDepartingAlert", PERSISTENT | FROGPILOT_VISUALS}, {"LeadDetectionThreshold", PERSISTENT | FROGPILOT_CONTROLS}, - {"LeadInfo", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"LockDoors", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"LockDoorsTimer", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"LongitudinalMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"LongitudinalTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"LongPitch", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, + {"LeadInfo", PERSISTENT | FROGPILOT_VISUALS}, + {"LockDoors", PERSISTENT | FROGPILOT_VEHICLES}, + {"LockDoorsTimer", PERSISTENT | FROGPILOT_VEHICLES}, + {"LongitudinalMetrics", PERSISTENT | FROGPILOT_VISUALS}, + {"LongitudinalTune", PERSISTENT | FROGPILOT_CONTROLS}, + {"LongPitch", PERSISTENT | FROGPILOT_VEHICLES}, {"LosAngelesDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"LosAngelesScore", PERSISTENT | FROGPILOT_CONTROLS}, - {"LoudBlindspotAlert", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"LoudBlindspotAlert", PERSISTENT | FROGPILOT_VISUALS}, {"LowVoltageShutdown", PERSISTENT | FROGPILOT_CONTROLS}, - {"ManualUpdateInitiated", PERSISTENT}, - {"MapAcceleration", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"ManualUpdateInitiated", CLEAR_ON_MANAGER_START}, + {"MapAcceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"MapboxPublicKey", PERSISTENT}, {"MapboxSecretKey", PERSISTENT}, - {"MapDeceleration", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"MapGears", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"MapDeceleration", PERSISTENT | FROGPILOT_CONTROLS}, + {"MapGears", PERSISTENT | FROGPILOT_CONTROLS}, {"MapsSelected", PERSISTENT | FROGPILOT_OTHER}, - {"MapSpeedLimit", PERSISTENT}, - {"MapStyle", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"MapTargetVelocities", PERSISTENT}, + {"MapSpeedLimit", CLEAR_ON_MANAGER_START}, + {"MapStyle", PERSISTENT | FROGPILOT_VISUALS}, + {"MapTargetVelocities", CLEAR_ON_MANAGER_START}, {"MaxDesiredAcceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"MinimumBackupSize", PERSISTENT}, - {"MinimumLaneChangeSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"Model", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"MinimumLaneChangeSpeed", PERSISTENT | FROGPILOT_CONTROLS}, + {"Model", PERSISTENT | FROGPILOT_CONTROLS}, {"ModelDrivesAndScores", PERSISTENT | FROGPILOT_CONTROLS}, - {"ModelDownloadProgress", PERSISTENT}, - {"ModelName", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ModelRandomizer", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ModelToDownload", PERSISTENT}, - {"ModelUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ModelVersion", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ModelVersions", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"MTSCCurvatureCheck", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"MTSCEnabled", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"NavigationUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"NextMapSpeedLimit", PERSISTENT}, - {"NewLongAPI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"NewLongAPIGM", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"NNFF", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"NNFFLite", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"ModelDownloadProgress", CLEAR_ON_MANAGER_START}, + {"ModelName", PERSISTENT | FROGPILOT_CONTROLS}, + {"ModelRandomizer", PERSISTENT | FROGPILOT_CONTROLS}, + {"ModelToDownload", CLEAR_ON_MANAGER_START}, + {"ModelUI", PERSISTENT | FROGPILOT_VISUALS}, + {"ModelVersion", PERSISTENT | FROGPILOT_CONTROLS}, + {"ModelVersions", PERSISTENT | FROGPILOT_CONTROLS}, + {"MTSCCurvatureCheck", PERSISTENT | FROGPILOT_CONTROLS}, + {"MTSCEnabled", PERSISTENT | FROGPILOT_CONTROLS}, + {"NavigationUI", PERSISTENT | FROGPILOT_VISUALS}, + {"NextMapSpeedLimit", CLEAR_ON_MANAGER_START}, + {"NewLongAPI", PERSISTENT | FROGPILOT_VEHICLES}, + {"NewLongAPIGM", PERSISTENT | FROGPILOT_VEHICLES}, + {"NNFF", PERSISTENT | FROGPILOT_CONTROLS}, + {"NNFFLite", PERSISTENT | FROGPILOT_CONTROLS}, {"NNFFModelName", CLEAR_ON_OFFROAD_TRANSITION}, - {"NoLogging", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"NoLogging", PERSISTENT | FROGPILOT_CONTROLS}, {"NorthDakotaDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"NorthDakotaScore", PERSISTENT | FROGPILOT_CONTROLS}, {"NotreDameDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"NotreDameScore", PERSISTENT | FROGPILOT_CONTROLS}, - {"NoUploads", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"NudgelessLaneChange", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"NumericalTemp", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"OfflineMode", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"Offset1", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"Offset2", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"Offset3", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"Offset4", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"OneLaneChange", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"OnroadDistanceButton", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"OnroadDistanceButtonPressed", PERSISTENT}, + {"NoUploads", PERSISTENT | FROGPILOT_CONTROLS}, + {"NudgelessLaneChange", PERSISTENT | FROGPILOT_CONTROLS}, + {"NumericalTemp", PERSISTENT | FROGPILOT_VISUALS}, + {"OfflineMode", PERSISTENT | FROGPILOT_CONTROLS}, + {"Offset1", PERSISTENT | FROGPILOT_CONTROLS}, + {"Offset2", PERSISTENT | FROGPILOT_CONTROLS}, + {"Offset3", PERSISTENT | FROGPILOT_CONTROLS}, + {"Offset4", PERSISTENT | FROGPILOT_CONTROLS}, + {"OneLaneChange", PERSISTENT | FROGPILOT_CONTROLS}, + {"OnroadDistanceButton", PERSISTENT | FROGPILOT_CONTROLS}, + {"OnroadDistanceButtonPressed", CLEAR_ON_MANAGER_START}, {"openpilotMinutes", PERSISTENT}, {"OSMDownloadBounds", PERSISTENT}, {"OSMDownloadLocations", PERSISTENT}, {"OSMDownloadProgress", CLEAR_ON_MANAGER_START}, - {"PathEdgeWidth", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"PathWidth", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"PauseAOLOnBrake", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"PauseLateralOnSignal", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"PauseLateralSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"PedalsOnUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"PersonalizeOpenpilot", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"PreferredSchedule", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_OTHER}, + {"PathEdgeWidth", PERSISTENT | FROGPILOT_VISUALS}, + {"PathWidth", PERSISTENT | FROGPILOT_VISUALS}, + {"PauseAOLOnBrake", PERSISTENT | FROGPILOT_CONTROLS}, + {"PauseLateralOnSignal", PERSISTENT | FROGPILOT_CONTROLS}, + {"PauseLateralSpeed", PERSISTENT | FROGPILOT_CONTROLS}, + {"PedalsOnUI", PERSISTENT | FROGPILOT_VISUALS}, + {"PersonalizeOpenpilot", PERSISTENT | FROGPILOT_VISUALS}, + {"PreferredSchedule", PERSISTENT | FROGPILOT_OTHER}, {"PreviousSpeedLimit", PERSISTENT}, {"PromptDistractedVolume", PERSISTENT | FROGPILOT_VISUALS}, {"PromptVolume", PERSISTENT | FROGPILOT_VISUALS}, - {"QOLLateral", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"QOLLongitudinal", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"QOLVisuals", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"QOLLateral", PERSISTENT | FROGPILOT_CONTROLS}, + {"QOLLongitudinal", PERSISTENT | FROGPILOT_CONTROLS}, + {"QOLVisuals", PERSISTENT | FROGPILOT_VISUALS}, {"RadicalTurtleDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"RadicalTurtleScore", PERSISTENT | FROGPILOT_CONTROLS}, - {"RainbowPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"RandomEvents", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"RainbowPath", PERSISTENT | FROGPILOT_VISUALS}, + {"RandomEvents", PERSISTENT | FROGPILOT_VISUALS}, {"RecertifiedHerbalistDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"RecertifiedHerbalistScore", PERSISTENT | FROGPILOT_CONTROLS}, {"RefuseVolume", PERSISTENT | FROGPILOT_VISUALS}, @@ -446,66 +454,66 @@ std::unordered_map keys = { {"RelaxedJerkDeceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"RelaxedJerkSpeed", PERSISTENT | FROGPILOT_CONTROLS}, {"RelaxedJerkSpeedDecrease", PERSISTENT | FROGPILOT_CONTROLS}, - {"RelaxedPersonalityProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ReverseCruise", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"RoadEdgesWidth", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"RoadName", PERSISTENT}, - {"RoadNameUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"RotatingWheel", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ScreenBrightness", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ScreenBrightnessOnroad", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ScreenManagement", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ScreenRecorder", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ScreenTimeout", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ScreenTimeoutOnroad", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"SearchInput", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_OTHER}, + {"RelaxedPersonalityProfile", PERSISTENT | FROGPILOT_CONTROLS}, + {"ReverseCruise", PERSISTENT | FROGPILOT_CONTROLS}, + {"RoadEdgesWidth", PERSISTENT | FROGPILOT_VISUALS}, + {"RoadName", CLEAR_ON_MANAGER_START}, + {"RoadNameUI", PERSISTENT | FROGPILOT_VISUALS}, + {"RotatingWheel", PERSISTENT | FROGPILOT_VISUALS}, + {"ScreenBrightness", PERSISTENT | FROGPILOT_VISUALS}, + {"ScreenBrightnessOnroad", PERSISTENT | FROGPILOT_VISUALS}, + {"ScreenManagement", PERSISTENT | FROGPILOT_VISUALS}, + {"ScreenRecorder", PERSISTENT | FROGPILOT_VISUALS}, + {"ScreenTimeout", PERSISTENT | FROGPILOT_VISUALS}, + {"ScreenTimeoutOnroad", PERSISTENT | FROGPILOT_VISUALS}, + {"SearchInput", PERSISTENT | FROGPILOT_OTHER}, {"SecretGoodOpenpilotDrives", PERSISTENT | FROGPILOT_CONTROLS}, {"SecretGoodOpenpilotScore", PERSISTENT | FROGPILOT_CONTROLS}, - {"SetSpeedLimit", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SetSpeedOffset", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ShowCEMStatus", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowCPU", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowGPU", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowIP", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowMemoryUsage", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowSLCOffset", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ShowSpeedLimits", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowSteering", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowStoppingPoint", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowStoppingPointMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowStorageLeft", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"ShowStorageUsed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"SetSpeedLimit", PERSISTENT | FROGPILOT_CONTROLS}, + {"SetSpeedOffset", PERSISTENT | FROGPILOT_CONTROLS}, + {"ShowCEMStatus", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowCPU", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowGPU", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowIP", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowMemoryUsage", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowSLCOffset", PERSISTENT | FROGPILOT_CONTROLS}, + {"ShowSpeedLimits", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowSteering", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowStoppingPoint", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowStoppingPointMetrics", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowStorageLeft", PERSISTENT | FROGPILOT_VISUALS}, + {"ShowStorageUsed", PERSISTENT | FROGPILOT_VISUALS}, {"Sidebar", PERSISTENT | FROGPILOT_OTHER}, - {"SidebarMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"SignalMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"SignalToDownload", PERSISTENT}, - {"SLCConfirmation", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCConfirmationHigher", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCConfirmationLower", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCLookaheadHigher", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCLookaheadLower", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCFallback", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCOverride", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCPriority1", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCPriority2", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SLCPriority3", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SNGHack", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, - {"SoundToDownload", PERSISTENT}, - {"SpeedLimitAccepted", PERSISTENT}, - {"SpeedLimitChangedAlert", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SpeedLimitController", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"SpeedLimitSources", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"StandardFollow", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"SidebarMetrics", PERSISTENT | FROGPILOT_VISUALS}, + {"SignalMetrics", PERSISTENT | FROGPILOT_VISUALS}, + {"SignalToDownload", CLEAR_ON_MANAGER_START}, + {"SLCConfirmation", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCConfirmationHigher", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCConfirmationLower", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCLookaheadHigher", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCLookaheadLower", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCFallback", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCOverride", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCPriority1", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCPriority2", PERSISTENT | FROGPILOT_CONTROLS}, + {"SLCPriority3", PERSISTENT | FROGPILOT_CONTROLS}, + {"SNGHack", PERSISTENT | FROGPILOT_VEHICLES}, + {"SoundToDownload", CLEAR_ON_MANAGER_START}, + {"SpeedLimitAccepted", CLEAR_ON_MANAGER_START}, + {"SpeedLimitChangedAlert", PERSISTENT | FROGPILOT_CONTROLS}, + {"SpeedLimitController", PERSISTENT | FROGPILOT_CONTROLS}, + {"SpeedLimitSources", PERSISTENT | FROGPILOT_VISUALS}, + {"StandardFollow", PERSISTENT | FROGPILOT_CONTROLS}, {"StandardJerkAcceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"StandardJerkDanger", PERSISTENT | FROGPILOT_CONTROLS}, {"StandardJerkDeceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"StandardJerkSpeed", PERSISTENT | FROGPILOT_CONTROLS}, {"StandardJerkSpeedDecrease", PERSISTENT | FROGPILOT_CONTROLS}, - {"StandardPersonalityProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"StandbyMode", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"StandardPersonalityProfile", PERSISTENT | FROGPILOT_CONTROLS}, + {"StandbyMode", PERSISTENT | FROGPILOT_VISUALS}, {"StartupMessageBottom", PERSISTENT | FROGPILOT_VISUALS}, {"StartupMessageTop", PERSISTENT | FROGPILOT_VISUALS}, - {"StaticPedalsOnUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"StaticPedalsOnUI", PERSISTENT | FROGPILOT_VISUALS}, {"SteerFriction", PERSISTENT | FROGPILOT_CONTROLS}, {"SteerFrictionStock", PERSISTENT}, {"SteerLatAccel", PERSISTENT | FROGPILOT_CONTROLS}, @@ -514,40 +522,40 @@ std::unordered_map keys = { {"SteerKPStock", PERSISTENT}, {"SteerRatio", PERSISTENT | FROGPILOT_CONTROLS}, {"SteerRatioStock", PERSISTENT}, - {"StoppedTimer", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"TacoTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"TestingSound", PERSISTENT}, - {"TetheringEnabled", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_OTHER}, - {"ThemeDownloadProgress", PERSISTENT}, - {"ToyotaDoors", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, + {"StoppedTimer", PERSISTENT | FROGPILOT_VISUALS}, + {"TacoTune", PERSISTENT | FROGPILOT_CONTROLS}, + {"TestingSound", CLEAR_ON_MANAGER_START}, + {"TetheringEnabled", PERSISTENT | FROGPILOT_OTHER}, + {"ThemeDownloadProgress", CLEAR_ON_MANAGER_START}, + {"ToyotaDoors", PERSISTENT | FROGPILOT_VEHICLES}, {"TrafficFollow", PERSISTENT | FROGPILOT_CONTROLS}, {"TrafficJerkAcceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"TrafficJerkDanger", PERSISTENT | FROGPILOT_CONTROLS}, {"TrafficJerkDeceleration", PERSISTENT | FROGPILOT_CONTROLS}, {"TrafficJerkSpeed", PERSISTENT | FROGPILOT_CONTROLS}, {"TrafficJerkSpeedDecrease", PERSISTENT | FROGPILOT_CONTROLS}, - {"TrafficPersonalityProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"TuningInfo", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"TrafficPersonalityProfile", PERSISTENT | FROGPILOT_CONTROLS}, + {"TuningInfo", PERSISTENT | FROGPILOT_VISUALS}, {"TuningLevel", PERSISTENT}, {"TuningLevelConfirmed", PERSISTENT}, {"TurnAggressiveness", PERSISTENT | FROGPILOT_CONTROLS}, - {"TurnDesires", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"UnlimitedLength", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"UnlockDoors", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, + {"TurnDesires", PERSISTENT | FROGPILOT_CONTROLS}, + {"UnlimitedLength", PERSISTENT | FROGPILOT_VISUALS}, + {"UnlockDoors", PERSISTENT | FROGPILOT_VEHICLES}, {"Updated", PERSISTENT}, - {"UpdateWheelImage", PERSISTENT}, - {"UseSI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"UpdateWheelImage", CLEAR_ON_MANAGER_START}, + {"UseSI", PERSISTENT | FROGPILOT_VISUALS}, {"UseStockColors", CLEAR_ON_MANAGER_START}, - {"UseVienna", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"VisionTurnControl", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"VoltSNG", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, + {"UseVienna", PERSISTENT | FROGPILOT_CONTROLS}, + {"VisionTurnControl", PERSISTENT | FROGPILOT_CONTROLS}, + {"VoltSNG", PERSISTENT | FROGPILOT_VEHICLES}, {"WarningImmediateVolume", PERSISTENT | FROGPILOT_VISUALS}, {"WarningSoftVolume", PERSISTENT | FROGPILOT_VISUALS}, {"WD40Drives", PERSISTENT | FROGPILOT_CONTROLS}, {"WD40Score", PERSISTENT | FROGPILOT_CONTROLS}, - {"WheelIcon", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"WheelSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"WheelToDownload", PERSISTENT}, + {"WheelIcon", PERSISTENT | FROGPILOT_VISUALS}, + {"WheelSpeed", PERSISTENT | FROGPILOT_VISUALS}, + {"WheelToDownload", CLEAR_ON_MANAGER_START}, }; } // namespace diff --git a/common/params.h b/common/params.h index 461f522cf806bf..4ca716a4158fb3 100644 --- a/common/params.h +++ b/common/params.h @@ -17,11 +17,10 @@ enum ParamKeyType { DONT_LOG = 0x20, DEVELOPMENT_ONLY = 0x40, FROGPILOT_CONTROLS = 0x80, + FROGPILOT_VISUALS = 0x100, FROGPILOT_OTHER = 0x400, - FROGPILOT_STORAGE = 0x800, - FROGPILOT_TRACKING = 0x1000, - FROGPILOT_VEHICLES = 0x100, - FROGPILOT_VISUALS = 0x200, + FROGPILOT_TRACKING = 0x800, + FROGPILOT_VEHICLES = 0x1000, ALL = 0xFFFFFFFF }; diff --git a/common/params_pyx.pyx b/common/params_pyx.pyx index e56eadf5bb0720..c8dc65855ab75c 100644 --- a/common/params_pyx.pyx +++ b/common/params_pyx.pyx @@ -13,7 +13,6 @@ cdef extern from "common/params.h": DEVELOPMENT_ONLY FROGPILOT_CONTROLS FROGPILOT_OTHER - FROGPILOT_STORAGE FROGPILOT_TRACKING FROGPILOT_VEHICLES FROGPILOT_VISUALS diff --git a/common/util.cc b/common/util.cc index 5ffd6e4099f27b..4e766c92585eb2 100644 --- a/common/util.cc +++ b/common/util.cc @@ -271,4 +271,18 @@ std::string check_output(const std::string& command) { return result; } +bool system_time_valid() { + // Default to March 30, 2024 + tm min_tm = {.tm_year = 2024 - 1900, .tm_mon = 2, .tm_mday = 30}; + + time_t min_date = mktime(&min_tm); + + struct stat st; + if (stat("/lib/systemd/systemd", &st) == 0) { + min_date = std::max(min_date, st.st_mtime + 86400); // Add 1 day (86400 seconds) + } + + return time(nullptr) > min_date; +} + } // namespace util diff --git a/common/util.h b/common/util.h index 4c67312a41e577..a8a5adf5e96cae 100644 --- a/common/util.h +++ b/common/util.h @@ -96,6 +96,8 @@ bool create_directories(const std::string &dir, mode_t mode); std::string check_output(const std::string& command); +bool system_time_valid(); + inline void sleep_for(const int milliseconds) { if (milliseconds > 0) { std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); diff --git a/merge-resolver.sh b/merge-resolver.sh new file mode 100644 index 00000000000000..47455ab6955edd --- /dev/null +++ b/merge-resolver.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +echo "Please enter the commit hash you want to cherry-pick:" +read COMMIT_HASH + +# Validate commit hash +if ! [[ $COMMIT_HASH =~ ^[0-9a-f]{40}$ ]] && ! [[ $COMMIT_HASH =~ ^[0-9a-f]{7,8}$ ]]; then + echo "Error: Invalid commit hash format" + exit 1 +fi + +# Abort existing cherry-pick +git cherry-pick --abort + +# Start cherry-pick +echo "Starting cherry-pick for commit: $COMMIT_HASH" +git cherry-pick -n $COMMIT_HASH + +# Accept all incoming changes including deletions +git diff --name-only --diff-filter=U | while read file; do + if [ -f "$file" ]; then + git checkout --theirs -- "$file" + else + git rm "$file" + fi +done + +# Stage and commit +git add -A +git commit -C $COMMIT_HASH + +echo "Cherry-pick completed successfully!" +exit 0 \ No newline at end of file diff --git a/opendbc/hyundai_canfd.dbc b/opendbc/hyundai_canfd.dbc index 00991a7141a796..9b566de4784da6 100644 --- a/opendbc/hyundai_canfd.dbc +++ b/opendbc/hyundai_canfd.dbc @@ -599,6 +599,8 @@ BO_ 442 BLINDSPOTS_REAR_CORNERS: 24 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 + SG_ FL_INDICATOR_ALT : 138|1@0+ (1,0) [0|1] "" XXX + SG_ FR_INDICATOR_ALT : 141|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 @@ -658,6 +660,116 @@ BO_ 1264 LOCAL_TIME: 8 XXX SG_ MINUTES : 21|6@0+ (1,0) [0|63] "" XXX SG_ SECONDS : 31|8@0+ (1,0) [0|59] "" XXX +BO_ 353 MSG_161: 32 CCNC + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ FCA_ICON : 24|3@1+ (1,0) [0|7] "" XXX + SG_ FCA_ALT_ICON : 27|3@1+ (1,0) [0|7] "" XXX + SG_ LKA_ICON : 30|3@1+ (1,0) [0|3] "" XXX + SG_ HBA_ICON : 33|3@1+ (1,0) [0|7] "" XXX + SG_ ZEROS_1 : 36|4@1+ (1,0) [0|15] "" XXX + SG_ ZEROS_2 : 40|2@1+ (1,0) [0|3] "" XXX + SG_ FCA_IMAGE : 42|3@1+ (1,0) [0|7] "" XXX + SG_ ZEROS_3 : 45|3@1+ (1,0) [0|7] "" XXX + SG_ ZEROS_4 : 48|3@1+ (1,0) [0|7] "" XXX + SG_ BCA_LEFT : 51|3@1+ (1,0) [0|7] "" XXX + SG_ BCA_RIGHT : 54|3@1+ (1,0) [0|7] "" XXX + SG_ LCA_LEFT_ARROW : 57|3@1+ (1,0) [0|7] "" XXX + SG_ LCA_RIGHT_ARROW : 60|3@1+ (1,0) [0|7] "" XXX + SG_ ZEROS_5 : 63|1@0+ (1,0) [0|1] "" XXX + SG_ CENTERLINE : 64|2@1+ (1,0) [0|3] "" XXX + SG_ TARGET : 66|3@1+ (1,0) [0|7] "" XXX + SG_ TARGET_POSITION : 69|11@1+ (1,0) [0|7] "" XXX + SG_ LANELINE_LEFT : 80|4@1+ (1,0) [0|15] "" XXX + SG_ LANELINE_LEFT_POSITION : 84|6@1+ (1,0) [0|15] "" XXX + SG_ LANELINE_RIGHT : 90|4@1+ (1,0) [0|15] "" XXX + SG_ LANELINE_RIGHT_POSITION : 94|6@1+ (1,0) [0|3] "" XXX + SG_ LANELINE_CURVATURE : 100|5@1- (1,15) [0|31] "" XXX + SG_ LANE_HIGHLIGHT : 105|4@1+ (1,0) [0|15] "" XXX + SG_ LANE_HIGHLIGHT_DISTANCE : 109|11@1+ (1,0) [0|7] "" XXX + SG_ LANE_LEFT : 120|3@1+ (1,0) [0|7] "" XXX + SG_ LANE_RIGHT : 123|3@1+ (1,0) [0|7] "" XXX + SG_ LANE_ZOOM : 126|2@1+ (1,0) [0|3] "" XXX + SG_ ALERTS_1 : 128|6@1+ (1,0) [0|63] "" XXX + SG_ ALERTS_2 : 134|5@1+ (1,0) [0|3] "" XXX + SG_ ALERTS_3 : 139|4@1+ (1,0) [0|15] "" XXX + SG_ ALERTS_4 : 143|9@1+ (1,0) [0|511] "" XXX + SG_ ALERTS_5 : 152|5@1+ (1,0) [0|7] "" XXX + SG_ MUTE : 157|3@1+ (1,0) [0|7] "" XXX + SG_ SOUNDS_1 : 160|4@1+ (1,0) [0|3] "" XXX + SG_ SOUNDS_2 : 164|4@1+ (1,0) [0|3] "" XXX + SG_ SOUNDS_3 : 168|4@1+ (1,0) [0|15] "" XXX + SG_ SOUNDS_4 : 172|3@1+ (1,0) [0|7] "" XXX + SG_ ZEROS_6 : 175|1@0+ (1,0) [0|1] "" XXX + SG_ ZEROS_7 : 176|5@1+ (1,0) [0|31] "" XXX + SG_ SETSPEED_HUD : 181|3@1+ (1,0) [0|3] "" XXX + SG_ DISTANCE_LEAD : 184|5@1+ (1,0) [0|31] "" XXX + SG_ DISTANCE_CAR : 189|3@1+ (1,0) [0|7] "" XXX + SG_ DISTANCE_SPACING : 192|4@1+ (1,0) [0|15] "" XXX + SG_ DISTANCE : 196|4@1+ (1,0) [0|7] "" XXX + SG_ SETSPEED_SPEED : 200|8@1+ (1,0) [0|255] "" XXX + SG_ SETSPEED : 208|4@1+ (1,0) [0|3] "" XXX + SG_ HDA_ICON : 212|4@1+ (1,0) [0|3] "" XXX + SG_ SLA_ICON : 216|4@1+ (1,0) [0|15] "" XXX + SG_ NAV_ICON : 220|4@1+ (1,0) [0|3] "" XXX + SG_ LFA_ICON : 224|4@1+ (1,0) [0|3] "" XXX + SG_ LCA_LEFT_ICON : 228|4@1+ (1,0) [0|15] "" XXX + SG_ LCA_RIGHT_ICON : 232|4@1+ (1,0) [0|15] "" XXX + SG_ BACKGROUND : 236|4@1+ (1,0) [0|15] "" XXX + SG_ DAW_ICON : 240|3@1+ (1,0) [0|7] "" XXX + SG_ CAR_CIRCLE : 243|3@1+ (1,0) [0|7] "" XXX + SG_ ZEROS_8 : 246|2@1+ (1,0) [0|3] "" XXX + SG_ ZEROS_9 : 248|8@1+ (1,0) [0|255] "" XXX + +BO_ 354 MSG_162: 32 CCNC + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ SPEEDLIMIT_STYLE : 24|4@1+ (1,0) [0|7] "" XXX + SG_ SPEEDLIMIT_FLASH : 28|4@1+ (1,0) [0|15] "" XXX + SG_ SPEEDLIMIT : 32|8@1+ (1,0) [0|255] "" XXX + SG_ SIGNS : 40|8@1+ (1,0) [0|15] "" XXX + SG_ SPEEDLIMIT_WEATHER : 48|4@1+ (1,0) [0|15] "" XXX + SG_ VIBRATE : 52|3@1+ (1,0) [0|7] "" XXX + SG_ ZEROS_1 : 55|1@0+ (1,0) [0|1] "" XXX + SG_ ZEROS_2 : 56|8@1+ (1,0) [0|255] "" XXX + SG_ LEAD : 64|5@1+ (1,0) [0|31] "" XXX + SG_ LEAD_DISTANCE : 69|11@1+ (1,0) [0|2047] "" XXX + SG_ LEAD_LATERAL : 80|7@1+ (1,0) [0|127] "" XXX + SG_ ZEROS_3 : 87|1@0+ (1,0) [0|1] "" XXX + SG_ LEAD_ALT : 88|5@1+ (1,0) [0|31] "" XXX + SG_ LEAD_ALT_DISTANCE : 93|11@1+ (1,0) [0|2047] "" XXX + SG_ LEAD_ALT_LATERAL : 104|7@1+ (1,0) [0|127] "" XXX + SG_ ZEROS_4 : 111|1@0+ (1,0) [0|1] "" XXX + SG_ LEAD_LEFT : 112|5@1+ (1,0) [0|31] "" XXX + SG_ LEAD_LEFT_DISTANCE : 117|11@1+ (1,0) [0|2047] "" XXX + SG_ LEAD_LEFT_LATERAL : 128|7@1+ (1,0) [0|127] "" XXX + SG_ ZEROS_5 : 135|1@0+ (1,0) [0|1] "" XXX + SG_ LEAD_RIGHT : 136|5@1+ (1,0) [0|31] "" XXX + SG_ LEAD_RIGHT_DISTANCE : 141|11@1+ (1,0) [0|2047] "" XXX + SG_ LEAD_RIGHT_LATERAL : 152|7@1+ (1,0) [0|127] "" XXX + SG_ ZEROS_6 : 159|1@0+ (1,0) [0|1] "" XXX + SG_ ZEROS_7 : 160|8@1+ (1,0) [0|255] "" XXX + SG_ ZEROS_8 : 168|8@1+ (1,0) [0|255] "" XXX + SG_ ZEROS_9 : 176|8@1+ (1,0) [0|255] "" XXX + SG_ ZEROS_10 : 184|8@1+ (1,0) [0|255] "" XXX + SG_ ZEROS_11 : 192|8@1+ (1,0) [0|255] "" XXX + SG_ ZEROS_12 : 200|8@1+ (1,0) [0|255] "" XXX + SG_ ZEROS_13 : 208|5@1+ (1,0) [0|31] "" XXX + SG_ FAULT_FSS : 213|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_FCA : 216|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_LSS : 219|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_SLA : 222|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_DAW : 225|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_HBA : 228|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_SCC : 231|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_LFA : 234|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_HDA : 237|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_LCA : 240|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_HDP : 243|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_DAS : 246|3@1+ (1,0) [0|7] "" XXX + SG_ FAULT_ESS : 249|3@1+ (1,0) [0|7] "" XXX + SG_ ZEROS_14 : 252|4@1+ (1,0) [0|15] "" 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."; diff --git a/opendbc/hyundai_kia_generic.dbc b/opendbc/hyundai_kia_generic.dbc index f7ecd38f560e8d..c4933b9038cbe0 100644 --- a/opendbc/hyundai_kia_generic.dbc +++ b/opendbc/hyundai_kia_generic.dbc @@ -1476,6 +1476,7 @@ BO_ 905 SCC14: 8 SCC SG_ JerkUpperLimit : 12|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC SG_ JerkLowerLimit : 19|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC SG_ ACCMode : 32|3@1+ (1,0) [0|7] "" CLU,HUD,LDWS_LKAS,ESC + SG_ ObjDistStat : 42|2@1+ (1,0) [0|3] "" XXX SG_ ObjGap : 56|8@1+ (1,0) [0|255] "" CLU,HUD,ESC BO_ 1157 LFAHDA_MFC: 4 XXX @@ -1657,6 +1658,7 @@ VAL_ 871 CF_Lvr_IsgState 0 "enabled" 1 "activated" 2 "unknown" 3 "disabled"; VAL_ 871 CF_Lvr_Gear 12 "T" 5 "D" 8 "S" 6 "N" 7 "R" 4 "S" 0 "P"; VAL_ 882 Elect_Gear_Shifter 5 "D" 8 "S" 6 "N" 7 "R" 4 "S" 0 "P"; VAL_ 905 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault" 4 "cancelled"; +VAL_ 905 ObjDistStat 0 "no_object" 1 "receding" 2 "approaching"; VAL_ 909 CF_VSM_Warn 2 "FCW" 3 "AEB"; VAL_ 916 ACCEnable 0 "SCC ready" 1 "SCC temp fault" 2 "SCC permanent fault" 3 "SCC permanent fault, communication issue"; VAL_ 1056 SCCInfoDisplay 0 "No Message" 2 "Cruise Control" 3 "Lost Lead" 4 "Standstill"; diff --git a/panda/board/main.c b/panda/board/main.c index b481c28b46847d..e26bcb83bbf00a 100644 --- a/panda/board/main.c +++ b/panda/board/main.c @@ -223,7 +223,7 @@ void tick_handler(void) { } // exit controls allowed if unused by openpilot for a few seconds - if (controls_allowed && !heartbeat_engaged) { + if ((controls_allowed || aol_allowed) && !(heartbeat_engaged || (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL))) { heartbeat_engaged_mismatches += 1U; if (heartbeat_engaged_mismatches >= 3U) { controls_allowed = false; diff --git a/panda/board/safety.h b/panda/board/safety.h index 24ff779074385e..16293358deff2b 100644 --- a/panda/board/safety.h +++ b/panda/board/safety.h @@ -354,6 +354,7 @@ int set_safety_hooks(uint16_t mode, uint16_t param) { reset_sample(&torque_driver); reset_sample(&angle_meas); + aol_allowed = false; controls_allowed = false; relay_malfunction_reset(); safety_rx_checks_invalid = false; @@ -552,10 +553,8 @@ bool steer_torque_cmd_checks(int desired_torque, int steer_req, const SteeringLi bool violation = false; uint32_t ts = microsecond_timer_get(); - bool aol_allowed = acc_main_on && (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL); - if (controls_allowed) { - // acc main must be on if controls are allowed - acc_main_on = controls_allowed; + if (controls_allowed && !aol_allowed) { + acc_main_on = true; } if (controls_allowed || aol_allowed) { @@ -642,10 +641,9 @@ bool steer_torque_cmd_checks(int desired_torque, int steer_req, const SteeringLi // Safety checks for angle-based steering commands bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const SteeringLimits limits) { bool violation = false; - bool aol_allowed = acc_main_on && (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL); - if (controls_allowed) { - // acc main must be on if controls are allowed - acc_main_on = controls_allowed; + + if (controls_allowed && !aol_allowed) { + acc_main_on = true; } if ((controls_allowed || aol_allowed) && steer_control_enabled) { diff --git a/panda/board/safety/safety_hyundai.h b/panda/board/safety/safety_hyundai.h index a849a87e1dc87d..367b6b1d865a8b 100644 --- a/panda/board/safety/safety_hyundai.h +++ b/panda/board/safety/safety_hyundai.h @@ -87,6 +87,17 @@ RxCheck hyundai_legacy_rx_checks[] = { HYUNDAI_SCC12_ADDR_CHECK(0) }; +RxCheck hyundai_non_scc_addr_checks[] = { + {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, + {0x371, 0, 8, .frequency = 100U}, { 0 }}}, + {.msg = {{0x367, 0, 8, .frequency = 100U}, + {0x595, 0, 8, .frequency = 10U}, { 0 }}}, + {.msg = {{0x386, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, +}; + + +const int HYUNDAI_PARAM_NON_SCC = 1024; +bool hyundai_non_scc = false; bool hyundai_legacy = false; @@ -180,6 +191,13 @@ static void hyundai_rx_hook(const CANPacket_t *to_push) { update_sample(&torque_driver, torque_driver_new); } + bool lkas_button = false; + if (addr == 0x391) { + lkas_button = GET_BIT(to_push, 4U); + if (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL) { + hyundai_lkas_button_check(lkas_button); + } + } // ACC steering wheel buttons if (addr == 0x4F1) { int cruise_button = GET_BYTE(to_push, 0) & 0x7U; @@ -187,6 +205,23 @@ static void hyundai_rx_hook(const CANPacket_t *to_push) { hyundai_common_cruise_buttons_check(cruise_button, main_button); } + if (hyundai_non_scc) { + bool cruise_engaged = (hyundai_ev_gas_signal || hyundai_hybrid_gas_signal) ? GET_BIT(to_push, 51U) : GET_BYTE(to_push, 0) != 0U; + if (((addr == 0x595) && (hyundai_ev_gas_signal || hyundai_hybrid_gas_signal)) || + ((addr == 0x367) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal)) { + hyundai_common_cruise_state_check(cruise_engaged); + } else { + } + + acc_main_on = (hyundai_ev_gas_signal || hyundai_hybrid_gas_signal) ? GET_BIT(to_push, 50U) : GET_BIT(to_push, 25U); + if (((addr == 0x595) && (hyundai_ev_gas_signal || hyundai_hybrid_gas_signal)) || + ((addr == 0x260) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal)) { + if (acc_main_on && (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL)) { + aol_allowed = true; + controls_allowed = false; + } + } + } // gas press, different for EV, hybrid, and ICE models if ((addr == 0x371) && hyundai_ev_gas_signal) { gas_pressed = (((GET_BYTE(to_push, 4) & 0x7FU) << 1) | GET_BYTE(to_push, 3) >> 7) != 0U; @@ -311,6 +346,7 @@ static int hyundai_fwd_hook(int bus_num, int addr) { static safety_config hyundai_init(uint16_t param) { hyundai_common_init(param); hyundai_legacy = false; + hyundai_non_scc = GET_FLAG(param, HYUNDAI_PARAM_NON_SCC); if (hyundai_camera_scc) { hyundai_longitudinal = false; @@ -321,6 +357,8 @@ static safety_config hyundai_init(uint16_t param) { ret = BUILD_SAFETY_CFG(hyundai_long_rx_checks, HYUNDAI_LONG_TX_MSGS); } else if (hyundai_camera_scc) { ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks, HYUNDAI_CAMERA_SCC_TX_MSGS); + } else if (hyundai_non_scc) { + ret = BUILD_SAFETY_CFG(hyundai_non_scc_addr_checks, HYUNDAI_TX_MSGS); } else { ret = BUILD_SAFETY_CFG(hyundai_rx_checks, HYUNDAI_TX_MSGS); } @@ -332,6 +370,7 @@ static safety_config hyundai_legacy_init(uint16_t param) { hyundai_legacy = true; hyundai_longitudinal = false; hyundai_camera_scc = false; + hyundai_non_scc = GET_FLAG(param, HYUNDAI_PARAM_NON_SCC); return BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_TX_MSGS); } diff --git a/panda/board/safety/safety_hyundai_canfd.h b/panda/board/safety/safety_hyundai_canfd.h index fb6ccf55a0db41..1754a85a559cf8 100644 --- a/panda/board/safety/safety_hyundai_canfd.h +++ b/panda/board/safety/safety_hyundai_canfd.h @@ -51,6 +51,8 @@ const CanMsg HYUNDAI_CANFD_HDA1_TX_MSGS[] = { {0x1A0, 0, 32}, // CRUISE_INFO {0x1CF, 2, 8}, // CRUISE_BUTTON {0x1E0, 0, 16}, // LFAHDA_CLUSTER + {0x161, 0, 32}, // MSG_161 + {0x162, 0, 32}, // MSG_162 }; @@ -172,14 +174,21 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *to_push) { if (addr == button_addr) { bool main_button = false; int cruise_button = 0; + bool lkas_button = false; if (addr == 0x1cf) { cruise_button = GET_BYTE(to_push, 2) & 0x7U; main_button = GET_BIT(to_push, 19U); + lkas_button = GET_BIT(to_push, 23U); } else { cruise_button = (GET_BYTE(to_push, 4) >> 4) & 0x7U; main_button = GET_BIT(to_push, 34U); + lkas_button = GET_BIT(to_push, 39U); } hyundai_common_cruise_buttons_check(cruise_button, main_button); + + if (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL) { + hyundai_lkas_button_check(lkas_button); + } } // gas press, different for EV, hybrid, and ICE models @@ -248,7 +257,7 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *to_send) { bool is_cancel = (button == HYUNDAI_BTN_CANCEL); bool is_resume = (button == HYUNDAI_BTN_RESUME); - bool allowed = (is_cancel && cruise_engaged_prev) || (is_resume && controls_allowed); + bool allowed = (is_cancel && cruise_engaged_prev) || (is_resume && (controls_allowed || aol_allowed)); if (!allowed) { tx = false; } @@ -304,7 +313,9 @@ static int hyundai_canfd_fwd_hook(int bus_num, int addr) { // CRUISE_INFO for non-HDA2, we send our own longitudinal commands bool is_scc_msg = ((addr == 0x1a0) && hyundai_longitudinal && !hyundai_canfd_hda2); - bool block_msg = is_lkas_msg || is_lfa_msg || is_lfahda_msg || is_scc_msg; + bool is_ccnc_msg = (addr == 0x161) || (addr == 0x162); + + bool block_msg = is_lkas_msg || is_lfa_msg || is_lfahda_msg || is_scc_msg || is_ccnc_msg; if (!block_msg) { bus_fwd = 0; } diff --git a/panda/board/safety/safety_hyundai_common.h b/panda/board/safety/safety_hyundai_common.h index b4cf2b540c7d4a..a483564433ef32 100644 --- a/panda/board/safety/safety_hyundai_common.h +++ b/panda/board/safety/safety_hyundai_common.h @@ -43,6 +43,7 @@ void hyundai_common_init(uint16_t param) { #else hyundai_longitudinal = false; #endif + } void hyundai_common_cruise_state_check(const bool cruise_engaged) { @@ -61,6 +62,15 @@ void hyundai_common_cruise_state_check(const bool cruise_engaged) { cruise_engaged_prev = cruise_engaged; } } +void hyundai_lkas_button_check(const bool lkas_pressed) { + // Check LKAS button press + if (lkas_pressed && !lkas_pressed_prev) { + if (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL) { + aol_allowed = true; + } + } + lkas_pressed_prev = lkas_pressed; +} void hyundai_common_cruise_buttons_check(const int cruise_button, const bool main_button) { if (main_button && main_button != cruise_main_prev) { @@ -68,6 +78,10 @@ void hyundai_common_cruise_buttons_check(const int cruise_button, const bool mai controls_allowed = false; } acc_main_on = !acc_main_on; + if (acc_main_on && (alternative_experience & ALT_EXP_ALWAYS_ON_LATERAL)) { + aol_allowed = true; + controls_allowed = false; + } } cruise_main_prev = main_button; if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) || main_button) { @@ -93,6 +107,7 @@ void hyundai_common_cruise_buttons_check(const int cruise_button, const bool mai } } + uint32_t hyundai_common_canfd_compute_checksum(const CANPacket_t *to_push) { int len = GET_LEN(to_push); uint32_t address = GET_ADDR(to_push); diff --git a/panda/board/safety_declarations.h b/panda/board/safety_declarations.h index 0427d90aab1433..d004901d0fe982 100644 --- a/panda/board/safety_declarations.h +++ b/panda/board/safety_declarations.h @@ -227,6 +227,8 @@ bool acc_main_on = false; // referred to as "ACC off" in ISO 15622:2018 int cruise_button_prev = 0; int cruise_main_prev = 0; bool safety_rx_checks_invalid = false; +bool aol_allowed = false; +bool lkas_pressed_prev = false; // for safety modes with torque steering control int desired_torque_last = 0; // last desired steer torque diff --git a/panda/python/__init__.py b/panda/python/__init__.py index d31235075cd7dc..bead026a2ad671 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -210,6 +210,7 @@ class Panda: FLAG_HYUNDAI_ALT_LIMITS = 64 FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING = 128 FLAG_HYUNDAI_LFA_BTN = 256 + FLAG_HYUNDAI_NON_SCC = 1024 FLAG_TESLA_POWERTRAIN = 1 FLAG_TESLA_LONG_CONTROL = 2 diff --git a/panda/tests/safety/test_hyundai_canfd.py b/panda/tests/safety/test_hyundai_canfd.py index 7f280b631949fd..ad68c8c4e54549 100644 --- a/panda/tests/safety/test_hyundai_canfd.py +++ b/panda/tests/safety/test_hyundai_canfd.py @@ -83,7 +83,7 @@ class TestHyundaiCanfdHDA1Base(TestHyundaiCanfdBase): TX_MSGS = [[0x12A, 0], [0x1A0, 1], [0x1CF, 0], [0x1E0, 0]] RELAY_MALFUNCTION_ADDRS = {0: (0x12A,)} # LFA - FWD_BLACKLISTED_ADDRS = {2: [0x12A, 0x1E0]} + FWD_BLACKLISTED_ADDRS = {2: [0x12A, 0x1E0, 0x161, 0x162]} FWD_BUS_LOOKUP = {0: 2, 2: 0} STEER_MSG = "LFA" @@ -233,7 +233,7 @@ def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): ]) class TestHyundaiCanfdHDA1Long(HyundaiLongitudinalBase, TestHyundaiCanfdHDA1Base): - FWD_BLACKLISTED_ADDRS = {2: [0x12a, 0x1e0, 0x1a0]} + FWD_BLACKLISTED_ADDRS = {2: [0x12a, 0x1e0, 0x1a0, 0x161, 0x162]} RELAY_MALFUNCTION_ADDRS = {0: (0x12A, 0x1a0)} # LFA, SCC_CONTROL diff --git a/selfdrive/car/__init__.py b/selfdrive/car/__init__.py index b918b684abc5a3..24bf1e187b47a1 100644 --- a/selfdrive/car/__init__.py +++ b/selfdrive/car/__init__.py @@ -8,6 +8,7 @@ import capnp from cereal import car +from panda.python.uds import SERVICE_TYPE from openpilot.common.numpy_fast import clip, interp from openpilot.common.utils import Freezable from openpilot.selfdrive.car.docs_definitions import CarDocs @@ -212,6 +213,15 @@ def create_gas_interceptor_command(packer, gas_amount, idx): def make_can_msg(addr, dat, bus): return [addr, 0, dat, bus] +def make_tester_present_msg(addr, bus, subaddr=None, suppress_response=False): + dat = [0x02, SERVICE_TYPE.TESTER_PRESENT] + if subaddr is not None: + dat.insert(0, subaddr) + dat.append(0x80 if suppress_response else 0x0) # sub-function + + dat.extend([0x0] * (8 - len(dat))) + return make_can_msg(addr, bytes(dat), bus) + def get_safety_config(safety_model, safety_param = None): ret = car.CarParams.SafetyConfig.new_message() @@ -277,6 +287,8 @@ class PlatformConfig(Freezable): flags: int = 0 + fpFlags: int = 0 + platform_str: str | None = None def __hash__(self) -> int: @@ -320,3 +332,11 @@ def create_dbc_map(cls) -> dict[str, DbcDict]: @classmethod def with_flags(cls, flags: IntFlag) -> set['Platforms']: return {p for p in cls if p.config.flags & flags} + + @classmethod + def with_fp_flags(cls, fpFlags: IntFlag) -> set['Platforms']: + return {p for p in cls if p.config.fpFlags & fpFlags} + + @classmethod + def without_fp_flags(cls, fpFlags: IntFlag) -> set['Platforms']: + return {p for p in cls if not (p.config.fpFlags & fpFlags)} diff --git a/selfdrive/car/car_helpers.py b/selfdrive/car/car_helpers.py index 45cfcafe937327..c4810e8da4f647 100644 --- a/selfdrive/car/car_helpers.py +++ b/selfdrive/car/car_helpers.py @@ -209,6 +209,8 @@ def get_car(logcan, sendcan, disable_openpilot_long, experimental_long_allowed, elif candidate != "MOCK" and not params.get_bool("FingerprintLogged"): threading.Thread(target=sentry.capture_fingerprint, args=(candidate, params,)).start() + threading.Thread(target=sentry.capture_model, args=(frogpilot_toggles,)).start() + CarInterface, _, _ = interfaces[candidate] CP = CarInterface.get_params(candidate, fingerprints, car_fw, disable_openpilot_long, experimental_long_allowed, params, docs=False) CP.carVin = vin diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py old mode 100755 new mode 100644 index 7e055f53c84dc4..7cf6d1b6903181 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 import os import time +import threading +from types import SimpleNamespace import cereal.messaging as messaging @@ -15,6 +17,7 @@ from openpilot.selfdrive.pandad import can_list_to_can_capnp from openpilot.selfdrive.car.car_helpers import get_car, get_one_can from openpilot.selfdrive.car.interfaces import CarInterfaceBase +from openpilot.selfdrive.car.hyundai.hkg_additions import ParamManager from openpilot.selfdrive.controls.lib.events import Events from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles, update_frogpilot_toggles @@ -40,6 +43,7 @@ def __init__(self, CI=None): self.last_actuators_output = car.CarControl.Actuators.new_message() + self.frogpilot_toggles = get_frogpilot_toggles() self.params = Params() if CI is None: @@ -108,6 +112,10 @@ def __init__(self, CI=None): self.params.put_nonblocking("CarParamsCache", cp_bytes) self.params.put_nonblocking("CarParamsPersistent", cp_bytes) + self.hkg_additions: ParamManager = ParamManager() + self.hkg_additions.update(self.params) + self._params_list: SimpleNamespace = self.hkg_additions.get_params() + update_frogpilot_toggles() def state_update(self) -> car.CarState: @@ -115,7 +123,11 @@ def state_update(self) -> car.CarState: # Update carState from CAN can_strs = messaging.drain_sock_raw(self.can_sock, wait_for_one=True) - CS, FPCS = self.CI.update(self.CC_prev, can_strs, self.frogpilot_toggles) + + if self.sm['frogpilotPlan'].togglesUpdated: + self.frogpilot_toggles = get_frogpilot_toggles() + + CS, FPCS = self.CI.update(self.CC_prev, can_strs, self._params_list, self.frogpilot_toggles) self.sm.update(0) @@ -206,14 +218,33 @@ def step(self): self.initialized_prev = initialized self.CS_prev = CS.as_reader() - def card_thread(self): - while True: - self.step() - self.rk.monitor_time() + def fp_params_thread(self, event: threading.Event) -> None: + while not event.is_set(): + self.hkg_additions.update(self.params) + self._params_list = self.hkg_additions.get_params() + time.sleep(0.1) - # Update FrogPilot parameters - if self.sm['frogpilotPlan'].togglesUpdated: - self.frogpilot_toggles = get_frogpilot_toggles() + def frogpilot_params_thread(self, event: threading.Event) -> None: + while not event.is_set(): + if self.sm['frogpilotPlan'].togglesUpdated: + self.frogpilot_toggles = get_frogpilot_toggles() + time.sleep(0.1) + + def card_thread(self): + event = threading.Event() + fp_thread = threading.Thread(target=self.fp_params_thread, args=(event,)) + frog_thread = threading.Thread(target=self.frogpilot_params_thread, args=(event,)) + + try: + fp_thread.start() + frog_thread.start() + while True: + self.step() + self.rk.monitor_time() + finally: + event.set() + fp_thread.join() + frog_thread.join() def main(): config_realtime_process(4, Priority.CTRL_HIGH) diff --git a/selfdrive/car/fingerprints.py b/selfdrive/car/fingerprints.py index 1128a31c293722..8960411cf886ec 100644 --- a/selfdrive/car/fingerprints.py +++ b/selfdrive/car/fingerprints.py @@ -215,11 +215,13 @@ def all_legacy_fingerprint_cars(): "HYUNDAI SONATA HYBRID 2021": HYUNDAI.HYUNDAI_SONATA_HYBRID, "HYUNDAI IONIQ 5 2022": HYUNDAI.HYUNDAI_IONIQ_5, "HYUNDAI IONIQ 6 2023": HYUNDAI.HYUNDAI_IONIQ_6, + "HYUNDAI IONIQ 6 2025" : HYUNDAI.HYUNDAI_IONIQ_6_2025, "HYUNDAI TUCSON 4TH GEN": HYUNDAI.HYUNDAI_TUCSON_4TH_GEN, "HYUNDAI SANTA CRUZ 1ST GEN": HYUNDAI.HYUNDAI_SANTA_CRUZ_1ST_GEN, "HYUNDAI CUSTIN 1ST GEN": HYUNDAI.HYUNDAI_CUSTIN_1ST_GEN, "KIA FORTE E 2018 & GT 2021": HYUNDAI.KIA_FORTE, "KIA K5 2021": HYUNDAI.KIA_K5_2021, + "KIA K5 2025": HYUNDAI.KIA_K5_2025, "KIA K5 HYBRID 2020": HYUNDAI.KIA_K5_HEV_2020, "KIA K8 HYBRID 1ST GEN": HYUNDAI.KIA_K8_HEV_1ST_GEN, "KIA NIRO EV 2020": HYUNDAI.KIA_NIRO_EV, diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index ff037c116e5c20..cf1a5e5729bba3 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -1,12 +1,16 @@ from cereal import car +import cereal.messaging as messaging from openpilot.common.conversions import Conversions as CV -from openpilot.common.numpy_fast import clip +from openpilot.common.numpy_fast import clip, interp +from openpilot.common.params import Params 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.hkg_additions import JerkLimiter +from openpilot.selfdrive.controls.lib.longcontrol import LongControl +from openpilot.selfdrive.car import apply_driver_steer_torque_limits, common_fault_avoidance, make_tester_present_msg 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 +from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CANFD_CAR, CAR, CAMERA_SCC_CAR from openpilot.selfdrive.car.interfaces import CarControllerBase from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_acceleration import get_max_allowed_accel @@ -53,20 +57,41 @@ def __init__(self, dbc_name, CP, VM): self.packer = CANPacker(dbc_name) self.angle_limit_counter = 0 self.frame = 0 - + self.jerk = 0.0 self.accel_last = 0 self.apply_steer_last = 0 self.car_fingerprint = CP.carFingerprint self.last_button_frame = 0 + self.LoC = LongControl(CP) + + sub_services = ['longitudinalPlan'] + if CP.openpilotLongitudinalControl: + sub_services.append('radarState') + + if sub_services: + self.sm = messaging.SubMaster(sub_services) + + self.param_s = Params() + self.hkg_tuning = self.param_s.get_bool('HKGtuning') + self.jerk_limiter = JerkLimiter() def update(self, CC, CS, now_nanos, frogpilot_toggles): actuators = CC.actuators hud_control = CC.hudControl + accel = actuators.accel + + self.jerk_limiter.using_e2e = ( + hasattr(self, 'mpc') and self.mpc.mode == 'blended' + ) + + #Update HKG tuning state + self.hkg_tuning = frogpilot_toggles.hkg_tuning # 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, @@ -81,8 +106,18 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): self.apply_steer_last = apply_steer # accel + longitudinal + if self.hkg_tuning and self.CP.openpilotLongitudinalControl: + if actuators.accel < 0 or self.jerk_limiter.using_e2e: + accel = self.jerk_limiter.calculate_limited_accel( + accel, actuators, CS, LongCtrlState, interp, clip) + elif frogpilot_toggles.sport_plus: + accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo))) + else: + accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, CarControllerParams.ACCEL_MAX)) + if frogpilot_toggles.sport_plus: accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo))) + else: accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, CarControllerParams.ACCEL_MAX)) stopping = actuators.longControlState == LongCtrlState.stopping @@ -97,16 +132,17 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): # *** 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: + if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and \ + self.CP.carFingerprint not in CAMERA_SCC_CAR and self.CP.openpilotLongitudinalControl: # for longitudinal control, either radar or ADAS driving ECU - addr, bus = 0x7d0, 0 + addr, bus = 0x7d0, self.CAN.ECAN if self.CP.carFingerprint in CANFD_CAR else 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]) + can_sends.append(make_tester_present_msg(addr, bus, suppress_response=True)) # 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_sends.append(make_tester_present_msg(0x7b1, self.CAN.ECAN, suppress_response=True)) # CAN-FD platforms if self.CP.carFingerprint in CANFD_CAR: @@ -122,8 +158,11 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): self.CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING)) # LFA and HDA icons - if self.frame % 5 == 0 and (not hda2 or hda2_long): + if self.frame % 5 == 0 and (not hda2 or hda2_long) and self.car_fingerprint not in (CAR.KIA_K5_2025): can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled, CC.latActive)) + if self.frame % 5 == 0 and (not hda2 or hda2_long) and self.car_fingerprint in (CAR.KIA_K5_2025): + can_sends.append(hyundaicanfd.create_msg_161(self.packer, self.CAN, CC.enabled, CS.msg_161, self.CP, hud_control, CS, CC, self.frame)) + can_sends.append(hyundaicanfd.create_msg_162(self.packer, self.CAN, CC.enabled, CS.msg_162, self.CP, hud_control, CS)) # blinkers if hda2 and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS: @@ -132,9 +171,11 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): if self.CP.openpilotLongitudinalControl: if hda2: can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame)) + else: + can_sends.extend(hyundaicanfd.create_fca_warning_light(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, hud_control)) + can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CS, CC.enabled, self.accel_last, accel, + stopping, CC.cruiseControl.override, set_speed_in_units, hud_control)) self.accel_last = accel else: # button presses @@ -148,13 +189,14 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): 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 + jerk = self.jerk_limiter.jerk_upper_limit if actuators.longControlState == LongCtrlState.pid else self.jerk_limiter.jerk_lower_limit 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, set_speed_in_units, stopping, - CC.cruiseControl.override, use_fca, CS.out.cruiseState.available)) + CC.cruiseControl.override, use_fca, CS, self.CP, CS.out.cruiseState.available)) # 20 Hz LFA MFA message if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value: @@ -162,7 +204,7 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): # 5 Hz ACC options if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl: - can_sends.extend(hyundaican.create_acc_opt(self.packer)) + can_sends.extend(hyundaican.create_acc_opt(self.packer, CS, self.CP)) # 2 Hz front radar options if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl: diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index 842074c83ad2f0..85e1f62a01f85b 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -7,8 +7,9 @@ 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, Buttons, CarControllerParams +from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, HyundaiFlagsFP, CAR, DBC, CAN_GEARS, CAMERA_SCC_CAR, \ + CANFD_CAR, NON_SCC_CAR, NON_SCC_FCA_CAR, NON_SCC_RADAR_FCA_CAR, \ + Buttons, CarControllerParams from openpilot.selfdrive.car.interfaces import CarStateBase PREV_BUTTON_SAMPLES = 8 @@ -43,7 +44,8 @@ def __init__(self, CP): "CRUISE_BUTTONS" self.is_metric = False self.buttons_counter = 0 - + self.msg_161 = {} + self.msg_162 = {} self.cruise_info = {} # On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz @@ -57,6 +59,7 @@ def __init__(self, CP): self.active_mode = 0 self.drive_mode_prev = 0 + self.lkas_previously_enabled = False # Traffic signals for Speed Limit Controller - Credit goes to Multikyd! def calculate_speed_limit(self, cp, cp_cam): @@ -74,7 +77,7 @@ def update(self, cp, cp_cam, frogpilot_toggles): ret = car.CarState.new_message() fp_ret = custom.FrogPilotCarState.new_message() - cp_cruise = cp_cam if self.CP.carFingerprint in CAMERA_SCC_CAR else cp + cp_cruise = cp_cam if self.CP.carFingerprint in (CAMERA_SCC_CAR | (NON_SCC_FCA_CAR - NON_SCC_RADAR_FCA_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 @@ -123,6 +126,16 @@ def update(self, cp, cp_cam, frogpilot_toggles): ret.cruiseState.enabled = cp.vl["TCS13"]["ACC_REQ"] == 1 ret.cruiseState.standstill = False ret.cruiseState.nonAdaptive = False + elif self.CP.carFingerprint in NON_SCC_CAR: + cruise_available_msg = "E_CRUISE_CONTROL" if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV) else "EMS16" + cruise_enabled_msg = "E_CRUISE_CONTROL" if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV) else "LVR12" + cruise_speed_msg = "ELECT_GEAR" if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV) else "LVR12" + cruise_speed_sig = "VSetDis" if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV) else "CF_Lvr_CruiseSet" + ret.cruiseState.available = cp.vl[cruise_available_msg]["CRUISE_LAMP_M"] != 0 + ret.cruiseState.enabled = cp.vl[cruise_enabled_msg]["CF_Lvr_CruiseSet"] != 0 + ret.cruiseState.speed = cp.vl[cruise_speed_msg][cruise_speed_sig] * speed_conv + 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 @@ -130,6 +143,7 @@ def update(self, cp, cp_cam, frogpilot_toggles): 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 @@ -160,12 +174,11 @@ def update(self, cp, cp_cam, frogpilot_toggles): gear = cp.vl["LVR12"]["CF_Lvr_Gear"] ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear)) - - if not self.CP.openpilotLongitudinalControl: + if (not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC) and self.CP.carFingerprint in NON_SCC_FCA_CAR: 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 + scc_warning = False if self.CP.carFingerprint in NON_SCC_CAR else 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 @@ -177,6 +190,10 @@ def update(self, cp, cp_cam, frogpilot_toggles): # save the entire LKAS11 and CLU11 self.lkas11 = copy.copy(cp_cam.vl["LKAS11"]) self.clu11 = copy.copy(cp.vl["CLU11"]) + # only forward FCA for FCW/AEB when using OPLong on Camera SCC + if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint in CAMERA_SCC_CAR: + self.fca11 = copy.copy(cp_cruise.vl["FCA11"]) + self.fca12 = copy.copy(cp_cruise.vl["FCA12"]) 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"]) @@ -188,7 +205,7 @@ def update(self, cp, cp_cam, frogpilot_toggles): # FrogPilot CarState functions fp_ret.brakeLights = bool(cp.vl["TCS13"]["BrakeLight"]) - if self.CP.flags & HyundaiFlags.LKAS12 or self.CP.flags & HyundaiFlags.NAV_MSG: + if self.CP.fpFlags & HyundaiFlagsFP.FP_LKAS12 or self.CP.flags & HyundaiFlags.NAV_MSG: fp_ret.dashboardSpeedLimit = self.calculate_speed_limit(cp, cp_cam) * speed_conv self.prev_distance_button = self.distance_button @@ -242,14 +259,14 @@ def update_canfd(self, cp, cp_cam, frogpilot_toggles): # 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.HYUNDAI_KONA_EV_2ND_GEN: + if self.CP.carFingerprint == (CAR.HYUNDAI_KONA_EV_2ND_GEN, CAR.KIA_K5_2025): 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 - + alt = "_ALT" if self.CP.carFingerprint == CAR.KIA_K5_2025 else "" + ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"][f"FL_INDICATOR{alt}"] != 0 + ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"][f"FR_INDICATOR{alt}"] != 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 @@ -287,7 +304,12 @@ def update_canfd(self, cp, cp_cam, frogpilot_toggles): # FrogPilot CarState functions fp_ret.brakeLights = bool(cp.vl["TCS"]["DriverBraking"]) - if self.CP.flags & HyundaiFlags.NAV_MSG: + if self.CP.carFingerprint in (CAR.KIA_K5_2025,) and "MSG_161" in cp_cam.vl: + self.msg_161 = copy.copy(cp_cam.vl["MSG_161"]) + self.msg_162 = copy.copy(cp_cam.vl["MSG_162"]) + + + if self.CP.flags & HyundaiFlags.NAV_MSG or self.CP.fpFlags & HyundaiFlagsFP.FP_LKAS12: fp_ret.dashboardSpeedLimit = self.calculate_speed_limit(cp, cp_cam) * speed_factor self.prev_distance_button = self.distance_button @@ -327,11 +349,12 @@ def get_can_parser(self, CP): ("SAS11", 100), ] - if not CP.openpilotLongitudinalControl and CP.carFingerprint not in CAMERA_SCC_CAR: - messages += [ - ("SCC11", 50), - ("SCC12", 50), - ] + if not CP.openpilotLongitudinalControl and CP.carFingerprint not in (CAMERA_SCC_CAR | (NON_SCC_CAR - NON_SCC_RADAR_FCA_CAR)): + if CP.carFingerprint not in NON_SCC_CAR: + messages += [ + ("SCC11", 50), + ("SCC12", 50), + ] if CP.flags & HyundaiFlags.USE_FCA.value: messages.append(("FCA11", 50)) @@ -348,6 +371,8 @@ def get_can_parser(self, CP): if CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV): messages.append(("ELECT_GEAR", 20)) + if CP.carFingerprint in NON_SCC_CAR: + messages.append(("E_CRUISE_CONTROL", 10)) elif CP.carFingerprint in CAN_GEARS["use_cluster_gears"]: pass elif CP.carFingerprint in CAN_GEARS["use_tcu_gears"]: @@ -373,16 +398,24 @@ def get_cam_can_parser(CP): ("LKAS11", 100) ] - if not CP.openpilotLongitudinalControl and CP.carFingerprint in CAMERA_SCC_CAR: - messages += [ - ("SCC11", 50), - ("SCC12", 50), - ] + if not CP.openpilotLongitudinalControl and CP.carFingerprint in (CAMERA_SCC_CAR | (NON_SCC_FCA_CAR - NON_SCC_RADAR_FCA_CAR)): + if CP.carFingerprint in CAMERA_SCC_CAR: + messages += [ + ("SCC11", 50), + ("SCC12", 50), + ] if CP.flags & HyundaiFlags.USE_FCA.value: messages.append(("FCA11", 50)) - if CP.flags & HyundaiFlags.LKAS12: + if CP.openpilotLongitudinalControl and CP.carFingerprint in CAMERA_SCC_CAR: + if CP.flags & HyundaiFlags.USE_FCA.value: + messages += [ + ("FCA11", 50), + ("FCA12", 50), + ] + + if CP.fpFlags & HyundaiFlagsFP.FP_LKAS12: messages.append(("LKAS12", 10)) return CANParser(DBC[CP.carFingerprint]["pt"], messages, 2) @@ -421,6 +454,7 @@ def get_can_parser_canfd(self, CP): ("SCC_CONTROL", 50), ] + if CP.flags & HyundaiFlags.CANFD_HDA2 and CP.flags & HyundaiFlags.NAV_MSG: messages.append(("CLUSTER_SPEED_LIMIT", 10)) @@ -436,8 +470,18 @@ def get_cam_can_parser_canfd(CP): messages += [ ("SCC_CONTROL", 50), ] - + if CP.carFingerprint in CAMERA_SCC_CAR: + messages += [ + ("SCC11", 50), + ("SCC12", 50), + ] if not (CP.flags & HyundaiFlags.CANFD_HDA2) and CP.flags & HyundaiFlags.NAV_MSG: messages.append(("CLUSTER_SPEED_LIMIT", 10)) + if CP.carFingerprint in (CAR.KIA_K5_2025,): + messages += [ + ("MSG_161", 20), + ("MSG_162", 20), + ] + return CANParser(DBC[CP.carFingerprint]["pt"], messages, CanBus(CP).CAM) diff --git a/selfdrive/car/hyundai/enable_radar_tracks.py b/selfdrive/car/hyundai/enable_radar_tracks.py new file mode 100644 index 00000000000000..3a64d50cb94b36 --- /dev/null +++ b/selfdrive/car/hyundai/enable_radar_tracks.py @@ -0,0 +1,47 @@ +from openpilot.selfdrive.car.isotp_parallel_query import IsoTpParallelQuery +from openpilot.common.swaglog import cloudlog + +EXT_DIAG_REQUEST = b'\x10\x07' +EXT_DIAG_RESPONSE = b'\x50\x07' + +WRITE_DATA_REQUEST = b'\x2e' +WRITE_DATA_RESPONSE = b'\x68' + +RADAR_TRACKS_CONFIG = b"\x00\x00\x00\x01\x00\x01" + + +def enable_radar_tracks(logcan, sendcan, bus=0, addr=0x7d0, config_data_id=b'\x01\x42', timeout=0.1, retry=10, debug=False): + cloudlog.warning("radar_tracks: enabling ...") + + for i in range(retry): + try: + query = IsoTpParallelQuery(sendcan, logcan, bus, [addr], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE], debug=debug) + + for _, _ in query.get_data(timeout).items(): + cloudlog.warning("radar_tracks: reconfigure radar to output radar points ...") + + query = IsoTpParallelQuery(sendcan, logcan, bus, [addr], + [WRITE_DATA_REQUEST + config_data_id + RADAR_TRACKS_CONFIG], + [WRITE_DATA_RESPONSE], debug=debug) + query.get_data(0) + + cloudlog.warning("radar_tracks: successfully enabled") + return True + + except Exception as e: + cloudlog.exception(f"radar_tracks exception: {e}") + + cloudlog.error(f"radar_tracks retry ({i + 1}) ...") + cloudlog.error(f"radar_tracks: failed") + return False + + +if __name__ == "__main__": + import time + import cereal.messaging as messaging + sendcan = messaging.pub_sock('sendcan') + logcan = messaging.sub_sock('can') + time.sleep(1) + + enabled = enable_radar_tracks(logcan, sendcan, bus=0, addr=0x7d0, config_data_id=b'\x01\x42', timeout=0.1, debug=False) + print(f"enabled: {enabled}") diff --git a/selfdrive/car/hyundai/fingerprinting.py b/selfdrive/car/hyundai/fingerprinting.py new file mode 100644 index 00000000000000..3551067204695e --- /dev/null +++ b/selfdrive/car/hyundai/fingerprinting.py @@ -0,0 +1,37 @@ +from collections.abc import Callable + +import cereal.messaging as messaging +from openpilot.selfdrive.car import gen_empty_fingerprint + +FRAME_FINGERPRINT = 25 # 0.25s + + +def get_one_can(logcan): + while True: + can = messaging.recv_one_retry(logcan) + if len(can.can) > 0: + return can + + +def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]: + finger = gen_empty_fingerprint() + frame = 0 + done = False + + while not done: + a = next_can() + + for can in a.can: + # The fingerprint dict is generated for all buses, this way the car interface + # can use it to detect a (valid) multipanda setup and initialize accordingly + if can.src < 128: + if can.src not in finger: + finger[can.src] = {} + finger[can.src][can.address] = len(can.dat) + + # bail if we've been waiting for more than 2s + done = frame > 100 + + frame += 1 + + return finger diff --git a/selfdrive/car/hyundai/fingerprints.py b/selfdrive/car/hyundai/fingerprints.py index e489a3cf641216..e61f0ead66b920 100644 --- a/selfdrive/car/hyundai/fingerprints.py +++ b/selfdrive/car/hyundai/fingerprints.py @@ -85,12 +85,14 @@ b'\xf1\x00AEhe SCC H-CUP 1.01 1.01 96400-G2100 ', ], (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00AE MDPS C 1.00 1.03 56310/G2300 4AEHC103', b'\xf1\x00AE MDPS C 1.00 1.05 56310/G2501 4AEHC105', b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2301 4AEHC107', b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2501 4AEHC107', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00AEH MFC AT EUR LHD 1.00 1.00 95740-G2400 180222', + b'\xf1\x00AEH MFC AT EUR LHD 1.00 1.00 95740-G7200 160418', b'\xf1\x00AEH MFC AT USA LHD 1.00 1.00 95740-G2400 180222', ], }, @@ -177,12 +179,24 @@ b'\xf1\x00AEhe SCC FHCUP 1.00 1.00 99110-G2600 ', ], (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00AE MDPS C 1.00 1.01 56310/G2510 4APHC101', b'\xf1\x00AE MDPS C 1.00 1.01 56310G2510\x00 4APHC101', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00AEH MFC AT USA LHD 1.00 1.00 95740-G2700 201027', ], }, + CAR.HYUNDAI_SONATA_2019: { + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00LF__ SCC F-CUP 1.00 1.00 96401-C2200 ', + ], + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x00LF ESC \t 11 \x17\x01\x13 58920-C2610', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00LFF LKAS AT USA LHD 1.01 1.02 95740-C1000 E52', + ], + }, CAR.HYUNDAI_SONATA: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00DN8_ SCC F-CU- 1.00 1.00 99110-L0000 ', @@ -207,6 +221,8 @@ (Ecu.eps, 0x7d4, None): [ b'\xf1\x00DN8 MDPS C 1,00 1,01 56310L0010\x00 4DNAC101', b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L0010 4DNAC101', + b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L0200 4DNAC101', + b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L0200 4DNAC102', b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L0210 4DNAC101', b'\xf1\x00DN8 MDPS C 1.00 1.01 56310-L0210 4DNAC102', b'\xf1\x00DN8 MDPS C 1.00 1.01 56310L0010\x00 4DNAC101', @@ -248,9 +264,11 @@ (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00TL__ FCA F-CUP 1.00 1.01 99110-D3500 ', b'\xf1\x00TL__ FCA F-CUP 1.00 1.02 99110-D3510 ', + b'\xf1\x00TL__ FCA FHCUP 1.00 1.02 99110-D3500 ', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00TL MFC AT KOR LHD 1.00 1.02 95895-D3800 180719', + b'\xf1\x00TL MFC AT KOR LHD 1.00 1.06 95895-D3800 190107', b'\xf1\x00TL MFC AT USA LHD 1.00 1.06 95895-D3800 190107', ], }, @@ -264,6 +282,7 @@ (Ecu.abs, 0x7d1, None): [ b'\xf1\x00TM ESC \x02 100\x18\x030 58910-S2600', b'\xf1\x00TM ESC \x02 102\x18\x07\x01 58910-S2600', + b'\xf1\x00TM ESC \x02 103\x18\x11\x05 58910-S2500', b'\xf1\x00TM ESC \x02 103\x18\x11\x07 58910-S2600', b'\xf1\x00TM ESC \x02 104\x19\x07\x07 58910-S2600', b'\xf1\x00TM ESC \x03 103\x18\x11\x07 58910-S2600', @@ -298,6 +317,7 @@ b'\xf1\x00TM ESC \x02 103"\x07\x08 58910-S2GA0', b'\xf1\x00TM ESC \x03 101 \x08\x02 58910-S2DA0', b'\xf1\x00TM ESC \x03 102!\x04\x03 58910-S2DA0', + b'\xf1\x00TM ESC \x03 103"\x07\x06 58910-S2DA0', b'\xf1\x00TM ESC \x04 101 \x08\x04 58910-S2GA0', b'\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0', b'\xf1\x00TM ESC \x04 103"\x07\x08 58910-S2GA0', @@ -327,6 +347,7 @@ b'\xf1\x00TM MDPS C 1.00 1.02 56310-CLAC0 4TSHC102', b'\xf1\x00TM MDPS C 1.00 1.02 56310-CLEC0 4TSHC102', b'\xf1\x00TM MDPS C 1.00 1.02 56310-GA000 4TSHA100', + b'\xf1\x00TM MDPS C 1.00 1.02 56310GA000\x00 4TSHA100', b'\xf1\x00TM MDPS R 1.00 1.05 57700-CL000 4TSHP105', b'\xf1\x00TM MDPS R 1.00 1.06 57700-CL000 4TSHP106', ], @@ -415,6 +436,7 @@ CAR.HYUNDAI_PALISADE: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00LX2 SCC FHCUP 1.00 1.04 99110-S8100 ', + b'\xf1\x00LX2_ SCC F-CUP 1.00 1.00 99110-S8110 ', b'\xf1\x00LX2_ SCC F-CUP 1.00 1.04 99110-S8100 ', b'\xf1\x00LX2_ SCC F-CUP 1.00 1.05 99110-S8100 ', b'\xf1\x00LX2_ SCC FHCU- 1.00 1.05 99110-S8100 ', @@ -422,6 +444,7 @@ b'\xf1\x00LX2_ SCC FHCUP 1.00 1.03 99110-S8100 ', b'\xf1\x00LX2_ SCC FHCUP 1.00 1.04 99110-S8100 ', b'\xf1\x00LX2_ SCC FHCUP 1.00 1.05 99110-S8100 ', + b'\xf1\x00ON__ FCA FHCU- 1.00 1.00 99110-S9110 ', b'\xf1\x00ON__ FCA FHCUP 1.00 1.00 99110-S9110 ', b'\xf1\x00ON__ FCA FHCUP 1.00 1.01 99110-S9110 ', b'\xf1\x00ON__ FCA FHCUP 1.00 1.02 99110-S9100 ', @@ -438,17 +461,20 @@ b'\xf1\x00LX ESC \x0b 103\x19\t\x07 58910-S8330', b'\xf1\x00LX ESC \x0b 103\x19\t\t 58910-S8350', b'\xf1\x00LX ESC \x0b 103\x19\t\x10 58910-S8360', + b'\xf1\x00LX ESC \x0b 104 \x10\x13 58910-S8330', b'\xf1\x00LX ESC \x0b 104 \x10\x16 58910-S8360', b'\xf1\x00ON ESC \x01 101\x19\t\x08 58910-S9360', b'\xf1\x00ON ESC \x0b 100\x18\x12\x18 58910-S9360', b'\xf1\x00ON ESC \x0b 101\x19\t\x05 58910-S9320', b'\xf1\x00ON ESC \x0b 101\x19\t\x08 58910-S9360', + b'\xf1\x00ON ESC \x0b 103$\x04\x08 58910-S9360', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00LX2 MDPS C 1,00 1,03 56310-S8020 4LXDC103', b'\xf1\x00LX2 MDPS C 1.00 1.03 56310-S8000 4LXDC103', b'\xf1\x00LX2 MDPS C 1.00 1.03 56310-S8020 4LXDC103', b'\xf1\x00LX2 MDPS C 1.00 1.03 56310-XX000 4LXDC103', + b'\xf1\x00LX2 MDPS C 1.00 1.04 56310-S8000 4LXDC104', b'\xf1\x00LX2 MDPS C 1.00 1.04 56310-S8020 4LXDC104', b'\xf1\x00LX2 MDPS C 1.00 1.04 56310-S8420 4LXDC104', b'\xf1\x00LX2 MDPS R 1.00 1.02 56370-S8300 9318', @@ -516,11 +542,13 @@ }, CAR.GENESIS_G80: { (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00DH__ SCC F-CU- 1.00 1.01 96400-B1110 ', b'\xf1\x00DH__ SCC F-CUP 1.00 1.01 96400-B1120 ', b'\xf1\x00DH__ SCC F-CUP 1.00 1.02 96400-B1120 ', b'\xf1\x00DH__ SCC FHCUP 1.00 1.01 96400-B1110 ', ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00DH LKAS AT EUR LHD 1.01 1.01 95895-B1500 161014', b'\xf1\x00DH LKAS AT KOR LHD 1.01 1.01 95895-B1500 161014', b'\xf1\x00DH LKAS AT KOR LHD 1.01 1.02 95895-B1500 170810', b'\xf1\x00DH LKAS AT USA LHD 1.01 1.01 95895-B1500 161014', @@ -529,6 +557,14 @@ b'\xf1\x00DH LKAS AT USA LHD 1.01 1.04 95895-B1500 181213', ], }, + CAR.GENESIS_G80_2ND_GEN_FL: { + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00RG3_ SCC ----- 1.00 1.02 99110-T1120 ', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00RG3 MFC AT USA LHD 1.00 1.01 99211-T1200 230607', + ], + }, CAR.GENESIS_G90: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00HI__ SCC F-CUP 1.00 1.01 96400-D2100 ', @@ -537,6 +573,7 @@ (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00HI LKAS AT USA LHD 1.00 1.00 95895-D2020 160302', b'\xf1\x00HI LKAS AT USA LHD 1.00 1.00 95895-D2030 170208', + b'\xf1\x00HI LKAS AT USA LHD 1.00 1.01 95895-D2030 170811', b'\xf1\x00HI MFC AT USA LHD 1.00 1.03 99211-D2000 190831', ], }, @@ -592,6 +629,7 @@ (Ecu.eps, 0x7d4, None): [ b'\xf1\x00DL3 MDPS C 1.00 1.01 56310-L3110 4DLAC101', b'\xf1\x00DL3 MDPS C 1.00 1.01 56310-L3220 4DLAC101', + b'\xf1\x00DL3 MDPS C 1.00 1.01 56310L3220\x00 4DLAC101', b'\xf1\x00DL3 MDPS C 1.00 1.02 56310-L2220 4DLDC102', b'\xf1\x00DL3 MDPS C 1.00 1.02 56310L3220\x00 4DLAC102', b'\xf1\x00DL3 MDPS R 1.00 1.02 57700-L3000 4DLAP102', @@ -606,11 +644,29 @@ b'\xf1\x00DL ESC \x01 104 \x07\x12 58910-L2200', b'\xf1\x00DL ESC \x03 100 \x08\x02 58910-L3600', b'\xf1\x00DL ESC \x06 101 \x04\x02 58910-L3200', + b'\xf1\x00DL ESC \x06 102 \x07\x02 58910-L3200', b'\xf1\x00DL ESC \x06 103"\x08\x06 58910-L3200', b'\xf1\x00DL ESC \t 100 \x06\x02 58910-L3800', b'\xf1\x00DL ESC \t 101 \x07\x02 58910-L3800', b'\xf1\x00DL ESC \t 102"\x08\x10 58910-L3800', ], + }, + CAR.KIA_K5_2025: { + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00DL3_ RDR ----- 1.00 1.01 99110-L2500 ', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.04 99210-L2500 240117', + ], + (Ecu.cornerRadar, 0x7b7, None): [ + b'\xf1\x8b #\x12\x12', + ], + (Ecu.combinationMeter, 0x7c6, None): [ + b'\xf1\x8b $\t#', + ], + (Ecu.hvac, 0x7b3, None): [ + b'\xf1\x8b $\t%', + ], }, CAR.KIA_K5_HEV_2020: { (Ecu.fwdRadar, 0x7d0, None): [ @@ -621,6 +677,7 @@ b'\xf1\x00DL3 MDPS C 1.00 1.02 56310-L7220 4DLHC102', ], (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00DL3HMFC AT KOR LHD 1.00 1.01 99210-L2000 191022', b'\xf1\x00DL3HMFC AT KOR LHD 1.00 1.02 99210-L2000 200309', b'\xf1\x00DL3HMFC AT KOR LHD 1.00 1.04 99210-L2000 210527', ], @@ -631,22 +688,28 @@ b'\xf1\x00OS IEB \x02 210 \x02\x14 58520-K4000', b'\xf1\x00OS IEB \x02 212 \x11\x13 58520-K4000', b'\xf1\x00OS IEB \x03 210 \x02\x14 58520-K4000', + b'\xf1\x00OS IEB \x03 211 \x04\x02 58520-K4000', b'\xf1\x00OS IEB \x03 212 \x11\x13 58520-K4000', + b'\xf1\x00OS IEB \x04 212 \x11\x13 58520-K4000', b'\xf1\x00OS IEB \r 105\x18\t\x18 58520-K4000', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00OE2 LKAS AT EUR LHD 1.00 1.00 95740-K4200 200', + b'\xf1\x00OSE LKAS AT AUS RHD 1.00 1.00 95740-K4300 W50', b'\xf1\x00OSE LKAS AT EUR LHD 1.00 1.00 95740-K4100 W40', b'\xf1\x00OSE LKAS AT EUR RHD 1.00 1.00 95740-K4100 W40', b'\xf1\x00OSE LKAS AT KOR LHD 1.00 1.00 95740-K4100 W40', + b'\xf1\x00OSE LKAS AT KOR LHD 1.00 1.00 95740-K4300 W50', b'\xf1\x00OSE LKAS AT USA LHD 1.00 1.00 95740-K4100 W40', b'\xf1\x00OSE LKAS AT USA LHD 1.00 1.00 95740-K4300 W50', ], (Ecu.eps, 0x7d4, None): [ b'\xf1\x00OS MDPS C 1.00 1.03 56310/K4550 4OEDC103', b'\xf1\x00OS MDPS C 1.00 1.04 56310-XX000 4OEDC104', + b'\xf1\x00OS MDPS C 1.00 1.04 56310/K4550 4OEDC104', b'\xf1\x00OS MDPS C 1.00 1.04 56310K4000\x00 4OEDC104', b'\xf1\x00OS MDPS C 1.00 1.04 56310K4050\x00 4OEDC104', + b'\xf1\x00OS MDPS C 1.00 1.05 56310K4000\x00 4OEDC105', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00OSev SCC F-CUP 1.00 1.00 99110-K4000 ', @@ -712,9 +775,11 @@ b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.00 99211-Q4000 191211', b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.00 99211-Q4100 200706', b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.03 95740-Q4000 180821', + b'\xf1\x00DEE MFC AT KOR LHD 1.00 1.02 95740-Q4000 180705', b'\xf1\x00DEE MFC AT KOR LHD 1.00 1.03 95740-Q4000 180821', b'\xf1\x00DEE MFC AT USA LHD 1.00 1.00 99211-Q4000 191211', b'\xf1\x00DEE MFC AT USA LHD 1.00 1.01 99211-Q4500 210428', + b'\xf1\x00DEE MFC AT USA LHD 1.00 1.02 99211-Q4100 201218', b'\xf1\x00DEE MFC AT USA LHD 1.00 1.03 95740-Q4000 180821', ], }, @@ -740,6 +805,7 @@ ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00DEhe SCC F-CUP 1.00 1.02 99110-G5100 ', + b'\xf1\x00DEhe SCC FHCUP 1.00 1.02 99110-G5100 ', b'\xf1\x00DEhe SCC H-CUP 1.01 1.02 96400-G5100 ', ], }, @@ -763,6 +829,7 @@ (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00DEH MFC AT KOR LHD 1.00 1.04 99211-G5000 190516', b'\xf1\x00DEH MFC AT USA LHD 1.00 1.00 99211-G5500 210428', + b'\xf1\x00DEH MFC AT USA LHD 1.00 1.06 99211-G5000 201028', b'\xf1\x00DEH MFC AT USA LHD 1.00 1.07 99211-G5000 201221', ], (Ecu.fwdRadar, 0x7d0, None): [ @@ -792,10 +859,12 @@ b'\xf1\x00JF__ SCC F-CUP 1.00 1.00 96400-D4100 ', ], (Ecu.abs, 0x7d1, None): [ + b'\xf1\x00JF ESC \t 17 \x16\x06# 58920-D4180', b'\xf1\x00JF ESC \x0f 16 \x16\x06\x17 58920-D5080', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00JFWGN LDWS AT USA LHD 1.00 1.02 95895-D4100 G21', + b'\xf1\x00JFWGN LKAS AT EUR LHD 1.00 1.01 95895-D4100 G20', ], }, CAR.KIA_OPTIMA_G4_FL: { @@ -805,6 +874,7 @@ (Ecu.abs, 0x7d1, None): [ b"\xf1\x00JF ESC \t 11 \x18\x03' 58920-D5260", b'\xf1\x00JF ESC \x0b 11 \x18\x030 58920-D5180', + b'\xf1\x00JF ESC \x0c 11 \x18\x030 58920-D5180', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00JFA LKAS AT USA LHD 1.00 1.00 95895-D5001 h32', @@ -969,6 +1039,8 @@ b'\xf1\x00CV1 MFC AT EUR LHD 1.00 1.05 99210-CV000 211027', b'\xf1\x00CV1 MFC AT EUR LHD 1.00 1.06 99210-CV000 220328', b'\xf1\x00CV1 MFC AT EUR RHD 1.00 1.00 99210-CV100 220630', + b'\xf1\x00CV1 MFC AT KOR LHD 1.00 1.00 99210-CV100 220630', + b'\xf1\x00CV1 MFC AT KOR LHD 1.00 1.00 99210-CV200 230510', b'\xf1\x00CV1 MFC AT KOR LHD 1.00 1.04 99210-CV000 210823', b'\xf1\x00CV1 MFC AT KOR LHD 1.00 1.05 99210-CV000 211027', b'\xf1\x00CV1 MFC AT KOR LHD 1.00 1.06 99210-CV000 220328', @@ -985,32 +1057,47 @@ (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00NE1 MFC AT CAN LHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT CAN LHD 1.00 1.05 99211-GI010 220614', + b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.00 99211-GI100 230915', b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.01 99211-GI010 211007', + b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.01 99211-GI100 240110', + b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.03 99211-GI010 220401', b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI000 210813', b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI010 230110', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT EUR RHD 1.00 1.02 99211-GI010 211206', + b'\xf1\x00NE1 MFC AT IND RHD 1.00 1.07 99211-GI010 230620', b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.00 99211-GI020 230719', + b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT KOR LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI020 230719', + b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI100 230915', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI010 211007', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.03 99211-GI010 220401', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614', b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.06 99211-GI010 230110', - b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI100 230915', - b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.00 99211-GI100 230915', ], }, CAR.HYUNDAI_IONIQ_6: { (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00CE__ RDR ----- 1.00 1.01 99110-KL000 ', + b'\xf1\x00CE__ RDR ----- 1.00 1.02 99110-KL000 ', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00CE MFC AT CAN LHD 1.00 1.04 99211-KL000 221213', b'\xf1\x00CE MFC AT EUR LHD 1.00 1.03 99211-KL000 221011', b'\xf1\x00CE MFC AT EUR LHD 1.00 1.04 99211-KL000 221213', b'\xf1\x00CE MFC AT USA LHD 1.00 1.04 99211-KL000 221213', + b'\xf1\x00CE MFC AT USA LHD 1.00 1.06 99211-KL000 230915', + ], + }, + CAR.HYUNDAI_IONIQ_6_2025: { + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00CE__ RDR ----- 1.00 1.02 99110-KL000 ', + b'\xf1\x8b $\x06\x12', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00CE MFC AT USA LHD 1.00 1.06 99211-KL000 230915', ], }, CAR.HYUNDAI_TUCSON_4TH_GEN: { @@ -1077,6 +1164,7 @@ }, CAR.GENESIS_GV60_EV_1ST_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00JW1 MFC AT AUS RHD 1.00 1.03 99211-CU100 221118', b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.02 99211-CU000 211215', b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.02 99211-CU100 211215', b'\xf1\x00JW1 MFC AT USA LHD 1.00 1.03 99211-CU000 221118', @@ -1090,6 +1178,7 @@ b'\xf1\x00MQ4 MFC AT USA LHD 1.00 1.00 99210-R5100 221019', b'\xf1\x00MQ4 MFC AT USA LHD 1.00 1.03 99210-R5000 200903', b'\xf1\x00MQ4 MFC AT USA LHD 1.00 1.05 99210-R5000 210623', + b'\xf1\x00MQ4 MFC AT USA LHD 1.00 1.06 99210-R5000 211216', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00MQ4_ SCC F-CUP 1.00 1.06 99110-P2000 ', @@ -1131,6 +1220,7 @@ CAR.KIA_CARNIVAL_4TH_GEN: { (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00KA4 MFC AT EUR LHD 1.00 1.06 99210-R0000 220221', + b'\xf1\x00KA4 MFC AT KOR LHD 1.00 1.05 99210-R0000 201221', b'\xf1\x00KA4 MFC AT KOR LHD 1.00 1.06 99210-R0000 220221', b'\xf1\x00KA4 MFC AT USA LHD 1.00 1.00 99210-R0100 230105', b'\xf1\x00KA4 MFC AT USA LHD 1.00 1.01 99210-R0100 230710', @@ -1141,6 +1231,7 @@ (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00KA4_ SCC F-CUP 1.00 1.03 99110-R0000 ', b'\xf1\x00KA4_ SCC FHCUP 1.00 1.00 99110-R0100 ', + b'\xf1\x00KA4_ SCC FHCUP 1.00 1.02 99110-R0000 ', b'\xf1\x00KA4_ SCC FHCUP 1.00 1.03 99110-R0000 ', b'\xf1\x00KA4c SCC FHCUP 1.00 1.01 99110-I4000 ', ], @@ -1162,4 +1253,134 @@ b'\xf1\x00US4_ RDR ----- 1.00 1.00 99110-CG000 ', ], }, + # Non SCC + CAR.GENESIS_G70_2021_NON_SCC: { + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00IK MDPS R 1.00 1.08 57700-G9200 4I2CL108', + ], + (Ecu.fwdRadar, 0x7d0, None): [ + b'\xf1\x00IK__ SCC --CUP 1.00 1.02 96400-G9100 ', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00IK MFC MT USA LHD 1.00 1.01 95740-G9000 170920', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x81606G2051\x00\x00\x00\x00\x00\x00\x00\x00', + ], + }, + CAR.HYUNDAI_KONA_NON_SCC: { + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x816V5RAJ00040.ELF\xf1\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8161699051\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00OS MDPS C 1.00 1.05 56310J9030\x00 4OSDC105', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00OS9 LKAS AT USA LHD 1.00 1.00 95740-J9200 g30', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x006T6J0_C2\x00\x006T6K1051\x00\x00TOS4N20NS2\x00\x00\x00\x00', + ], + }, + CAR.HYUNDAI_KONA_EV_NON_SCC: { + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x00OS IEB \x02 212 \x11\x13 58520-K4000', + ], + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00OS MDPS C 1.00 1.04 56310K4000\x00 4OEDC104', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00OSE LKAS AT USA LHD 1.00 1.00 95740-K4100 W40', + ], + }, + CAR.KIA_CEED_PHEV_2022_NON_SCC: { + (Ecu.eps, 0x7D4, None): [ + b'\xf1\x00CD MDPS C 1.00 1.01 56310-XX000 4CPHC101', + ], + (Ecu.fwdCamera, 0x7C4, None): [ + b'\xf1\x00CDH LKAS AT EUR LHD 1.00 1.01 99211-CR700 931', + ], + }, + CAR.KIA_FORTE_2019_NON_SCC: { + (Ecu.eps, 0x7D4, None): [ + b'\xf1\x00BD MDPS C 1.00 1.04 56310/M6000 4BDDC104', + ], + (Ecu.fwdCamera, 0x7C4, None): [ + b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x81616B5051\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x816VFRAF00018.ELF\xf1\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x87CXJQAM4966515JB0x\xa9\x98\x9b\x99fff\x98feg\x88\x88w\x88Ff\x8f\xff{\xff\xff\xff\xa8\xf6\xf1\x816V2C1051\x00\x00\xf1\x006V2B0_C2\x00\x006V2C1051\x00\x00CBD0N20NS8q\xc1&\xd2', + ], + }, + CAR.KIA_FORTE_2021_NON_SCC: { + (Ecu.eps, 0x7D4, None): [ + b'\xf1\x00BD MDPS C 1.00 1.08 56310M6000\x00 4BDDC108', + ], + (Ecu.fwdCamera, 0x7C4, None): [ + b'\xf1\x00BD LKAS AT USA LHD 1.00 1.04 95740-M6000 J33', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x81616B6051\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x816VFRAL00010.ELF\xf1\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x87CXLQAM0906975JB0\x89\x88\xa6\x8aVfug\xba\x87\x94yffuxgfo\xff\x8b\xff\xff\xff\x91\x82\xf1\x816V2C1051\x00\x00\xf1\x006V2B0_C2\x00\x006V2C1051\x00\x00CBD0N20NS8q\xc1&\xd2', + ], + }, + CAR.KIA_SELTOS_2023_NON_SCC: { + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x00SP ESC \t 101"\t\x01 58910-Q5510', + b'\xf1\x00SP ESC \r 100"\x04\x01 58910-Q5510', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x81616K0051\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xf1\x81616G2051\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x00SP2 MDPS C 1.00 1.04 56310Q5240 4SPSC104', + b'\xf1\x00SP2 MDPS C 1.00 1.01 56300Q5920 ', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00SP2 MFC AT USA LHD 1.00 1.03 99210-Q5500 230208', + b'\xf1\x00SP2 MFC AT AUS RHD 1.00 1.02 99210-Q5500 220624', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x006V2B0_C2\x00\x006V2D5051\x00\x00CSP2N20NL0\x00\x00\x00\x00', + b'\xf1\x006V2B0_C2\x00\x006V2D4051\x00\x00CSP2N20KL1\x00\x00\x00\x00', + ], + }, + CAR.HYUNDAI_ELANTRA_2022_NON_SCC: { + (Ecu.eps, 0x7d4, None): [ + b'\xf1\x8756310AA030\x00\xf1\x00CN7 MDPS C 1.00 1.06 56310AA030\x00 4CNDC106', + ], + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.01 99210-AB000 210205', + ], + (Ecu.abs, 0x7d1, None): [ + b'\xf1\x8758910-AB500\xf1\x00CN ESC \t 100 \x06\x01 58910-AB500', + ], + (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x87CXNQEM4091445JB3g\x98\x98\x89\x99\x87gv\x89wuwgwv\x89hD_\xffx\xff\xff\xff\x86\xeb\xf1\x89HT6VA640A1\xf1\x82CCN0N20NS5\x00\x00\x00\x00\x00\x00', + ], + (Ecu.engine, 0x7e0, None): [ + b'\xf1\x870\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\x82CNDWD0ANF3XCSG8A', + ], + }, + CAR.HYUNDAI_BAYON_1ST_GEN_NON_SCC: { + # TODO: Check working route for more FW + (Ecu.fwdCamera, 0x7c4, None): [ + b'\xf1\x00BC3 LKA AT EUR LHD 1.00 1.01 99211-Q0100 261' + ], + }, } diff --git a/selfdrive/car/hyundai/hkg_additions.py b/selfdrive/car/hyundai/hkg_additions.py new file mode 100644 index 00000000000000..a9d4a2ab817cec --- /dev/null +++ b/selfdrive/car/hyundai/hkg_additions.py @@ -0,0 +1,174 @@ +from cereal import car +from types import SimpleNamespace +from openpilot.common.realtime import DT_CTRL +from openpilot.common.numpy_fast import interp +from openpilot.common.params import Params +from openpilot.selfdrive.car.hyundai.values import CarControllerParams + + +LongCtrlState = car.CarControl.Actuators.LongControlState +class JerkLimiter: + def __init__(self): + self.using_e2e = False + # Initialize limited_accel related variables + self.brake_ramp = 0.0 + self.accel_last = 0.0 + self.accel_last_jerk = 0.0 + self.accel_raw = 0.0 + + # Initialize jerk related variables + self.jerk = 0.0 + self.jerk_count = 0.0 + self.jerk_upper_limit = 0.0 + self.jerk_lower_limit = 0.0 + + # Constants + self.DT_CTRL = DT_CTRL + self.param_s = Params() + self.hkg_tuning = self.param_s.get_bool('HKGtuning') + + + def cal_jerk(self, accel, actuators): + self.accel_raw = accel + + if actuators.longControlState in (LongCtrlState.off, LongCtrlState.stopping): + accel_diff = 0.0 + elif accel < 0: # Braking + accel_diff = (self.accel_raw - self.accel_last_jerk) * 0.8 + # Reduced jerk when transitioning from braking to acceleration + elif self.accel_last_jerk < 0 and accel > self.accel_last_jerk: + accel_diff = (self.accel_raw - self.accel_last_jerk) * 0.7 + else: + accel_diff = self.accel_raw - self.accel_last_jerk + + # Apply time-based smoothing + accel_diff /= self.DT_CTRL + self.jerk = self.jerk * 0.95 + accel_diff * 0.05 + self.accel_last_jerk = self.accel_raw + return self.jerk + + + def make_jerk(self, CS, accel, actuators): + jerk = self.cal_jerk(accel, actuators) + jerk += (accel - CS.out.aEgo) * 2.0 + mode = actuators.longControlState + + + if self.hkg_tuning: + if mode in [LongCtrlState.pid, self.using_e2e]: + # Jerk Limits + startingJerk = 0.5 + jerkLimit = 3.0 + self.jerk_count += self.DT_CTRL + jerk_max = interp(self.jerk_count, [0, 1.5, 2.5], [startingJerk, startingJerk, jerkLimit]) + + if actuators.longControlState == LongCtrlState.off: + self.jerk_upper_limit = self.jerk_lower_limit = jerkLimit + self.jerk_count = 0 + elif accel < 0: # Braking + # Progressive braking - gentler as we get closer to stop + stopping_factor = interp(CS.out.vEgo, + [0.0, 0.5, 1.0, 1.5, 2.0], + [0.2, 0.3, 0.4, 0.7, 1.0]) + + self.jerk_upper_limit = jerkLimit * 2.0 # Still allow for quick stops + self.jerk_lower_limit = min(max(jerkLimit * 0.2, jerkLimit * stopping_factor), jerkLimit) + elif CS.out.vEgo < 3.57: # Low speed acceleration (under 8-ish mph) + self.jerk_upper_limit = min(max(0.5, jerk * 2.0), jerk_max) + self.jerk_lower_limit = min(max(0.9, -jerk * 2.0), jerkLimit) + else: # Normal driving + self.jerk_upper_limit = jerkLimit * 3.0 + self.jerk_lower_limit = jerkLimit * 0.9 + else: + # Default limits + self.jerk_upper_limit = 3 if actuators.longControlState == LongCtrlState.pid else 2.0 + self.jerk_lower_limit = 3 + + return jerk + + + def calculate_limited_accel(self, accel, actuators, CS, LongCtrlState, interp, clip): + """Limit acceleration based on jerk limits""" + + self.make_jerk(CS, accel, actuators) + accel_delta = accel - self.accel_last + brake_aggressiveness = 0.0 + ramp_rate = 0.7 + + if accel < 0: + brake_ratio = clip(abs(accel / CarControllerParams.ACCEL_MIN), 0.0, 1.0) + brake_aggressiveness = brake_ratio ** 1.5 + ramp_rate = interp(brake_aggressiveness, + [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], + [0.2, 0.3, 0.4, 0.6, 0.9, 1.2]) + + if brake_ratio > 0.8: + ramp_rate *= 0.8 + + if self.accel_last >= 0: # Transitioning into braking + self.brake_ramp = 0.0 + ramp_rate *= 0.5 + + self.brake_ramp = min(1.0, self.brake_ramp + (ramp_rate * self.DT_CTRL)) + + abs_accel = clip(abs(accel), 0.0, 2.0) + smooth_factor = interp(abs_accel, + [0.0, 0.3, 0.6, 1.0, 1.5, 2.0], # breakpoints + [0.95, 0.85, 0.75, 0.65, 0.55, 0.45]) # Smoothing factors + + if accel < 0 and brake_aggressiveness > 0.8: + smooth_factor *= 0.8 + + if abs_accel < 0.5: + smooth_factor *= interp(abs_accel, + [0.0, 0.5], + [1.3, 1.0]) + + accel_delta *= (smooth_factor * self.brake_ramp) + + if abs(accel_delta) > 0.5: # For large mph changes + damping = interp(abs(accel_delta), + [0.5, 1.0, 1.5, 2.0], + [0.90, 0.80, 0.70, 0.60]) + accel_delta *= damping + + jerk_limit_factor = interp(abs(accel), + [0.0, 1.0, 2.0, 3.0], + [1.0, 0.8, 0.6, 0.5]) + limited_accel_delta =clip(accel_delta, + -self.jerk_lower_limit * self.DT_CTRL * jerk_limit_factor, + self.jerk_upper_limit * self.DT_CTRL * jerk_limit_factor) + + if accel < 0 and self.accel_last > 0: + transition_factor = interp(abs(accel), + [0, 0.5, 1.0, 1.5, 2.0], + [0.3, 0.4, 0.5, 0.6, 0.7]) + limited_accel_delta *= transition_factor + + accel = self.accel_last + limited_accel_delta + self.accel_last = accel + + return accel + + + + +class ParamManager: + def __init__(self): + self._params_list: SimpleNamespace = self._create_namespace({ + "hyundai_radar_tracks_available": False, + "hyundai_radar_tracks_available_cache": False, + }) + + @staticmethod + def _create_namespace(data: dict) -> SimpleNamespace: + return SimpleNamespace(**data) + + def get_params(self) -> SimpleNamespace: + return self._params_list + + def update(self, params: Params) -> None: + self._params_list = self._create_namespace({ + "hyundai_radar_tracks_available": params.get_bool("HyundaiRadarTracksAvailable"), + "hyundai_radar_tracks_available_cache": params.get_bool("HyundaiRadarTracksAvailableCache"), + }) diff --git a/selfdrive/car/hyundai/hyundaican.py b/selfdrive/car/hyundai/hyundaican.py index 81ff568a6ea6fd..969333e57f2576 100644 --- a/selfdrive/car/hyundai/hyundaican.py +++ b/selfdrive/car/hyundai/hyundaican.py @@ -38,7 +38,9 @@ def create_lkas11(packer, frame, CP, apply_steer, steer_req, CAR.HYUNDAI_ELANTRA_HEV_2021, CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022, CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_IONIQ_HEV_2022, CAR.HYUNDAI_SANTA_FE_HEV_2022, CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED, - CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN): + CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN, + CAR.HYUNDAI_ELANTRA_2022_NON_SCC, CAR.GENESIS_G70_2021_NON_SCC, CAR.KIA_SELTOS_2023_NON_SCC, + CAR.HYUNDAI_BAYON_1ST_GEN_NON_SCC, CAR.KIA_CEED_PHEV_2022_NON_SCC): values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1) values["CF_Lkas_LdwsOpt_USM"] = 2 @@ -126,19 +128,24 @@ def create_lfahda_mfc(packer, enabled, lat_active, hda_set_speed=0): } return packer.make_can_msg("LFAHDA_MFC", 0, values) -def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed, stopping, long_override, use_fca, cruise_available): - commands = [] +def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed, + stopping, long_override, use_fca, CS, CP, cruise_available): + + d = hud_control.leadDistance + objGap = 0 if d == 0 else 2 if d < 15 else 3 if d < 21 else 4 if d < 26 else 5 + objGap2 = 0 if objGap == 0 else 2 if hud_control.leadRelSpeed < -0.2 else 1 + commands = [] scc11_values = { "MainMode_ACC": 1 if cruise_available else 0, "TauGapSet": hud_control.leadDistanceBars, "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 + "ObjValid": 1 if hud_control.leadVisible else 0, # close lead makes controls tighter + "ACC_ObjStatus": 1 if hud_control.leadVisible else 0, # close lead makes controls tighter "ACC_ObjLatPos": 0, - "ACC_ObjRelSpd": 0, - "ACC_ObjDist": 1, # close lead makes controls tighter + "ACC_ObjRelSpd": hud_control.leadRelSpeed, + "ACC_ObjDist": int(hud_control.leadDistance), # close lead makes controls tighter } commands.append(packer.make_can_msg("SCC11", 0, scc11_values)) @@ -167,7 +174,8 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, se "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 hud_control.leadVisible else 0, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead + "ObjGap": objGap, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead + "ObjDistStat": objGap2, } commands.append(packer.make_can_msg("SCC14", 0, scc14_values)) @@ -175,19 +183,25 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, se 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 - } + if CP.flags & HyundaiFlags.CAMERA_SCC: + fca11_values = CS.fca11 + fca11_values["PAINT1_Status"] = 1 + fca11_values["FCA_DrvSetStatus"] = 1 + fca11_values["FCA_Status"] = 1 # AEB disabled, until a route with AEB or FCW trigger is verified + else: + 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): +def create_acc_opt(packer, CS, CP): commands = [] scc13_values = { @@ -198,10 +212,15 @@ def create_acc_opt(packer): 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 - } + if CP.flags & HyundaiFlags.CAMERA_SCC: + fca12_values = CS.fca12 + fca12_values["FCA_DrvSetState"] = 2 + fca12_values["FCA_USM"] = 1 # AEB disabled + else: + fca12_values = { + "FCA_DrvSetState": 2, + "FCA_USM": 1, # AEB disabled + } commands.append(packer.make_can_msg("FCA12", 0, fca12_values)) return commands diff --git a/selfdrive/car/hyundai/hyundaicanfd.py b/selfdrive/car/hyundai/hyundaicanfd.py index c26c42c02d45f9..8448e9037c22dd 100644 --- a/selfdrive/car/hyundai/hyundaicanfd.py +++ b/selfdrive/car/hyundai/hyundaicanfd.py @@ -1,5 +1,6 @@ from openpilot.common.numpy_fast import clip from openpilot.selfdrive.car import CanBusBase +from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car.hyundai.values import HyundaiFlags @@ -120,8 +121,118 @@ def create_lfahda_cluster(packer, CAN, enabled, lat_active): } return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values) +def create_msg_161(packer, CAN, enabled, msg_161, car_params, hud_control, car_state, car_control, frame): + values = msg_161.copy() + + # HIDE ALERTS + if values.get("MUTE") == 3: + values["MUTE"] = 0 + if values.get("ALERTS_5") == 5: # USE SWITCH OR PEDAL TO ACCELERATE + values["ALERTS_5"] = 0 + if values.get("ALERTS_2") == 5: # CONSIDER TAKING A BREAK + values.update({"ALERTS_2": 0, "SOUNDS_2": 0, "DAW_ICON": 0}) + if values.get("ALERTS_3") == 4: + values["ALERTS_3"] = 0 + if values.get("ALERTS_4") == 9: # SMART CRUISE CONTROL CONDITIONS NOT MET + values["ALERTS_4"] = 0 + if values.get("ALERTS_1") == 6: + values["ALERTS_1"] = 0 + values["LKA_WARNING"] = 0 + # LANELINES + curvature = { + i: (31 if i == -1 else 13 - abs(i + 15)) if i < 0 else 15 + i + for i in range(-15, 16) + } + values.update({ + "LANELINE_CURVATURE": curvature.get(max(-15, min(int(car_state.out.steeringAngleDeg / 3), 15)), 14) if enabled else 15, + "LFA_ICON": 2 if enabled else 0, + "LKA_ICON": 4 if enabled else 0, + "LANELINE_LEFT": 2 if enabled else 0, + "LANELINE_RIGHT": 2 if enabled else 0, + "CENTERLINE": 1 if enabled else 0, + }) + + # LCA + if enabled: + speed_below_threshold = car_state.out.vEgo < 8.94 + values.update({ + "LCA_LEFT_ICON": 0 if car_state.out.leftBlindspot or speed_below_threshold else 2 if car_control.leftBlinker else 1, + "LCA_RIGHT_ICON": 0 if car_state.out.rightBlindspot or speed_below_threshold else 2 if car_control.rightBlinker else 1, + "LCA_LEFT_ARROW": 2 if car_control.leftBlinker else 0, + "LCA_RIGHT_ARROW": 2 if car_control.rightBlinker else 0, + }) + + # LANE DEPARTURE + if hud_control.leftLaneDepart: + values["LANELINE_LEFT"] = 4 if (frame // 50) % 2 == 0 else 1 + if hud_control.rightLaneDepart: + values["LANELINE_RIGHT"] = 4 if (frame // 50) % 2 == 0 else 1 + + if car_params.openpilotLongitudinalControl: + # HIDE ALERTS + if values.get("ALERTS_5") == 4: # SMART CRUISE CONTROL CONDITIONS NOT MET + values["ALERTS_5"] = 0 + + # SETSPEED + values["SETSPEED"] = 3 if enabled else 1 + values["SETSPEED_HUD"] = 2 if enabled else 1 + values["SETSPEED_SPEED"] = 25 if (s := round(car_state.out.vCruiseCluster * CV.KPH_TO_MPH)) > 100 else s + + # DISTANCE + if 1 <= hud_control.leadDistanceBars <= 3: + values["DISTANCE"] = hud_control.leadDistanceBars + values["DISTANCE_SPACING"] = 1 if enabled else 0 + values["DISTANCE_LEAD"] = 2 if enabled and hud_control.leadVisible else 1 if enabled else 0 + values["DISTANCE_CAR"] = 2 if enabled else 1 + values["ALERTS_3"] = hud_control.leadDistanceBars + 6 + else: + values["DISTANCE"] = 0 + values["DISTANCE_SPACING"] = 0 + values["DISTANCE_LEAD"] = 0 + values["DISTANCE_CAR"] = 0 + + # BACKGROUND + values["BACKGROUND"] = 1 if enabled else 7 + + return packer.make_can_msg("MSG_161", CAN.ECAN, values) + +def create_msg_162(packer, CAN, enabled, msg_162, car_params, hud_control, car_state): + values = msg_162.copy() + TURN_ANGLE_THRESHOLD = 85 + SPEED_THRESHOLD = 8.33 + # HIDE FAULTS + values.update({ + "LKA_FAULT": 0, + "FAULT_FSS": 0, # Forward Safety System + "FAULT_LSS": 0, + "FAULT_SLA": 0, # Speed Limit Assist + "FAULT_LFA": 0, # Hide LFA errors + "FAULT_HDA": 0, + "FAULT_LCA": 0, # Lane Change Assist + "FAULT_HDP": 0, # Highway Driving Pilot + "FAULT_SCC": 0, # Smart Cruise Control + "FAULT_DAS": 0 if (abs(car_state.out.steeringAngleDeg) < TURN_ANGLE_THRESHOLD or car_state.out.vEgo < SPEED_THRESHOLD) else values.get("FAULT_DAS", 0), + }) -def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control): + values["LKA_WARNING"] = 0 + # LANE DEPARTURE + if hud_control.leftLaneDepart or hud_control.rightLaneDepart: + values["VIBRATE"] = 1 + + if car_params.openpilotLongitudinalControl: + # *** TODO *** LEAD_DISTANCE/LEAD_LATERAL + # LEAD + if hud_control.leadVisible: + values["LEAD"] = 2 if enabled else 1 + values["LEAD_DISTANCE"] = 100 + else: + values["LEAD"] = 0 + values["LEAD_DISTANCE"] = 0 + + return packer.make_can_msg("MSG_162", CAN.ECAN, values) + + +def create_acc_control(packer, CAN, CS, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control): jerk = 5 jn = jerk / 50 if not enabled or gas_override: @@ -132,7 +243,7 @@ def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_ov values = { "ACCMode": 0 if not enabled else (2 if gas_override else 1), - "MainMode_ACC": 1, + "MainMode_ACC": 1 if CS.out.cruiseState.available else 0, "StopReq": 1 if stopping else 0, "aReqValue": a_val, "aReqRaw": a_raw, @@ -171,6 +282,18 @@ def create_spas_messages(packer, CAN, frame, left_blink, right_blink): return ret +def create_fca_warning_light(packer, CAN, frame): + ret = [] + 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)) + return ret def create_adrv_messages(packer, CAN, frame): # messages needed to car happy after disabling @@ -182,6 +305,8 @@ def create_adrv_messages(packer, CAN, frame): } ret.append(packer.make_can_msg("ADRV_0x51", CAN.ACAN, values)) + ret.extend(create_fca_warning_light(packer, CAN, frame)) + if frame % 2 == 0: values = { 'AEB_SETTING': 0x1, # show AEB disabled icon @@ -204,6 +329,7 @@ def create_adrv_messages(packer, CAN, frame): values = { 'SET_ME_E1': 0xe1, 'SET_ME_3A': 0x3a, + 'TauGapSet' : 1, } ret.append(packer.make_can_msg("ADRV_0x200", CAN.ECAN, values)) diff --git a/selfdrive/car/hyundai/interface.py b/selfdrive/car/hyundai/interface.py index 7ae1679bfd0e34..be1802c31d363b 100644 --- a/selfdrive/car/hyundai/interface.py +++ b/selfdrive/car/hyundai/interface.py @@ -1,15 +1,20 @@ +import cereal.messaging as messaging from cereal import car, custom from panda import Panda +from openpilot.common.params import Params +from openpilot.common.numpy_fast import interp +from openpilot.selfdrive.car.hyundai.fingerprinting import can_fingerprint, get_one_can +from openpilot.selfdrive.car.hyundai.enable_radar_tracks import enable_radar_tracks 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, \ +from openpilot.selfdrive.car.hyundai.values import HyundaiFlags, HyundaiFlagsFP, CAR, DBC, CANFD_CAR, CAMERA_SCC_CAR, CANFD_RADAR_SCC_CAR, \ + CANFD_UNSUPPORTED_LONGITUDINAL_CAR, NON_SCC_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.selfdrive.frogpilot.frogpilot_variables import params +from openpilot.selfdrive.frogpilot.frogpilot_variables import params, CITY_SPEED_LIMIT Ecu = car.CarParams.Ecu ButtonType = car.CarState.ButtonEvent.Type @@ -78,7 +83,7 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp ret.flags |= HyundaiFlags.USE_FCA.value if 0x53E in fingerprint[2]: - ret.flags |= HyundaiFlags.LKAS12.value + ret.fpFlags |= HyundaiFlagsFP.FP_LKAS12.value ret.steerActuatorDelay = 0.1 # Default delay ret.steerLimitTimer = 0.4 @@ -88,28 +93,80 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp ret.steerActuatorDelay = 0.2 # *** longitudinal control *** - if candidate in CANFD_CAR: - if not use_new_api: - ret.longitudinalTuning.deadzoneBP = [0.] - ret.longitudinalTuning.deadzoneV = [0.] - ret.longitudinalTuning.kpV = [0.1] - ret.longitudinalTuning.kiV = [0.0] - ret.experimentalLongitudinalAvailable = candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR) + # Determine if candidate is in CANFD_CAR + is_canfd_car = candidate in CANFD_CAR + + # Set FP_CAMERA_SCC_LEAD flag if applicable + if (is_canfd_car and ret.flags & HyundaiFlags.CANFD_CAMERA_SCC and not hda2) or \ + (not is_canfd_car and candidate in CAMERA_SCC_CAR): + ret.fpFlags |= HyundaiFlagsFP.FP_CAMERA_SCC_LEAD.value + + # Configure longitudinal tuning + hkg_tuning = params.get_bool("HKGtuning") + hat_trick = params.get_bool("HatTrick") + ret.longitudinalTuning.deadzoneBP = [0.0] + ret.longitudinalTuning.deadzoneV = [0.0] + ret.longitudinalTuning.kpBP = [0.0] + ret.longitudinalTuning.kiBP = [0.0] + + # HKG tuning without hat trick + if hkg_tuning and not hat_trick: + ret.longitudinalTuning.kiV = [0.0] + ret.vEgoStopping = 0.20 + ret.vEgoStarting = 0.10 + ret.longitudinalActuatorDelay = 0.5 + + if ret.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV): + ret.startingState = False + else: + ret.startingState = True + ret.startAccel = 1.6 + + ret.longitudinalTuning.kpV = [0.5] if is_canfd_car else [0.5] + if Params().get_bool("HyundaiRadarTracksAvailable"): + ret.stoppingDecelRate = 0.01 # Lower decel rate when we have working Mando radar tracks + else: + ret.stoppingDecelRate = 0.05 # Default decel rate + + # HKG tuning with hat trick or just hat trick + elif (hkg_tuning and hat_trick) or hat_trick: + ret.vEgoStopping = 0.50 + ret.vEgoStarting = 0.10 + ret.longitudinalActuatorDelay = 0.1 + ret.startAccel = 2.0 + + ret.startingState = not bool(ret.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV)) + ret.longitudinalTuning.kpV = [1.5] if is_canfd_car else [1.5] + ret.stoppingDecelRate = 0.05 + + # Default tuning else: - if not use_new_api: - ret.longitudinalTuning.deadzoneBP = [0.] - ret.longitudinalTuning.deadzoneV = [0.] - ret.longitudinalTuning.kpV = [0.5] - ret.longitudinalTuning.kiV = [0.0] - ret.experimentalLongitudinalAvailable = candidate not in (UNSUPPORTED_LONGITUDINAL_CAR | CAMERA_SCC_CAR) + ret.longitudinalTuning.kpV = [0.1] if is_canfd_car else [0.5] + ret.longitudinalTuning.kiV = [0.0] + + # API-specific tuning + if use_new_api: + ret.longitudinalTuning.kiBP = [0.0] + ret.longitudinalTuning.kiV = [0.0] + if Params().get_bool("HyundaiRadarTracksAvailable"): + ret.stoppingDecelRate = 0.01 + else: + ret.stoppingDecelRate = 0.05 + + # Determine experimental longitudinal availability + unsupported_long_cars = ( + CANFD_UNSUPPORTED_LONGITUDINAL_CAR | NON_SCC_CAR if is_canfd_car else NON_SCC_CAR | UNSUPPORTED_LONGITUDINAL_CAR) + ret.experimentalLongitudinalAvailable = candidate not in unsupported_long_cars + + # Configure longitudinal control flags 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.longitudinalActuatorDelay = 0.5 + + # Configure radar settings + if DBC[ret.carFingerprint]["radar"] is None and ret.fpFlags & HyundaiFlagsFP.FP_CAMERA_SCC_LEAD.value: + #ret.radarTimeStep = 0.02 + ret.radarUnavailable = False # *** feature detection *** if candidate in CANFD_CAR: @@ -123,6 +180,11 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp if 0x544 in fingerprint[0]: ret.flags |= HyundaiFlags.NAV_MSG.value + if ret.flags & HyundaiFlags.MANDO_RADAR and ret.radarUnavailable: + ret.fpFlags |= HyundaiFlagsFP.FP_RADAR_TRACKS.value + if Params().get_bool("HyundaiRadarTracksAvailable"): + ret.radarUnavailable = False + # *** panda safety config *** if candidate in CANFD_CAR: cfgs = [get_safety_config(car.CarParams.SafetyModel.hyundaiCanfd), ] @@ -151,7 +213,8 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp if 0x391 in fingerprint[0]: ret.flags |= HyundaiFlags.CAN_LFA_BTN.value ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_LFA_BTN - + if candidate in NON_SCC_CAR: + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_HYUNDAI_NON_SCC if ret.openpilotLongitudinalControl: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_LONG if ret.flags & HyundaiFlags.HYBRID: @@ -159,7 +222,8 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp elif ret.flags & HyundaiFlags.EV: ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_EV_GAS - if candidate in (CAR.HYUNDAI_KONA, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022): + if candidate in (CAR.HYUNDAI_KONA, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022, + CAR.HYUNDAI_KONA_NON_SCC, CAR.HYUNDAI_KONA_EV_NON_SCC): ret.flags |= HyundaiFlags.ALT_LIMITS.value ret.safetyConfigs[-1].safetyParam |= Panda.FLAG_HYUNDAI_ALT_LIMITS @@ -171,10 +235,47 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp return ret + #Initialize radar tracks if enabled in FrogPilot Variables. + @staticmethod + def initialize_radar_tracks(CP, logcan, sendcan): + if not (CP.fpFlags & HyundaiFlagsFP.FP_RADAR_TRACKS): + return + + params = Params() + if not params.get_bool("HyundaiRadarTracks"): + return + + # Enable radar tracks config + enable_radar_tracks(logcan, sendcan, + bus=0, + addr=0x7d0, + config_data_id=b'\x01\x42') + + # Handle radar tracks availability status + CarInterface.update_radar_tracks_availability(params, logcan, CP) + + @staticmethod + def update_radar_tracks_availability(params, logcan, CP): + rt_avail = params.get_bool("HyundaiRadarTracksAvailable") + rt_avail_persist = params.get_bool("HyundaiRadarTracksAvailablePersistent") + + # Cache current availability + params.put_bool_nonblocking("HyundaiRadarTracksAvailableCache", rt_avail) + + # Only update persistent status if not already set + if not rt_avail_persist: + messaging.drain_sock_raw(logcan) + fingerprint = can_fingerprint(lambda: get_one_can(logcan)) + radar_unavailable = RADAR_START_ADDR not in fingerprint[1] or DBC[CP.carFingerprint]["radar"] is None + + params.put_bool_nonblocking("HyundaiRadarTracksAvailable", not radar_unavailable) + params.put_bool_nonblocking("HyundaiRadarTracksAvailablePersistent", True) + @staticmethod def init(CP, logcan, sendcan): - if CP.openpilotLongitudinalControl and not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value): - addr, bus = 0x7d0, 0 + CarInterface.initialize_radar_tracks(CP, logcan, sendcan) + if CP.openpilotLongitudinalControl and not (CP.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)): + addr, bus = 0x7d0, CanBus(CP).ECAN if CP.carFingerprint in CANFD_CAR else 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') @@ -191,6 +292,10 @@ def _update(self, c, frogpilot_toggles): *create_button_events(self.CS.cruise_buttons[-1], self.CS.prev_cruise_buttons, BUTTONS_DICT), *create_button_events(self.CS.lkas_enabled, self.CS.lkas_previously_enabled, {1: FrogPilotButtonType.lkas}), ] + else: + ret.buttonEvents = create_button_events(self.CS.lkas_enabled, self.CS.lkas_previously_enabled, {1: FrogPilotButtonType.lkas}) + + # 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 @@ -207,6 +312,10 @@ def _update(self, c, frogpilot_toggles): if self.low_speed_alert: events.add(car.CarEvent.EventName.belowSteerSpeed) + if frogpilot_toggles.hyundai_radar_tracks and self.CS.params_list.hyundai_radar_tracks_available \ + and not self.CS.params_list.hyundai_radar_tracks_available_cache: + events.add(car.CarEvent.EventName.hyundaiRadarTracksAvailable) + ret.events = events.to_msg() return ret, fp_ret diff --git a/selfdrive/car/hyundai/radar_interface.py b/selfdrive/car/hyundai/radar_interface.py index 52600509860f5a..b953c9612d9eab 100644 --- a/selfdrive/car/hyundai/radar_interface.py +++ b/selfdrive/car/hyundai/radar_interface.py @@ -3,7 +3,8 @@ 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 +from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus +from openpilot.selfdrive.car.hyundai.values import DBC, HyundaiFlagsFP, CANFD_CAR RADAR_START_ADDR = 0x500 RADAR_MSG_COUNT = 32 @@ -12,7 +13,18 @@ def get_radar_can_parser(CP): if DBC[CP.carFingerprint]['radar'] is None: - return None + if CP.carFingerprint in CANFD_CAR: + if CP.fpFlags & HyundaiFlagsFP.FP_CAMERA_SCC_LEAD: + lead_src, bus = "SCC_CONTROL", CanBus(CP).CAM + else: + return None + else: + if CP.fpFlags & HyundaiFlagsFP.FP_CAMERA_SCC_LEAD: + lead_src, bus = "SCC11", 2 + else: + return None + messages = [(lead_src, 50)] + return CANParser(DBC[CP.carFingerprint]['pt'], messages, bus) 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) @@ -21,13 +33,19 @@ def get_radar_can_parser(CP): class RadarInterface(RadarInterfaceBase): def __init__(self, CP): super().__init__(CP) + self.CP = CP + self.camera_scc = CP.fpFlags & HyundaiFlagsFP.FP_CAMERA_SCC_LEAD self.updated_messages = set() - self.trigger_msg = RADAR_START_ADDR + RADAR_MSG_COUNT - 1 + self.trigger_msg = 0x1A0 if self.camera_scc and CP.carFingerprint in CANFD_CAR else \ + 0x420 if self.camera_scc else \ + (RADAR_START_ADDR + RADAR_MSG_COUNT - 1) self.track_id = 0 self.radar_off_can = CP.radarUnavailable self.rcp = get_radar_can_parser(CP) + self.fp_radar_tracks = CP.fpFlags & HyundaiFlagsFP.FP_RADAR_TRACKS + def update(self, can_strings): if self.radar_off_can or (self.rcp is None): return super().update(None) @@ -41,6 +59,11 @@ def update(self, can_strings): rr = self._update(self.updated_messages) self.updated_messages.clear() + radar_error = [] + if rr is not None: + radar_error = rr.errors + if list(radar_error) and self.fp_radar_tracks: + return super().update(can_strings) return rr def _update(self, updated_messages): @@ -54,26 +77,49 @@ def _update(self, updated_messages): 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] + if self.camera_scc: + msg_src = "SCC_CONTROL" if self.CP.carFingerprint in CANFD_CAR else \ + "SCC11" + msg = self.rcp.vl[msg_src] + valid = msg['ACC_ObjDist'] < 240 if self.CP.carFingerprint in CANFD_CAR else \ + msg['ACC_ObjStatus'] + for ii in range(1): + if valid: + if ii not in self.pts: + self.pts[ii] = car.RadarData.RadarPoint.new_message() + self.pts[ii].trackId = self.track_id + self.track_id += 1 + self.pts[ii].measured = True + self.pts[ii].dRel = msg['ACC_ObjDist'] * 1.1 + self.pts[ii].yRel = float('nan') + self.pts[ii].vRel = msg['ACC_ObjRelSpd'] + self.pts[ii].aRel = float('nan') + self.pts[ii].yvRel = float('nan') + + else: + if ii in self.pts: + del self.pts[ii] + else: + 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 diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index df4079449344b0..ec723a2ce56b7e 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -78,6 +78,7 @@ class HyundaiFlags(IntFlag): # The radar does SCC on these cars when HDA I, rather than the camera RADAR_SCC = 2 ** 14 + # The camera does SCC on these cars, rather than the radar CAMERA_SCC = 2 ** 15 CHECKSUM_CRC8 = 2 ** 16 CHECKSUM_6B = 2 ** 17 @@ -88,6 +89,8 @@ class HyundaiFlags(IntFlag): # these cars have not been verified to work with longitudinal yet - radar disable, sending correct messages, etc. UNSUPPORTED_LONGITUDINAL = 2 ** 19 + # These CAN FD cars do not accept communication control to disable the ADAS ECU, + # responds with 0x7F2822 - 'conditions not correct' CANFD_NO_RADAR_DISABLE = 2 ** 20 CLUSTER_GEARS = 2 ** 21 @@ -100,6 +103,16 @@ class HyundaiFlags(IntFlag): LKAS12 = 2 ** 25 NAV_MSG = 2 ** 26 + +class HyundaiFlagsFP(IntFlag): + FP_CAMERA_SCC_LEAD = 2 ** 2 + FP_LKAS12 = 2 ** 3 + FP_RADAR_TRACKS = 2 ** 4 + FP_NON_SCC = 2 ** 3 + FP_NON_SCC_FCA = 2 ** 4 + FP_NON_SCC_RADAR_FCA = 2 ** 5 + + class Footnote(Enum): CANFD = CarFootnote( "Requires a CAN FD panda kit if not using " + @@ -162,7 +175,7 @@ class CAR(Platforms): ) HYUNDAI_ELANTRA_GT_I30 = HyundaiPlatformConfig( [ - HyundaiCarDocs("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), + HyundaiCarDocs("Hyundai Elantra GT 2017-20", car_parts=CarParts.common([CarHarness.hyundai_e])), HyundaiCarDocs("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), ], HYUNDAI_ELANTRA.specs, @@ -201,7 +214,7 @@ class CAR(Platforms): HYUNDAI_IONIQ_EV_LTD = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Ioniq Electric 2019", car_parts=CarParts.common([CarHarness.hyundai_c]))], CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385), - flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.EV | HyundaiFlags.LEGACY | HyundaiFlags.MIN_STEER_32_MPH, + flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.EV | HyundaiFlags.MIN_STEER_32_MPH, ) HYUNDAI_IONIQ_EV_2020 = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Ioniq Electric 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_h]))], @@ -221,17 +234,17 @@ class CAR(Platforms): HYUNDAI_KONA = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Kona 2020", min_enable_speed=6 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b]))], CarSpecs(mass=1275, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385), - flags=HyundaiFlags.CLUSTER_GEARS, + flags=HyundaiFlags.CLUSTER_GEARS | HyundaiFlags.ALT_LIMITS, ) HYUNDAI_KONA_EV = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g]))], CarSpecs(mass=1685, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385), - flags=HyundaiFlags.EV, + flags=HyundaiFlags.EV | HyundaiFlags.ALT_LIMITS, ) HYUNDAI_KONA_EV_2022 = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Kona Electric 2022-23", car_parts=CarParts.common([CarHarness.hyundai_o]))], CarSpecs(mass=1743, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385), - flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.EV, + flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.EV | HyundaiFlags.ALT_LIMITS, ) HYUNDAI_KONA_EV_2ND_GEN = HyundaiCanFDPlatformConfig( [HyundaiCarDocs("Hyundai Kona Electric (with HDA II, Korea only) 2023", video_link="https://www.youtube.com/watch?v=U2fOCmcQ8hw", @@ -242,7 +255,7 @@ class CAR(Platforms): HYUNDAI_KONA_HEV = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Kona Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_i]))], # TODO: check packages, CarSpecs(mass=1425, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385), - flags=HyundaiFlags.HYBRID, + flags=HyundaiFlags.HYBRID | HyundaiFlags.ALT_LIMITS, ) HYUNDAI_SANTA_FE = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Santa Fe 2019-20", "All", video_link="https://youtu.be/bjDR0YjM__s", @@ -266,6 +279,11 @@ class CAR(Platforms): HYUNDAI_SANTA_FE.specs, flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID, ) + HYUNDAI_SONATA_2019 = HyundaiPlatformConfig( + [HyundaiCarDocs("Hyundai Sonata 2019", car_parts=CarParts.common([CarHarness.hyundai_e]))], + CarSpecs(mass=1536, wheelbase=2.804, steerRatio=15.26), + flags=HyundaiFlags.TCU_GEARS, + ) HYUNDAI_SONATA = HyundaiPlatformConfig( [HyundaiCarDocs("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", car_parts=CarParts.common([CarHarness.hyundai_a]))], @@ -310,7 +328,7 @@ class CAR(Platforms): ) HYUNDAI_IONIQ_5 = HyundaiCanFDPlatformConfig( [ - HyundaiCarDocs("Hyundai Ioniq 5 (Non-US only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_q])), + HyundaiCarDocs("Hyundai Ioniq 5 (Southeast Asia and Europe only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_q])), HyundaiCarDocs("Hyundai Ioniq 5 (without HDA II) 2022-24", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), HyundaiCarDocs("Hyundai Ioniq 5 (with HDA II) 2022-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), ], @@ -322,6 +340,11 @@ class CAR(Platforms): HYUNDAI_IONIQ_5.specs, flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE, ) + HYUNDAI_IONIQ_6_2025 = HyundaiCanFDPlatformConfig( + [HyundaiCarDocs("Hyundai Ioniq 6 (with HDA II) 2025", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))], + CarSpecs(mass=2084, wheelbase=2.97, steerRatio=14.26, tireStiffnessFactor=0.65), + flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE, + ) HYUNDAI_TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig( [ HyundaiCarDocs("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])), @@ -355,6 +378,11 @@ class CAR(Platforms): CarSpecs(mass=3381 * CV.LB_TO_KG, wheelbase=2.85, steerRatio=13.27, tireStiffnessFactor=0.5), # 2021 Kia K5 Steering Ratio (all trims) flags=HyundaiFlags.CHECKSUM_CRC8, ) + KIA_K5_2025 = HyundaiCanFDPlatformConfig( + [HyundaiCarDocs("Kia K5 2025", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_m]))], + # https://www.kiamedia.com/us/en/models/k5/2025/specifications + CarSpecs(mass=3230 * CV.LB_TO_KG, wheelbase=2.85, steerRatio=13.27), + ) KIA_K5_HEV_2020 = HyundaiPlatformConfig( [HyundaiCarDocs("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a]))], KIA_K5_2021.specs, @@ -394,8 +422,8 @@ class CAR(Platforms): HyundaiCarDocs("Kia Niro Plug-in Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_d])), HyundaiCarDocs("Kia Niro Plug-in Hybrid 2022", car_parts=CarParts.common([CarHarness.hyundai_f])), ], - KIA_NIRO_EV.specs, - flags=HyundaiFlags.HYBRID | HyundaiFlags.MANDO_RADAR, + CarSpecs(mass=3543 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=13.6, tireStiffnessFactor=0.385), # average of all the cars + flags= HyundaiFlags.MANDO_RADAR | HyundaiFlags.HYBRID, ) KIA_NIRO_HEV_2021 = HyundaiPlatformConfig( [ @@ -482,7 +510,7 @@ class CAR(Platforms): ) KIA_EV6 = HyundaiCanFDPlatformConfig( [ - HyundaiCarDocs("Kia EV6 (Non-US only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])), + HyundaiCarDocs("Kia EV6 (Southeast Asia only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])), HyundaiCarDocs("Kia EV6 (without HDA II) 2022-24", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])), HyundaiCarDocs("Kia EV6 (with HDA II) 2022-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])) ], @@ -502,9 +530,9 @@ class CAR(Platforms): GENESIS_GV60_EV_1ST_GEN = HyundaiCanFDPlatformConfig( [ HyundaiCarDocs("Genesis GV60 (Advanced Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), - HyundaiCarDocs("Genesis GV60 (Performance Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + HyundaiCarDocs("Genesis GV60 (Performance Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), ], - CarSpecs(mass=2205, wheelbase=2.9, steerRatio=12.6), # steerRatio: https://www.motor1.com/reviews/586376/2023-genesis-gv60-first-drive/#:~:text=Relative%20to%20the%20related%20Ioniq,5%2FEV6%27s%2014.3%3A1. + CarSpecs(mass=2205, wheelbase=2.9, steerRatio=17.6), flags=HyundaiFlags.EV, ) GENESIS_G70 = HyundaiPlatformConfig( @@ -524,8 +552,9 @@ class CAR(Platforms): ) GENESIS_GV70_1ST_GEN = HyundaiCanFDPlatformConfig( [ - HyundaiCarDocs("Genesis GV70 (2.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), - HyundaiCarDocs("Genesis GV70 (3.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_m])), + # TODO: Hyundai P is likely the correct harness for HDA II for 2.5T (unsupported due to missing ADAS ECU, is that the radar?) + HyundaiCarDocs("Genesis GV70 (2.5T Trim, without HDA II) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), + HyundaiCarDocs("Genesis GV70 (3.5T Trim, without HDA II) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_m])), ], CarSpecs(mass=1950, wheelbase=2.87, steerRatio=14.6), flags=HyundaiFlags.RADAR_SCC, @@ -535,6 +564,10 @@ class CAR(Platforms): CarSpecs(mass=2060, wheelbase=3.01, steerRatio=16.5), flags=HyundaiFlags.LEGACY, ) + GENESIS_G80_2ND_GEN_FL = HyundaiCanFDPlatformConfig( + [HyundaiCarDocs("Genesis G80 (2.5T Advanced Trim, with HDA II) 2024", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))], + CarSpecs(mass=2060, wheelbase=3.00, steerRatio=14.0), + ) GENESIS_G90 = HyundaiPlatformConfig( [HyundaiCarDocs("Genesis G90 2017-20", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))], CarSpecs(mass=2200, wheelbase=3.15, steerRatio=12.069), @@ -545,6 +578,60 @@ class CAR(Platforms): flags=HyundaiFlags.RADAR_SCC, ) + # Non-SCC Cars + HYUNDAI_BAYON_1ST_GEN_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Hyundai Bayon Non-SCC 2021", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_n]))], + CarSpecs(mass=1150, wheelbase=2.58, steerRatio=13.27 * 1.15), + flags=HyundaiFlags.CHECKSUM_CRC8, + fpFlags=HyundaiFlagsFP.FP_NON_SCC | HyundaiFlagsFP.FP_NON_SCC_FCA, + ) + HYUNDAI_ELANTRA_2022_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Hyundai Elantra Non-SCC 2022", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_k]))], + HYUNDAI_ELANTRA_2021.specs, + flags=HyundaiFlags.CHECKSUM_CRC8, + fpFlags=HyundaiFlagsFP.FP_NON_SCC | HyundaiFlagsFP.FP_NON_SCC_FCA, + ) + HYUNDAI_KONA_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Hyundai Kona Non-SCC 2019", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_b]))], + HYUNDAI_KONA.specs, + fpFlags=HyundaiFlagsFP.FP_NON_SCC | HyundaiFlagsFP.FP_NON_SCC_FCA, + ) + HYUNDAI_KONA_EV_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Hyundai Kona Electric Non-SCC 2019", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_g]))], + HYUNDAI_KONA.specs, + flags=HyundaiFlags.EV, + fpFlags=HyundaiFlagsFP.FP_NON_SCC | HyundaiFlagsFP.FP_NON_SCC_FCA, + ) + KIA_CEED_PHEV_2022_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Kia Ceed PHEV Non-SCC 2022", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_i]))], + CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5), + flags=HyundaiFlags.HYBRID, + fpFlags=HyundaiFlagsFP.FP_NON_SCC | HyundaiFlagsFP.FP_NON_SCC_FCA, + ) + KIA_FORTE_2019_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Kia Forte Non-SCC 2019", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_g]))], + KIA_FORTE.specs, + fpFlags=HyundaiFlagsFP.FP_NON_SCC, + ) + KIA_FORTE_2021_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Kia Forte Non-SCC 2021", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_g]))], + KIA_FORTE.specs, + fpFlags=HyundaiFlagsFP.FP_NON_SCC | HyundaiFlagsFP.FP_NON_SCC_FCA, + ) + KIA_SELTOS_2023_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Kia Seltos Non-SCC 2023-24", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_l]))], + KIA_SELTOS.specs, + flags=HyundaiFlags.CHECKSUM_CRC8, + fpFlags=HyundaiFlagsFP.FP_NON_SCC | HyundaiFlagsFP.FP_NON_SCC_FCA, + ) + GENESIS_G70_2021_NON_SCC = HyundaiPlatformConfig( + [HyundaiCarDocs("Genesis G70 Non-SCC 2021", "No Smart Cruise Control (SCC)", car_parts=CarParts.common([CarHarness.hyundai_f]))], + GENESIS_G70_2020.specs, + flags=HyundaiFlags.CHECKSUM_CRC8, + fpFlags=HyundaiFlagsFP.FP_NON_SCC | HyundaiFlagsFP.FP_NON_SCC_FCA | HyundaiFlagsFP.FP_NON_SCC_RADAR_FCA, + ) + + class Buttons: NONE = 0 @@ -716,7 +803,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str (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), + (Ecu.cornerRadar, 0x7b7, None), # Corner radar (Ecu.combinationMeter, 0x7c6, None), # CAN FD Instrument cluster ], # Custom fuzzy fingerprinting function using platform codes, part numbers + FW dates: @@ -735,7 +822,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str } CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD) -CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) +CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR # These CAN FD cars do not accept communication control to disable the ADAS ECU, # responds with 0x7F2822 - 'conditions not correct' @@ -750,6 +837,12 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str LEGACY_SAFETY_MODE_CAR = CAR.with_flags(HyundaiFlags.LEGACY) +# TODO: another PR with (HyundaiFlags.LEGACY | HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.CAMERA_SCC | +# HyundaiFlags.CANFD_RADAR_SCC | HyundaiFlags.CANFD_NO_RADAR_DISABLE | ) UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL) +NON_SCC_CAR = CAR.with_fp_flags(HyundaiFlagsFP.FP_NON_SCC) +NON_SCC_FCA_CAR = CAR.with_fp_flags(HyundaiFlagsFP.FP_NON_SCC_FCA) +NON_SCC_RADAR_FCA_CAR = CAR.with_fp_flags(HyundaiFlagsFP.FP_NON_SCC_RADAR_FCA) + DBC = CAR.create_dbc_map() diff --git a/selfdrive/car/interfaces.py b/selfdrive/car/interfaces.py index 4871ff5a426171..9c06d607900076 100644 --- a/selfdrive/car/interfaces.py +++ b/selfdrive/car/interfaces.py @@ -5,6 +5,7 @@ from abc import abstractmethod, ABC from difflib import SequenceMatcher from enum import StrEnum +from types import SimpleNamespace from typing import Any, NamedTuple from collections.abc import Callable from functools import cache @@ -14,8 +15,10 @@ from openpilot.common.conversions import Conversions as CV from openpilot.common.simple_kalman import KF1D, get_kalman_gain from openpilot.common.numpy_fast import clip +from openpilot.common.params import Params from openpilot.common.realtime import DT_CTRL from openpilot.selfdrive.car import apply_hysteresis, gen_empty_fingerprint, scale_rot_inertia, scale_tire_stiffness, STD_CARGO_KG +from openpilot.selfdrive.car.hyundai.hkg_additions import ParamManager from openpilot.selfdrive.car.values import PLATFORMS from openpilot.selfdrive.controls.lib.drive_helpers import CRUISE_LONG_PRESS, V_CRUISE_MAX, get_friction from openpilot.selfdrive.controls.lib.events import Events @@ -223,6 +226,7 @@ def __init__(self, CP, CarController, CarState): dbc_name = "" if self.cp is None else self.cp.dbc_name self.CC: CarControllerBase = CarController(dbc_name, CP, self.VM) + self.param_s = Params() # FrogPilot variables self.frogpilot_toggles = get_frogpilot_toggles() @@ -234,7 +238,7 @@ def __init__(self, CP, CarController, CarState): self.use_nnff = not comma_nnff_supported and nnff_supported and self.frogpilot_toggles.nnff self.use_nnff_lite = not self.use_nnff and self.frogpilot_toggles.nnff_lite - self.always_on_lateral_disabled = False + self.always_on_lateral_enabled = False self.belowSteerSpeed_shown = False self.disable_belowSteerSpeed = False self.disable_resumeRequired = False @@ -286,6 +290,7 @@ def get_params(cls, candidate: str, fingerprint: dict[int, dict[int, int]], car_ ret.minSteerSpeed = platform.config.specs.minSteerSpeed ret.tireStiffnessFactor = platform.config.specs.tireStiffnessFactor ret.flags |= int(platform.config.flags) + ret.fpFlags |= int(platform.config.fpFlags) ret = cls._get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, experimental_long, docs) @@ -387,12 +392,14 @@ def configure_torque_tune(candidate, tune, steering_angle_deadzone_deg=0.0, use_ def _update(self, c: car.CarControl) -> car.CarState: pass - def update(self, c: car.CarControl, can_strings: list[bytes], frogpilot_toggles) -> car.CarState: + def update(self, c: car.CarControl, can_strings: list[bytes], params_list: SimpleNamespace, frogpilot_toggles) -> car.CarState: # parse can for cp in self.can_parsers: if cp is not None: cp.update_strings(can_strings) + self.CS.params_list = params_list + # get CarState ret, fp_ret = self._update(c, frogpilot_toggles) @@ -414,7 +421,7 @@ def update(self, c: car.CarControl, can_strings: list[bytes], frogpilot_toggles) ret.cruiseState.speedCluster = ret.cruiseState.speed # Add any additional frogpilotCarStates - fp_ret.alwaysOnLateralDisabled = self.always_on_lateral_disabled + fp_ret.alwaysOnLateralEnabled = self.always_on_lateral_enabled fp_ret.distanceLongPressed = self.frogpilot_distance_functions(frogpilot_toggles) fp_ret.ecoGear |= ret.gearShifter == GearShifter.eco fp_ret.sportGear |= ret.gearShifter == GearShifter.sport @@ -476,7 +483,7 @@ def create_common_events(self, cs_out, extra_gears=None, pcm_enable=True, allow_ # FrogPilot button presses if b.type == FrogPilotButtonType.lkas and b.pressed: - self.always_on_lateral_disabled = not self.always_on_lateral_disabled + self.always_on_lateral_enabled = not self.always_on_lateral_enabled # Handle permanent and temporary steering faults self.steering_unpressed = 0 if cs_out.steeringPressed else self.steering_unpressed + 1 @@ -563,6 +570,8 @@ def __init__(self, CP): self.cluster_speed_hyst_gap = 0.0 self.cluster_min_speed = 0.0 # min speed before dropping to 0 + self.params_list: SimpleNamespace = ParamManager().get_params() + Q = [[0.0, 0.0], [0.0, 100.0]] R = 0.3 A = [[1.0, DT_CTRL], [0.0, 1.0]] diff --git a/selfdrive/car/torque_data/override.toml b/selfdrive/car/torque_data/override.toml index b37817b4cc6e40..7a4dacc758d848 100644 --- a/selfdrive/car/torque_data/override.toml +++ b/selfdrive/car/torque_data/override.toml @@ -66,12 +66,14 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] "LEXUS_IS_TSS2" = [2.0, 2.0, 0.1] "HYUNDAI_KONA_EV_2ND_GEN" = [2.5, 2.5, 0.1] "HYUNDAI_IONIQ_6" = [2.5, 2.5, 0.005] +"HYUNDAI_IONIQ_6_2025" = [2.5, 2.5, 0.005] "HYUNDAI_AZERA_6TH_GEN" = [1.8, 1.8, 0.1] "HYUNDAI_AZERA_HEV_6TH_GEN" = [1.8, 1.8, 0.1] "KIA_K8_HEV_1ST_GEN" = [2.5, 2.5, 0.1] "HYUNDAI_CUSTIN_1ST_GEN" = [2.5, 2.5, 0.1] "LEXUS_GS_F" = [2.5, 2.5, 0.08] "HYUNDAI_STARIA_4TH_GEN" = [1.8, 2.0, 0.15] +"KIA_K5_2025" = [2.5, 2.5, 0.1] # Dashcam or fallback configured as ideal car "MOCK" = [10.0, 10, 0.0] @@ -79,3 +81,4 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] # Manually checked "HONDA_CIVIC_2022" = [2.5, 1.2, 0.15] "HONDA_HRV_3G" = [2.5, 1.2, 0.2] +"HYUNDAI_BAYON_1ST_GEN_NON_SCC" = [2.5, 2.5, 0.1] \ No newline at end of file diff --git a/selfdrive/car/torque_data/substitute.toml b/selfdrive/car/torque_data/substitute.toml index 1525bf11411fd6..aae40b9392435d 100644 --- a/selfdrive/car/torque_data/substitute.toml +++ b/selfdrive/car/torque_data/substitute.toml @@ -92,3 +92,12 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] "SUBARU_FORESTER_PREGLOBAL" = "SUBARU_IMPREZA" "SUBARU_LEGACY_PREGLOBAL" = "SUBARU_IMPREZA" "SUBARU_ASCENT" = "SUBARU_FORESTER" + +"KIA_FORTE_2019_NON_SCC" = "HYUNDAI_SONATA" +"KIA_FORTE_2021_NON_SCC" = "HYUNDAI_SONATA" +"KIA_SELTOS_2023_NON_SCC" = "HYUNDAI_SONATA" +"HYUNDAI_KONA_NON_SCC" = "HYUNDAI_KONA_EV" +"HYUNDAI_KONA_EV_NON_SCC" = "HYUNDAI_KONA_EV" +"HYUNDAI_ELANTRA_2022_NON_SCC" = "HYUNDAI_ELANTRA_2021" +"GENESIS_G70_2021_NON_SCC" = "HYUNDAI_SONATA" +"KIA_CEED_PHEV_2022_NON_SCC" = "HYUNDAI_SONATA" \ No newline at end of file diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 016a7084e8570d..e97acc99778c8d 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -49,20 +49,9 @@ def get_long_tune(CP, params): kiBP = [0.] - kdBP = [0.] - kdV = [0.] - - if CP.enableGasInterceptor: - kiBP = [0., 5., 20.] - kiV = [1.3, 1.0, 0.7] - elif CP.carFingerprint in TSS2_CAR: - kiV = [0.25] - kdV = [0.25 / 4] - else: - kiBP = [0., 5., 35.] - kiV = [3.6, 2.4, 1.5] - - return PIDController(0.0, (kiBP, kiV), k_f=1.0, k_d=(kdBP, kdV), + kiV = [0.25] + + return PIDController(0.0, (kiBP, kiV), k_f=1.0, pos_limit=params.ACCEL_MAX, neg_limit=params.ACCEL_MIN, rate=1 / (DT_CTRL * 3)) @@ -84,11 +73,7 @@ def __init__(self, dbc_name, CP, VM): # *** start long control state *** self.long_pid = get_long_tune(self.CP, self.params) - self.error_rate = FirstOrderFilter(0.0, 0.5, DT_CTRL * 3) - self.prev_error = 0.0 - self.aego = FirstOrderFilter(0.0, 0.25, DT_CTRL * 3) - self.pitch = FirstOrderFilter(0, 0.5, DT_CTRL) self.accel = 0 @@ -115,10 +100,17 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): if frogpilot_toggles.sport_plus: if not self.updated_pid: self.params.ACCEL_MAX = get_max_allowed_accel(0) - self.long_pid = get_long_tune(self.CP, self.params) + self.long_pid = get_long_tune(self.CP, self.params, frogpilot_toggles) self.updated_pid = True self.params.ACCEL_MAX = min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo)) + elif frogpilot_toggles.frogsgomoo_tweak: + if not self.updated_pid: + self.params.ACCEL_MAX = self.stock_max_accel + self.long_pid = get_long_tune(self.CP, self.params, frogpilot_toggles) + self.updated_pid = True + + self.params.ACCEL_MAX = min(frogpilot_toggles.max_desired_acceleration, self.stock_max_accel) else: if self.updated_pid: self.params.ACCEL_MAX = self.stock_max_accel @@ -289,18 +281,12 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): a_ego_future = a_ego_blended + j_ego * 0.5 if actuators.longControlState == LongCtrlState.pid: - error = pcm_accel_cmd - a_ego_blended - self.error_rate.update((error - self.prev_error) / (DT_CTRL * 3)) - self.prev_error = error - error_future = pcm_accel_cmd - a_ego_future - pcm_accel_cmd = self.long_pid.update(error_future, error_rate=self.error_rate.x, + pcm_accel_cmd = self.long_pid.update(error_future, speed=CS.out.vEgo, feedforward=pcm_accel_cmd) else: self.long_pid.reset() - self.error_rate.x = 0.0 - self.prev_error = 0.0 # Along with rate limiting positive jerk above, this greatly improves gas response time # Consider the net acceleration request that the PCM should be applying (pitch included) diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 02cb4a5795965d..077a1720e6b13d 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -2,7 +2,7 @@ from panda import Panda from panda.python import uds from openpilot.selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \ - MIN_ACC_SPEED, PEDAL_TRANSITION, EPS_SCALE, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR, STOP_AND_GO_CAR + MIN_ACC_SPEED, EPS_SCALE, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR, STOP_AND_GO_CAR from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.disable_ecu import disable_ecu from openpilot.selfdrive.car.interfaces import CarInterfaceBase @@ -145,22 +145,15 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp # to a negative value, so it won't matter. ret.minEnableSpeed = -1. if (candidate in STOP_AND_GO_CAR or ret.enableGasInterceptor) else MIN_ACC_SPEED - tune = ret.longitudinalTuning - if ret.enableGasInterceptor: - tune.kpBP = [0., 5., 20.] - tune.kpV = [1.3, 1.0, 0.7] - tune.kiBP = [0., 5., 12., 20., 27.] - tune.kiV = [.35, .23, .20, .17, .1] - elif candidate in TSS2_CAR: - ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value - - ret.vEgoStopping = 0.25 - ret.vEgoStarting = 0.25 - ret.stoppingDecelRate = 0.3 # reach stopping target smoothly - - # Hybrids have much quicker longitudinal actuator response - if ret.flags & ToyotaFlags.HYBRID.value: - ret.longitudinalActuatorDelay = 0.05 + ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value + + ret.vEgoStopping = 0.25 + ret.vEgoStarting = 0.25 + ret.stoppingDecelRate = 0.3 # reach stopping target smoothly + + # Hybrids have much quicker longitudinal actuator response + if ret.flags & ToyotaFlags.HYBRID.value: + ret.longitudinalActuatorDelay = 0.05 return ret diff --git a/selfdrive/classic_modeld/classic_modeld.py b/selfdrive/classic_modeld/classic_modeld.py index d271667b5d1754..0fbc7d84711d6d 100755 --- a/selfdrive/classic_modeld/classic_modeld.py +++ b/selfdrive/classic_modeld/classic_modeld.py @@ -25,7 +25,7 @@ from openpilot.selfdrive.classic_modeld.constants import ModelConstants from openpilot.selfdrive.classic_modeld.models.commonmodel_pyx import ModelFrame, CLContext -from openpilot.selfdrive.frogpilot.frogpilot_variables import DEFAULT_CLASSIC_MODEL, MODELS_PATH, get_frogpilot_toggles +from openpilot.selfdrive.frogpilot.frogpilot_variables import METADATAS_PATH, MODELS_PATH, get_frogpilot_toggles PROCESS_NAME = "selfdrive.classic_modeld.classic_modeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') @@ -34,8 +34,6 @@ ModelRunner.THNEED: Path(__file__).parent / 'models/supercombo.thneed', ModelRunner.ONNX: Path(__file__).parent / 'models/supercombo.onnx'} -METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl' - class FrameMeta: frame_id: int = 0 timestamp_sof: int = 0 @@ -53,38 +51,32 @@ class ModelState: prev_desire: np.ndarray # for tracking the rising edge of the pulse model: ModelRunner - def __init__(self, context: CLContext, model: str, model_version: str, navigation: bool, radarless: bool): + def __init__(self, context: CLContext, model: str, model_version: str): # FrogPilot variables - model_path = MODELS_PATH / f'{model}.thneed' - if model != DEFAULT_CLASSIC_MODEL and model_path.exists(): - MODEL_PATHS[ModelRunner.THNEED] = model_path + MODEL_PATHS[ModelRunner.THNEED] = MODELS_PATH / f'{model}.thneed' + + with open(METADATAS_PATH / f'supercombo_metadata_{model_version}.pkl', 'rb') as f: + model_metadata = pickle.load(f) - metadata_path = METADATA_PATH - desired_metadata_path = MODELS_PATH / f'supercombo_metadata_{model_version}.pkl' - if model != DEFAULT_CLASSIC_MODEL and desired_metadata_path.exists(): - metadata_path = desired_metadata_path + input_shapes = model_metadata.get('input_shapes') + self.navigation = 'nav_features' in input_shapes and 'nav_instructions' in input_shapes + self.radarless = 'radar_tracks' in input_shapes self.frame = ModelFrame(context) self.wide_frame = ModelFrame(context) self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32) + self.inputs = { 'desire': np.zeros(ModelConstants.DESIRE_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32), 'traffic_convention': np.zeros(ModelConstants.TRAFFIC_CONVENTION_LEN, dtype=np.float32), 'lateral_control_params': np.zeros(ModelConstants.LATERAL_CONTROL_PARAMS_LEN, dtype=np.float32), 'prev_desired_curv': np.zeros(ModelConstants.PREV_DESIRED_CURV_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32), + **({'nav_features': np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32), + 'nav_instructions': np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32)} if self.navigation else {}), 'features_buffer': np.zeros((ModelConstants.HISTORY_BUFFER_LEN) * ModelConstants.FEATURE_LEN, dtype=np.float32), + **({'radar_tracks': np.zeros(ModelConstants.RADAR_TRACKS_LEN * ModelConstants.RADAR_TRACKS_WIDTH, dtype=np.float32)} if self.radarless else {}), } - if navigation: - self.inputs['nav_features'] = np.zeros(ModelConstants.NAV_FEATURE_LEN, dtype=np.float32) - self.inputs['nav_instructions'] = np.zeros(ModelConstants.NAV_INSTRUCTION_LEN, dtype=np.float32) - - if radarless: - self.inputs['radar_tracks'] = np.zeros(ModelConstants.RADAR_TRACKS_LEN * ModelConstants.RADAR_TRACKS_WIDTH, dtype=np.float32) - - with open(metadata_path, 'rb') as f: - model_metadata = pickle.load(f) - self.output_slices = model_metadata['output_slices'] net_output_size = model_metadata['output_shapes']['outputs'][1] self.output = np.zeros(net_output_size, dtype=np.float32) @@ -103,7 +95,7 @@ def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]: return parsed_model_outputs def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, - inputs: dict[str, np.ndarray], prepare_only: bool, navigation: bool, radarless: bool) -> dict[str, np.ndarray] | None: + inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None: # Model decides when action is completed, so desire input is just a pulse triggered on rising edge inputs['desire'][0] = 0 self.inputs['desire'][:-ModelConstants.DESIRE_LEN] = self.inputs['desire'][ModelConstants.DESIRE_LEN:] @@ -113,11 +105,11 @@ def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_ self.inputs['traffic_convention'][:] = inputs['traffic_convention'] self.inputs['lateral_control_params'][:] = inputs['lateral_control_params'] - if navigation: + if self.navigation: self.inputs['nav_features'][:] = inputs['nav_features'] self.inputs['nav_instructions'][:] = inputs['nav_instructions'] - if radarless: + if self.radarless: self.inputs['radar_tracks'][:] = inputs['radar_tracks'] # if getCLBuffer is not None, frame will be None @@ -142,12 +134,9 @@ def main(demo=False): # FrogPilot variables frogpilot_toggles = get_frogpilot_toggles() - model = frogpilot_toggles.model + model_name = frogpilot_toggles.model model_version = frogpilot_toggles.model_version - navigation = frogpilot_toggles.navigation_model - radarless = frogpilot_toggles.radarless_model - cloudlog.warning("classic_modeld init") sentry.set_tag("daemon", PROCESS_NAME) @@ -158,7 +147,7 @@ def main(demo=False): cloudlog.warning("setting up CL context") cl_context = CLContext() cloudlog.warning("CL context ready; loading model") - model = ModelState(cl_context, model, model_version, navigation, radarless) + model = ModelState(cl_context, model_name, model_version) cloudlog.warning("models loaded, classic_modeld starting") # visionipc clients @@ -274,7 +263,7 @@ def main(demo=False): # Enable/disable nav features timestamp_llk = sm["navModel"].locationMonoTime nav_valid = sm.valid["navModel"] # and (nanos_since_boot() - timestamp_llk < 1e9) - nav_enabled = nav_valid and navigation + nav_enabled = nav_valid and model.navigation if not nav_enabled: nav_features[:] = 0 @@ -320,17 +309,12 @@ def main(demo=False): 'desire': vec_desire, 'traffic_convention': traffic_convention, 'lateral_control_params': lateral_control_params, + **({'nav_features': nav_features, 'nav_instructions': nav_instructions} if model.navigation else {}), + **({'radar_tracks': radar_tracks} if model.radarless else {}), } - if navigation: - inputs['nav_features'] = nav_features - inputs['nav_instructions'] = nav_instructions - - if radarless: - inputs['radar_tracks'] = radar_tracks - mt1 = time.perf_counter() - model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only, navigation, radarless) + model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only) mt2 = time.perf_counter() model_execution_time = mt2 - mt1 diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 5cfb89ff22eb10..14894558883e17 100644 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -65,7 +65,6 @@ class Controls: def __init__(self, CI=None): self.params = Params() - if CI is None: cloudlog.info("controlsd is waiting for CarParams") with car.CarParams.from_bytes(self.params.get("CarParams", block=True)) as msg: @@ -194,7 +193,7 @@ def __init__(self, CI=None): self.onroad_distance_pressed = False self.steer_saturated_event_triggered = False - self.clip_curves = self.frogpilot_toggles.clipped_curvature_model + self.planner_curves = self.frogpilot_toggles.planner_curvature_model self.radarless_model = self.frogpilot_toggles.radarless_model self.use_old_long = self.frogpilot_toggles.old_long_api @@ -625,7 +624,7 @@ def state_control(self, CS): if self.use_old_long: t_since_plan = (self.sm.frame - self.sm.recv_frame['longitudinalPlan']) * DT_CTRL - actuators.accel = self.LoC.update_old_long(CC.longActive, CS, long_plan, pid_accel_limits, t_since_plan) + actuators.accel = self.LoC.update_old_long(CC.longActive, CS, long_plan, pid_accel_limits, t_since_plan, self.frogpilot_toggles) else: actuators.accel = self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop or self.sm['frogpilotPlan'].forcingStopLength < 1, pid_accel_limits, self.frogpilot_toggles) @@ -633,7 +632,7 @@ def state_control(self, CS): actuators.speed = long_plan.speeds[-1] # Steering PID loop and lateral MPC - self.desired_curvature = clip_curvature(CS.vEgo, self.desired_curvature, model_v2.action.desiredCurvature, self.clip_curves) + self.desired_curvature = clip_curvature(CS.vEgo, self.desired_curvature, model_v2.action.desiredCurvature, self.planner_curves) actuators.curvature = self.desired_curvature actuators.steer, actuators.steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp, self.steer_limited, self.desired_curvature, @@ -723,12 +722,16 @@ def state_control(self, CS): return CC, lac_log, FPCC def update_frogpilot_variables(self, CS): - self.always_on_lateral_active |= self.frogpilot_toggles.always_on_lateral_main or CS.cruiseState.enabled - self.always_on_lateral_active &= self.frogpilot_toggles.always_on_lateral_set and CS.cruiseState.available + main_enabled = (self.frogpilot_toggles.always_on_lateral_main or CS.cruiseState.enabled or (self.frogpilot_toggles.always_on_lateral_lkas and self.sm['frogpilotCarState'].alwaysOnLateralEnabled)) + if not hasattr(self, 'always_on_lateral_active'): + self.always_on_lateral_active = False + + self.always_on_lateral_active = main_enabled self.always_on_lateral_active &= CS.gearShifter not in NON_DRIVING_GEARS self.always_on_lateral_active &= self.sm['frogpilotPlan'].lateralCheck self.always_on_lateral_active &= self.sm['liveCalibration'].calPerc >= 1 - self.always_on_lateral_active &= not (self.frogpilot_toggles.always_on_lateral_lkas and self.sm['frogpilotCarState'].alwaysOnLateralDisabled) + self.always_on_lateral_active &= ((self.frogpilot_toggles.always_on_lateral_lkas) and self.sm['frogpilotCarState'].alwaysOnLateralEnabled) or \ + ((self.frogpilot_toggles.always_on_lateral_main) and CS.cruiseState.available) self.always_on_lateral_active &= not (CS.brakePressed and CS.vEgo < self.frogpilot_toggles.always_on_lateral_pause_speed) or CS.standstill self.always_on_lateral_active = bool(self.always_on_lateral_active) @@ -793,6 +796,10 @@ def publish_logs(self, CS, start_time, CC, lac_log, FPCC): hudControl.leadVisible = self.sm['longitudinalPlan'].hasLead hudControl.leadDistanceBars = self.personality + 1 + leadOne = self.sm['radarState'].leadOne + hudControl.leadDistance = leadOne.dRel if leadOne.status else 0 + hudControl.leadRelSpeed = leadOne.vRel if leadOne.status else 0 + hudControl.rightLaneVisible = True hudControl.leftLaneVisible = True diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 9304d51d6674eb..396cee52c69f8d 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -179,8 +179,8 @@ def rate_limit(new_value, last_value, dw_step, up_step): return clip(new_value, last_value + dw_step, last_value + up_step) -def clip_curvature(v_ego, prev_curvature, new_curvature, clip_curves): - if clip_curves: +def clip_curvature(v_ego, prev_curvature, new_curvature, planner_curves): + if planner_curves: new_curvature = clip(new_curvature, -MAX_CURVATURE, MAX_CURVATURE) v_ego = max(MIN_SPEED, v_ego) max_curvature_rate = MAX_LATERAL_JERK / (v_ego**2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755 diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py old mode 100755 new mode 100644 index 7a5dd36f86f16b..7c8566bc277548 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -1062,6 +1062,11 @@ def torque_nn_load_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubM ET.PERMANENT: holiday_alert, }, + EventName.hyundaiRadarTracksAvailable: { + ET.PERMANENT: NormalPermanentAlert("Radar tracks initialized. Restart the car and turn on OPLong to engage."), + }, + + EventName.laneChangeBlockedLoud: { ET.WARNING: Alert( "Car detected in blindspot", diff --git a/selfdrive/controls/lib/latcontrol_pid.py b/selfdrive/controls/lib/latcontrol_pid.py index 56035e979c34bc..23211c1703876a 100644 --- a/selfdrive/controls/lib/latcontrol_pid.py +++ b/selfdrive/controls/lib/latcontrol_pid.py @@ -10,7 +10,7 @@ def __init__(self, CP, CI): super().__init__(CP, CI) self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV), (CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV), - k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max) + k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max, lateral_pid=True) self.get_steer_feedforward = CI.get_steer_feedforward_function() def reset(self): @@ -37,7 +37,7 @@ def update(self, active, CS, VM, params, steer_limited, desired_curvature, llk, steer_feedforward = self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo) output_steer = self.pid.update(error, override=CS.steeringPressed, - feedforward=steer_feedforward, speed=CS.vEgo) + feedforward=steer_feedforward, speed=CS.vEgo, frogpilot_toggles=frogpilot_toggles) pid_log.active = True pid_log.p = self.pid.p pid_log.i = self.pid.i diff --git a/selfdrive/controls/lib/latcontrol_torque.py b/selfdrive/controls/lib/latcontrol_torque.py index 096a8dcd584645..8d79ecc06f3e1e 100644 --- a/selfdrive/controls/lib/latcontrol_torque.py +++ b/selfdrive/controls/lib/latcontrol_torque.py @@ -68,7 +68,7 @@ def __init__(self, CP, CI): super().__init__(CP, CI) self.torque_params = CP.lateralTuning.torque self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, - k_f=self.torque_params.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max) + k_f=self.torque_params.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max, lateral_pid=True) self.torque_from_lateral_accel = CI.torque_from_lateral_accel() self.use_steering_angle = self.torque_params.useSteeringAngle self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index 8716b7f8a7cf5d..539bad30a68bb1 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -11,14 +11,14 @@ def long_control_state_trans(CP, active, long_control_state, v_ego, - should_stop, brake_pressed, cruise_standstill, frogpilot_variables): + should_stop, brake_pressed, cruise_standstill, frogpilot_toggles): # Ignore cruise standstill if car has a gas interceptor cruise_standstill = cruise_standstill and not CP.enableGasInterceptor stopping_condition = should_stop starting_condition = (not should_stop and not cruise_standstill and not brake_pressed) - started_condition = v_ego > frogpilot_variables.vEgoStarting + started_condition = v_ego > frogpilot_toggles.vEgoStarting if not active: long_control_state = LongCtrlState.off @@ -92,21 +92,27 @@ def __init__(self, CP): self.long_control_state = LongCtrlState.off self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV), (CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV), - k_f=CP.longitudinalTuning.kf, rate=1 / DT_CTRL) + k_f=CP.longitudinalTuning.kf, rate=1 / DT_CTRL, + longitudinal_pid=True) self.v_pid = 0.0 self.last_output_accel = 0.0 + # FrogPilot variables + self.CP = CP + + self.updated_pid = False + def reset(self): self.pid.reset() - def update(self, active, CS, a_target, should_stop, accel_limits, frogpilot_variables): + def update(self, active, CS, a_target, should_stop, accel_limits, frogpilot_toggles): """Update longitudinal control. This updates the state machine and runs a PID loop""" self.pid.neg_limit = accel_limits[0] self.pid.pos_limit = accel_limits[1] self.long_control_state = long_control_state_trans(self.CP, active, self.long_control_state, CS.vEgo, should_stop, CS.brakePressed, - CS.cruiseState.standstill, frogpilot_variables) + CS.cruiseState.standstill, frogpilot_toggles) if self.long_control_state == LongCtrlState.off: self.reset() output_accel = 0. @@ -115,17 +121,17 @@ def update(self, active, CS, a_target, should_stop, accel_limits, frogpilot_vari output_accel = self.last_output_accel if output_accel > self.CP.stopAccel: output_accel = min(output_accel, 0.0) - output_accel -= frogpilot_variables.stoppingDecelRate * DT_CTRL + output_accel -= frogpilot_toggles.stoppingDecelRate * DT_CTRL self.reset() elif self.long_control_state == LongCtrlState.starting: - output_accel = (a_target if frogpilot_variables.human_acceleration else self.CP.startAccel) + output_accel = (a_target if frogpilot_toggles.human_acceleration else self.CP.startAccel) self.reset() else: # LongCtrlState.pid error = a_target - CS.aEgo output_accel = self.pid.update(error, speed=CS.vEgo, - feedforward=a_target) + feedforward=a_target, frogpilot_toggles=frogpilot_toggles) self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1]) return self.last_output_accel @@ -135,7 +141,7 @@ def reset_old_long(self, v_pid): self.pid.reset() self.v_pid = v_pid - def update_old_long(self, active, CS, long_plan, accel_limits, t_since_plan): + def update_old_long(self, active, CS, long_plan, accel_limits, t_since_plan, frogpilot_toggles): """Update longitudinal control. This updates the state machine and runs a PID loop""" # Interp control trajectory speeds = long_plan.speeds @@ -189,7 +195,8 @@ def update_old_long(self, active, CS, long_plan, accel_limits, t_since_plan): error_deadzone = apply_deadzone(error, deadzone) output_accel = self.pid.update(error_deadzone, speed=CS.vEgo, feedforward=a_target, - freeze_integrator=freeze_integrator) + freeze_integrator=freeze_integrator, + frogpilot_toggles=frogpilot_toggles) self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1]) diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index a144574304a7e2..3c6ffedcd7f215 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -31,7 +31,7 @@ COST_DIM = COST_E_DIM + 1 CONSTR_DIM = 4 -X_EGO_OBSTACLE_COST = 3. +X_EGO_OBSTACLE_COST = 4. #3. X_EGO_COST = 0. V_EGO_COST = 0. A_EGO_COST = 0. @@ -56,7 +56,7 @@ FCW_IDXS = T_IDXS < 5.0 T_DIFFS = np.diff(T_IDXS, prepend=[0.]) COMFORT_BRAKE = 2.5 -STOP_DISTANCE = 6.0 +STOP_DISTANCE = 7.0 def get_jerk_factor(aggressive_jerk_acceleration=0.5, aggressive_jerk_danger=0.5, aggressive_jerk_speed=0.5, standard_jerk_acceleration=1.0, standard_jerk_danger=1.0, standard_jerk_speed=1.0, diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index c179b4c8865017..371873ba930847 100644 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -229,9 +229,11 @@ def update(self, radarless_model, sm, frogpilot_toggles): accel_limits = [sm['frogpilotPlan'].minAcceleration, sm['frogpilotPlan'].maxAcceleration] if self.mpc.mode == 'acc': + accel_limits = [A_CRUISE_MIN, get_max_accel(v_ego)] steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg accel_limits_turns = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_limits, self.CP) else: + accel_limits = [ACCEL_MIN, ACCEL_MAX] accel_limits_turns = [ACCEL_MIN, ACCEL_MAX] if reset_state: @@ -276,7 +278,7 @@ def update(self, radarless_model, sm, frogpilot_toggles): self.mpc.set_weights(sm['frogpilotPlan'].accelerationJerk, sm['frogpilotPlan'].dangerJerk, sm['frogpilotPlan'].speedJerk, prev_accel_constraint, personality=sm['controlsState'].personality) self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1]) self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired) - self.mpc.update(self.lead_one, self.lead_two, sm['frogpilotPlan'].vCruise, x, v, a, j, radarless_model, sm['frogpilotPlan'].tFollow, + self.mpc.update(self.lead_one, self.lead_two, sm['frogpilotPlan'].vCruise, x, v, a, j, radarless_model, sm['frogpilotPlan'].tFollow, sm['frogpilotCarState'].trafficModeActive, personality=sm['controlsState'].personality) self.a_desired_trajectory_full = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution) @@ -294,7 +296,7 @@ def update(self, radarless_model, sm, frogpilot_toggles): self.a_desired = float(interp(self.dt, CONTROL_N_T_IDX, self.a_desired_trajectory)) self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0 - def publish(self, classic_model, sm, pm, frogpilot_variables): + def publish(self, classic_model, sm, pm, frogpilot_toggles): plan_send = messaging.new_message('longitudinalPlan') plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState']) @@ -313,11 +315,11 @@ def publish(self, classic_model, sm, pm, frogpilot_variables): longitudinalPlan.fcw = self.fcw if classic_model: - a_target, should_stop = get_accel_from_plan_classic(self.CP, longitudinalPlan.speeds, longitudinalPlan.accels, vEgoStopping=frogpilot_variables.vEgoStopping) + a_target, should_stop = get_accel_from_plan_classic(self.CP, longitudinalPlan.speeds, longitudinalPlan.accels, vEgoStopping=frogpilot_toggles.vEgoStopping) else: action_t = self.CP.longitudinalActuatorDelay + DT_MDL a_target, should_stop = get_accel_from_plan(longitudinalPlan.speeds, longitudinalPlan.accels, - action_t=action_t, vEgoStopping=frogpilot_variables.vEgoStopping) + action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping) longitudinalPlan.aTarget = a_target longitudinalPlan.shouldStop = should_stop longitudinalPlan.allowBrake = True diff --git a/selfdrive/controls/lib/pid.py b/selfdrive/controls/lib/pid.py index 7c242e7c549878..1bcfaddf196ce9 100644 --- a/selfdrive/controls/lib/pid.py +++ b/selfdrive/controls/lib/pid.py @@ -5,7 +5,7 @@ class PIDController: - def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100): + def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100, lateral_pid=False, longitudinal_pid=False): self._k_p = k_p self._k_i = k_i self._k_d = k_d @@ -26,6 +26,10 @@ def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, self.reset() + # FrogPilot variables + self.lateral_pid = lateral_pid + self.longitudinal_pid = longitudinal_pid + @property def k_p(self): return interp(self.speed, self._k_p[0], self._k_p[1]) @@ -52,7 +56,10 @@ def reset(self): def update(self, error, error_rate=0.0, speed=0.0, override=False, feedforward=0., freeze_integrator=False, frogpilot_toggles=None): self.speed = speed - self.p = float(error) * (frogpilot_toggles.steer_kp if frogpilot_toggles and frogpilot_toggles.use_custom_kp else self.k_p) + if self.lateral_pid: + self.p = float(error) * (frogpilot_toggles.steer_kp if frogpilot_toggles.use_custom_kp else self.k_p) + else: + self.p = float(error) * self.k_p self.f = feedforward * self.k_f self.d = error_rate * self.k_d @@ -60,7 +67,10 @@ def update(self, error, error_rate=0.0, speed=0.0, override=False, feedforward=0 self.i -= self.i_unwind_rate * float(np.sign(self.i)) else: if not freeze_integrator: - self.i = self.i + error * self.k_i * self.i_rate + if self.longitudinal_pid and frogpilot_toggles.frogsgomoo_tweak: + self.i = self.i + error * interp(self.speed, frogpilot_toggles.kiBP, frogpilot_toggles.kiV) * self.i_rate + else: + self.i = self.i + error * self.k_i * self.i_rate # Clip i to prevent exceeding control limits control_no_i = self.p + self.d + self.f diff --git a/selfdrive/debug/hyundai_enable_radar_points.py b/selfdrive/debug/hyundai_enable_radar_points.py index 5e7080ef64c81f..b6855f0a05686d 100755 --- a/selfdrive/debug/hyundai_enable_radar_points.py +++ b/selfdrive/debug/hyundai_enable_radar_points.py @@ -69,6 +69,10 @@ class ConfigValues(NamedTuple): b"DLhe SCC FHCUP 1.00 1.02 99110-L7000 \x01 \x102 ": ConfigValues( default_config=b"\x00\x00\x00\x01\x00\x00", tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), + # 2022 Niro PHEV + b"DEhe SCC FHCUP 1.00 1.00 99110-G5600 \xf1\x00DEhe ": ConfigValues( + default_config=b"\x00\x00\x00\x01\x00\x00", + tracks_enabled=b"\x00\x00\x00\x01\x00\x01"), } if __name__ == "__main__": diff --git a/selfdrive/frogpilot/assets/download_functions.py b/selfdrive/frogpilot/assets/download_functions.py index ac5d4d4f0358c0..493211b87a1c4b 100644 --- a/selfdrive/frogpilot/assets/download_functions.py +++ b/selfdrive/frogpilot/assets/download_functions.py @@ -2,6 +2,7 @@ import requests import tempfile +from datetime import datetime from pathlib import Path from openpilot.selfdrive.frogpilot.frogpilot_utilities import delete_file, is_url_pingable @@ -9,6 +10,25 @@ GITHUB_URL = "https://raw.githubusercontent.com/FrogAi/FrogPilot-Resources" GITLAB_URL = "https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw" +def check_github_rate_limit(): + try: + response = requests.get("https://api.github.com/rate_limit") + response.raise_for_status() + rate_limit_info = response.json() + + remaining = rate_limit_info["rate"]["remaining"] + print(f"GitHub API Requests Remaining: {remaining}") + if remaining > 0: + return True + + reset_time = datetime.utcfromtimestamp(rate_limit_info["rate"]["reset"]).strftime('%Y-%m-%d %H:%M:%S') + print("GitHub rate limit reached") + print(f"GitHub Rate Limit Resets At (UTC): {reset_time}") + return False + except requests.exceptions.RequestException as error: + print(f"Error checking GitHub rate limit: {error}") + return False + def download_file(cancel_param, destination, progress_param, url, download_param, params_memory): try: destination.parent.mkdir(parents=True, exist_ok=True) @@ -76,7 +96,8 @@ def get_remote_file_size(url): def get_repository_url(): if is_url_pingable("https://github.com"): - return GITHUB_URL + if check_github_rate_limit(): + return GITHUB_URL if is_url_pingable("https://gitlab.com"): return GITLAB_URL return None diff --git a/selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v1.pkl b/selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v1.pkl new file mode 100644 index 0000000000000000000000000000000000000000..c61356a9162853ae5d9360735b8e1590bfefb812 GIT binary patch literal 734 zcmZ{iy>8nu6oqZWOm>Zu&hchBLymwZ@0{awuB`QM zU2xr0fM$B0TEVcfYCy)ScsZ>=n+uATVNAqvHV@(yb}X2P&()$nKi4`btlMjMSfG&Zbhhvy}RE`wki~ YTO6D{{?uvo@qPNcF}MBp=(PR*29uK%O8@`> literal 0 HcmV?d00001 diff --git a/selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v2.pkl b/selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v2.pkl new file mode 100644 index 0000000000000000000000000000000000000000..6a8b5729b42cce8195bc2981ecc046e9fb96a900 GIT binary patch literal 687 zcmZ{i%}yIJ6or#0g)|KyiZY{0JOHac0S|yQauzJph2@OxjD^P@`6nz;C01nPUCkTx z#gJI?5cOZks$QP&p5uG3{XBjBI~k?(+uyh|Y0Y2_)SR_M`zTEZj)Zr!jb+?0p}qT( zsQ=@e5%_hr+WUL&=Ano{&O~r^CqV`e%S+Qw83N_BCpsF>e)kFJOU*W{N;m2^A)DMcJJFaVR zThMfnIz`;p;I3%ZBf|tzC3}d;F6;&p%ZZAicQsg*s+ndzJh)diSQq^~&1e)7no8A2 z*SZF@(97HkhOJdQWUNY;(;9T8V9_y*i8#&{K$^n31rzDHIy98$nrDUeTkUU}akFTi z!Dwu}JKwM^{2x5YJ@{b2&1~=8_s}h+tc9%U+*xBJI$XlCWG$Bi`Vw&KM;`t A=>Px# literal 0 HcmV?d00001 diff --git a/selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v3.pkl b/selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v3.pkl new file mode 100644 index 0000000000000000000000000000000000000000..fbb3ca6c0da222cb1a75448c0eb9e9c0564931b1 GIT binary patch literal 708 zcmZ{iJ#HH@5QQZ{aTH}^!!fjr06u`LoWKV#u%L>RPC-yhQcH+SF8HGqMgUiG!!FEW z(&Z`!T>22{b zoO6P3tbVA$bJ0FT?JefH1}_Cvm}sU5)CL(6O)oWgJq8WLps2mp;JV4-A4~nHNV?;? z0k;LsN2yc9Z3FI#R{vm_K&s>bG1-URKw>#jG4!qh>r%DQY=B4ix&ik^KTk6ng@mS3 z4bgSq09xo}ZUw{6sy#ARrORmp`chE!3}Yfrvjvc*u&!bvJy(y$@?6WTuwkqHO*?I? z_7O~`#=G+k+rt0Bv)qFZ2HY;5y!#xwrIdBBlVVSVVNygmJ9t;~uwsRHL`F#tN2Ji8 zkN7gIbRV`9pCiwdQ>P*If|4|#h8s(1*k6e3z+@Isr4U96hHJZB%KCfo_Vgc*1G@M5 OhsF}ne>r$6qFRjQ54076KEGH-MPvg+-C7o#AOPCT5=^q+$F(}QWybTSq;0;r^#sy zxbzhQg+E1ASkAl|a^~@V`uKY?O6RA)cJGBX(i)^WYpM1Tno5ob?`A8@xM73#?sa1Q zFW-#d`^T?)|INF37@~-?A*eQI0fy}|GZ!ZYNhMa1-Y!8GdaeW}h>~>{>LuJ&Svchs zlQ?}>!BfHBM(m%>Qw7h3SP-h2qDUKTXcRqH@NzWjslgF@so<*4=I_e9R5aajRl`l8 zrc&r+aZ>{ntop$SMV%5oB6JtJLDX`pqAApHTc&23_2{U+tpOGMJY_Tr54sbok1ABd zwy>A^DFn4v?Xa;bUB0cMF9VAWBZLm8Y<>OZ&*5;$NnXj+QbrPF-GU8iiyJJ<7UvmZ z{aX9$X51{Adl-$4cjqg%M*kzv^3!|}kY={`?n4OCb*x3L7n+vYd5ZP33pjA0s^Q?K~PJsM3_r1_@fkt0asGP&ddpN zG661qhlGE!s<3?D%p>OwpNGHy40`cAxjTCyjP8tviqV=V7o=gw5O;R8GL&h`m9rlr zbuX^!fuGmk_wI+YV_!rdquh6G&bmdX5cuJ0s?cifiX zKBHkLRJ6D+!6IwLFNz6lq-Y@~Tb~Uih7lP|7bRHcs%e}>OT<+pWE<$R1Sr$XbR~wh zkz1sVjF+Gi)VW}?p%@c>nxQVg{q-lk_^gX*Bqd75PAHGt+Mq6PYn&9?u9ds1`qiX* z2ED#^cDkZ#_&<1_p5}bRO*Ps(d*z2{8d}3z@Gaq*3LfBK=WM}z!VNTOC9 z@ug36>q83LNTVj#zIUB9B+jki$~29S&s%;0y}{m@)6YB3Vn3$87@PsV**Rst{sU>$ B1_%HE literal 0 HcmV?d00001 diff --git a/selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v6.pkl b/selfdrive/frogpilot/assets/model_metadata/supercombo_metadata_v6.pkl new file mode 100644 index 0000000000000000000000000000000000000000..f5a2311b9de74b3f7a40a1a68b56f26ca185eb7a GIT binary patch literal 594 zcmZ{iy-ve06opGEf2Fh%s0p3BF!2%$NJOR*QYQw>t#4Y3)QS89143eI7jKvcMC#aA zK*f#?oyd5;b58EH{oeY#Z8qxf&0d+aQmaZUEDJUTX}#2{3c{^9o@iDm#-%j}HMJi$ zYoN!A`;~pLrtM6C3&ve)e&}TM)DNV$Be)Xf#N($vk`cE>Nhk(73qjdOuSp2D3Lwj6SVm)T7V*6+aze1hVs+suLRWp7^dhZqE=PP zy5c59qo^e>i~`O@xxf^j-PvH!1qfd{O3^sFy7ewf5H9mlEIb{jh(y|%GfIK)MkMLo z_jbDt1MOG!vO%dOZ90tCmoX<*yo`2G*kUH_RkoLPvUk+jQ`U4RY=-}XhhbUk0)y;$ zWzD&pG0)i)PfPv`T(OdS*j!rE%Yi5@K;ntcbGM??FHxhpvU>MAeg`r~;k`)bLUgdS HQeXc7?VaL@ literal 0 HcmV?d00001 diff --git a/selfdrive/frogpilot/assets/theme_manager.py b/selfdrive/frogpilot/assets/theme_manager.py index fd24259f79aad9..06b9bce5c49545 100644 --- a/selfdrive/frogpilot/assets/theme_manager.py +++ b/selfdrive/frogpilot/assets/theme_manager.py @@ -8,7 +8,7 @@ from dateutil import easter from pathlib import Path -from openpilot.selfdrive.frogpilot.assets.download_functions import GITLAB_URL, download_file, get_repository_url, handle_error, verify_download +from openpilot.selfdrive.frogpilot.assets.download_functions import GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, verify_download from openpilot.selfdrive.frogpilot.frogpilot_utilities import delete_file, extract_zip from openpilot.selfdrive.frogpilot.frogpilot_variables import ACTIVE_THEME_PATH, RANDOM_EVENTS_PATH, THEME_SAVE_PATH, params, params_memory, update_frogpilot_toggles @@ -271,50 +271,50 @@ def fetch_assets(repo_url): "wheels": [] } - for branch in branches: - if "github" in repo_url: - api_url = f"https://api.github.com/repos/{repo}/git/trees/{branch}?recursive=1" - elif "gitlab" in repo_url: - api_url = f"https://gitlab.com/api/v4/projects/{repo.replace('/', '%2F')}/repository/tree?ref={branch}&recursive=true" - else: - print(f"Unsupported repository URL: {repo_url}") - return assets + try: + for branch in branches: + if "github" in repo_url: + api_url = f"https://api.github.com/repos/{repo}/git/trees/{branch}?recursive=1" + elif "gitlab" in repo_url: + api_url = f"https://gitlab.com/api/v4/projects/{repo.replace('/', '%2F')}/repository/tree?ref={branch}&recursive=true" + else: + print(f"Unsupported repository URL: {repo_url}") + return assets - try: print(f"Fetching assets from branch '{branch}': {api_url}") response = requests.get(api_url, timeout=10) response.raise_for_status() content = response.json() - if "github" in repo_url: - content = content.get('tree', []) - - for item in content: - if item["type"] != "blob": - continue - - if branch == "Steering-Wheels": - assets["wheels"].append(item["path"]) - elif branch == "Themes": - theme_name = item["path"].split('/')[0] - assets["themes"].setdefault(theme_name, set()) - - item_path = item["path"].lower() - if "colors" in item_path: - assets["themes"][theme_name].add("colors") - elif "distance_icons" in item_path: - assets["themes"][theme_name].add("distance_icons") - elif "icons" in item_path: - assets["themes"][theme_name].add("icons") - elif "signals" in item_path: - assets["themes"][theme_name].add("signals") - elif "sounds" in item_path: - assets["themes"][theme_name].add("sounds") - - except requests.exceptions.RequestException as error: - print(f"Error occurred when fetching from branch '{branch}': {error}") - - return {**assets, "themes": {k: list(v) for k, v in assets["themes"].items()}} + if "github" in repo_url: + content = content.get('tree', []) + + for item in content: + if item["type"] != "blob": + continue + + if branch == "Steering-Wheels": + assets["wheels"].append(item["path"]) + elif branch == "Themes": + theme_name = item["path"].split('/')[0] + assets["themes"].setdefault(theme_name, set()) + + item_path = item["path"].lower() + if "colors" in item_path: + assets["themes"][theme_name].add("colors") + elif "distance_icons" in item_path: + assets["themes"][theme_name].add("distance_icons") + elif "icons" in item_path: + assets["themes"][theme_name].add("icons") + elif "signals" in item_path: + assets["themes"][theme_name].add("signals") + elif "sounds" in item_path: + assets["themes"][theme_name].add("sounds") + + return {**assets, "themes": {k: list(v) for k, v in assets["themes"].items()}} + except requests.exceptions.RequestException as error: + handle_request_error(f"Failed to fetch theme sizes from {'GitHub' if 'github' in repo_url else 'GitLab'}: {error}", None, None, None, None) + return {} def update_theme_params(self, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels): def update_param(key, assets, subfolder): @@ -395,7 +395,7 @@ def update_themes(self, frogpilot_toggles, boot_run=False): return assets = self.fetch_assets(repo_url) - if not assets["themes"] and not assets["wheels"]: + if not assets: return downloadable_colors = [] diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_brake.png b/selfdrive/frogpilot/assets/toggle_icons/icon_brake.png new file mode 100644 index 0000000000000000000000000000000000000000..18c28a57a270b1774cd8cd38d9d6318ec4a4390a GIT binary patch literal 1749 zcmeHH`#aMM9R6-=E@Lj4+sa7_hmI^vg@Zz3HO$It!{oXyZgZKUHacC72uCiDtb>Yn zsE85S`JUv+rJ^#k&8)e%Lb{<%XXoEI&-pyh`+lC!Z}0QI&-=L?i1$Sz&+i&tI2U6w0;en})koN+HK^ zzTWH`O*L8o08{vR?;(=9-j~V44MOxTjLcfuVXOQ-wM;lpVaM&Wg%>mq7V?b{w%j^W z0&&pF2B8K+1V#nJzN}*T1H3r%TEmvlsXIKUoP{ z6h>K;7fu~;_|V@X^{X{&?(%xI+kfj$EThe(c~z2;62)tkIEdF4=q8t(_)(hi<7Jex z_RIysOyP8AbzAGo{+X!{#0U!S+g;i&-++DP&Di1LqZ~KXbqS$)j;=#zT%2Ayu_12I zdGTy(J?RN~K$_L027Yue<>fKHqyzkbkn^G}T%+Rj)y6Tl))63{DB#Ltfyc;e>9Rav z5sZ!^?u0Ei^@t-bZe@}x|5}X!N`%kab3EXBuR?1kAYe7T^b`Z3H`-9cgJa~}C8(GV z)Cl`@nB!tTd)74of~Va%`W=2)hm`e6Vo5#N)n!Hp`_tt0DMYAjYF+>v3`M@k($xED zqEuUtUooK-`yk>5+ZlQjXPT@{8acCxr|r2W!PW1ZS_m`&_wrfj_)0jWS}YYf8>Ibgl* z8dQ{*26U3aZ?0pxU@=ua@RH&CB=l^Px&Y}W>t=24^4rVMfj#mi?`d+Qc{-n=8Ya*zx3N_Lh)L>d2{ljyPuhZ2hgsaP{pX z_(rv}frVW^cQ7Zw8QWF=;cQ<1NC8#ELSGTQD*wJ2X_=6H%Tm zN)1zED7X>jQ|^HNz02}EC?>4x_gND-bz;Mi0RL_rV5GJrE9H@v$d!%-yPWo!Q4iGG z9ER>}m-GvTkmC|mMT_%kWGu0Jlfy9$2L$=4r91YLzYcJ#v-A_ozT8`NC9rOVSYHVE zMi%31JG#%sFpj0x{+%HbRtVF(F>~Tab1efF^Mmz2P~0YsI7M8RZ$= THRESHOLD + return self.tracking_lead_filter.update(1 if following_lead else 0) >= THRESHOLD**2 def publish(self, sm, pm, toggles_updated): frogpilot_plan_send = messaging.new_message('frogpilotPlan') diff --git a/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py b/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py index b9a93e1f2591d2..9806ebf189d2e1 100644 --- a/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py +++ b/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py @@ -27,8 +27,9 @@ def update(self, carState, frogpilotCarState, frogpilotNavigation, modelData, v_ self.experimental_mode = self.check_conditions(carState, frogpilotNavigation, modelData, self.frogpilot_planner.frogpilot_following.following_lead, v_ego, v_lead, frogpilot_toggles) params_memory.put_int("CEStatus", self.status_value if self.experimental_mode else 0) else: - self.experimental_mode = self.status_value in {2, 4, 6} or carState.standstill and self.experimental_mode + self.experimental_mode = self.status_value in {2, 4, 6} or carState.standstill and self.experimental_mode and self.frogpilot_planner.model_stopped self.stop_light_detected &= self.status_value not in {1, 2, 3, 4, 5, 6} + self.stop_light_filter.x = 0 def check_conditions(self, carState, frogpilotNavigation, modelData, following_lead, v_ego, v_lead, frogpilot_toggles): below_speed = frogpilot_toggles.conditional_limit > v_ego >= 1 and not following_lead @@ -94,7 +95,7 @@ def stop_sign_and_light(self, frogpilotCarState, tracking_lead, v_ego, frogpilot if not (self.curve_detected or tracking_lead or frogpilotCarState.trafficModeActive): model_stopping = self.frogpilot_planner.model_length < v_ego * frogpilot_toggles.conditional_model_stop_time - self.stop_light_detected = self.stop_light_filter.update(1 if self.frogpilot_planner.model_stopped or model_stopping else 0) >= THRESHOLD / 2 + self.stop_light_detected = self.stop_light_filter.update(1 if self.frogpilot_planner.model_stopped or model_stopping else 0) >= THRESHOLD**2 else: self.stop_light_filter.update(0) self.stop_light_detected = False diff --git a/selfdrive/frogpilot/controls/lib/frogpilot_vcruise.py b/selfdrive/frogpilot/controls/lib/frogpilot_vcruise.py index 9e8ef4f5e5dcc0..cdbbfe79541354 100644 --- a/selfdrive/frogpilot/controls/lib/frogpilot_vcruise.py +++ b/selfdrive/frogpilot/controls/lib/frogpilot_vcruise.py @@ -93,6 +93,8 @@ def update(self, carControl, carState, controlsState, frogpilotCarControl, frogp self.slc.speed_limit_changed = self.slc_target != desired_slc_target and not speed_limit_denied elif self.slc_target == 0: self.slc_target = desired_slc_target + else: + self.speed_limit_timer = 0 if frogpilot_toggles.speed_limit_controller: self.override_slc = self.overridden_speed > self.slc_target + self.slc_offset diff --git a/selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py b/selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py index 2e7ca3d80cf818..2b0a035e5ec15f 100644 --- a/selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py +++ b/selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py @@ -36,9 +36,9 @@ def calculate_curvature(p1, p2, p3): class MapTurnSpeedController: def get_map_curvature(self, v_ego): - target_velocities = json.loads(params_memory.get("MapTargetVelocities") or "{}") - position = json.loads(params_memory.get("LastGPSPosition") or "{}") + if not position: + return 1e-6 current_latitude = position["latitude"] current_longitude = position["longitude"] @@ -46,11 +46,12 @@ def get_map_curvature(self, v_ego): minimum_idx = 0 minimum_distance = 1000.0 + target_velocities = json.loads(params_memory.get("MapTargetVelocities") or "[]") for i, target_velocity in enumerate(target_velocities): - latitude = target_velocity["latitude"] - longitude = target_velocity["longitude"] + target_latitude = target_velocity["latitude"] + target_longitude = target_velocity["longitude"] - distance = calculate_distance_to_point(current_latitude * TO_RADIANS, current_longitude * TO_RADIANS, latitude * TO_RADIANS, longitude * TO_RADIANS) + distance = calculate_distance_to_point(current_latitude * TO_RADIANS, current_longitude * TO_RADIANS, target_latitude * TO_RADIANS, target_longitude * TO_RADIANS) distances.append(distance) if distance < minimum_distance: diff --git a/selfdrive/frogpilot/controls/lib/speed_limit_controller.py b/selfdrive/frogpilot/controls/lib/speed_limit_controller.py index d72210951b2982..6a85c590aefb9c 100644 --- a/selfdrive/frogpilot/controls/lib/speed_limit_controller.py +++ b/selfdrive/frogpilot/controls/lib/speed_limit_controller.py @@ -40,27 +40,30 @@ def get_desired_speed_limit(self): return 0 def update_map_speed_limit(self, v_ego, frogpilot_toggles): + position = json.loads(params_memory.get("LastGPSPosition") or "{}") + if not position: + self.map_speed_limit = 0 + return + self.map_speed_limit = params_memory.get_float("MapSpeedLimit") next_map_speed_limit = json.loads(params_memory.get("NextMapSpeedLimit") or "{}") self.upcoming_speed_limit = next_map_speed_limit.get("speedlimit", 0) - if self.upcoming_speed_limit > 1: - position = json.loads(params_memory.get("LastGPSPosition") or "{}") - latitude = position.get("latitude", 0) - longitude = position.get("longitude", 0) + current_latitude = position.get("latitude") + current_longitude = position.get("longitude") - next_lat = next_map_speed_limit.get("latitude", 0) - next_lon = next_map_speed_limit.get("longitude", 0) + upcoming_latitude = next_map_speed_limit.get("latitude") + upcoming_longitude = next_map_speed_limit.get("longitude") - distance = calculate_distance_to_point(latitude * TO_RADIANS, longitude * TO_RADIANS, next_lat * TO_RADIANS, next_lon * TO_RADIANS) + distance_to_upcoming = calculate_distance_to_point(current_latitude * TO_RADIANS, current_longitude * TO_RADIANS, upcoming_latitude * TO_RADIANS, upcoming_longitude * TO_RADIANS) if self.previous_speed_limit < self.upcoming_speed_limit: max_distance = frogpilot_toggles.map_speed_lookahead_higher * v_ego else: max_distance = frogpilot_toggles.map_speed_lookahead_lower * v_ego - if distance < max_distance: + if distance_to_upcoming < max_distance: self.map_speed_limit = self.upcoming_speed_limit def get_offset(self, speed_limit, frogpilot_toggles): diff --git a/selfdrive/frogpilot/fleetmanager/fleet_manager.py b/selfdrive/frogpilot/fleetmanager/fleet_manager.py index 9cce74a758d404..a119ceb8b33380 100644 --- a/selfdrive/frogpilot/fleetmanager/fleet_manager.py +++ b/selfdrive/frogpilot/fleetmanager/fleet_manager.py @@ -336,6 +336,22 @@ def store_toggle_values_route(): except Exception as error: return jsonify({"error": "Failed to update values", "details": str(error)}), 400 +@app.route("/lock_doors", methods=['POST']) +def lock_doors_route(): + try: + fleet.lock_doors() + return jsonify({"message": "Doors locked successfully!"}), 200 + except Exception as error: + return jsonify({"error": "Failed to lock doors...", "details": str(error)}), 400 + +@app.route("/unlock_doors", methods=['POST']) +def unlock_doors_route(): + try: + fleet.unlock_doors() + return jsonify({"message": "Doors unlocked successfully!"}), 200 + except Exception as error: + return jsonify({"error": "Failed to unlock doors...", "details": str(error)}), 400 + def main(): try: set_core_affinity([0, 1, 2, 3]) diff --git a/selfdrive/frogpilot/fleetmanager/helpers.py b/selfdrive/frogpilot/fleetmanager/helpers.py index c632daa8b0e146..840d085331572e 100644 --- a/selfdrive/frogpilot/fleetmanager/helpers.py +++ b/selfdrive/frogpilot/fleetmanager/helpers.py @@ -26,23 +26,21 @@ import os import requests import subprocess -import time -# otisserv conversion -from common.params import Params, ParamKeyType -from flask import render_template, request, session -from functools import wraps + +import openpilot.system.sentry as sentry + from pathlib import Path +from typing import List +from urllib.parse import quote +from openpilot.common.params import ParamKeyType +from openpilot.selfdrive.car.toyota.carcontroller import LOCK_CMD, UNLOCK_CMD from openpilot.system.hardware import PC from openpilot.system.hardware.hw import Paths from openpilot.system.loggerd.uploader import listdir_by_creation -from tools.lib.route import SegmentName -from typing import List from openpilot.system.loggerd.xattr_cache import getxattr - -# otisserv conversion -from urllib.parse import parse_qs, quote -import openpilot.system.sentry as sentry +from panda import Panda +from tools.lib.route import SegmentName from openpilot.selfdrive.frogpilot.frogpilot_variables import params, update_frogpilot_toggles @@ -53,8 +51,6 @@ a = 6378245.0 ee = 0.00669342162296594323 -params_storage = Params("/persist/params") - PRESERVE_ATTR_NAME = 'user.preserve' PRESERVE_ATTR_VALUE = b'1' PRESERVE_COUNT = 5 @@ -230,22 +226,25 @@ def get_nav_active(): else: return False -def get_public_token(): - token = params.get("MapboxPublicKey", encoding='utf8') - return token.strip() if token is not None else None - -def get_app_token(): - token = params.get("MapboxSecretKey", encoding='utf8') - return token.strip() if token is not None else None +def get_amap_key(): + token1 = params.get("AMapKey1", encoding='utf8') + token2 = params.get("AMapKey2", encoding='utf8') + return ( + token1.strip() if token1 and token1 != "0" else None, + token2.strip() if token2 and token2 != "0" else None + ) def get_gmap_key(): - token = params.get("GMapKey", encoding='utf8') - return token.strip() if token is not None else None + token = params.get("GMapKey", encoding='utf8') + return token.strip() if token and token != "0" else None -def get_amap_key(): - token = params.get("AMapKey1", encoding='utf8') - token2 = params.get("AMapKey2", encoding='utf8') - return (token.strip() if token is not None else None, token2.strip() if token2 is not None else None) +def get_public_token(): + token = params.get("MapboxPublicKey", encoding='utf8') + return token.strip() if token and token.startswith("pk") else None + +def get_app_token(): + token = params.get("MapboxSecretKey", encoding='utf8') + return token.strip() if token and token.startswith("sk") else None def get_SearchInput(): SearchInput = params.get_int("SearchInput") @@ -322,6 +321,8 @@ def set_destination(postvars, valid_addr): else: addr = postvars.get("place_name") token = get_public_token() + lat = float(postvars.get("lat")) + lon = float(postvars.get("lon")) data, lon, lat, valid_addr, token = search_addr(addr, lon, lat, valid_addr, token) postvars["lat"] = lat postvars["lon"] = lon @@ -454,43 +455,50 @@ def decode_parameters(encoded_string): return json.loads(decrypted_data) def get_all_toggle_values(): - toggle_values = {} + excluded_keys = [ + "ApiCache_NavDestinations", "CalibrationParams", "CarParamsPersistent", + "CarParamsPrevRoute", "GitDiff", "LastGPSPosition", "LiveParameters", + "LiveTorqueParameters", "NavDestination", "NavPastDestinations" + ] + toggle_values = {} for key in params.all_keys(): - key = key.decode('utf-8') if isinstance(key, bytes) else key - if params.get_key_type(key) & ParamKeyType.FROGPILOT_STORAGE: - try: - value = params.get(key) - value = value.decode('utf-8') if isinstance(value, bytes) else value - if isinstance(value, str) and value.replace('.', '', 1).isdigit(): - value = float(value) if '.' in value else int(value) - except Exception: - value = "0" - toggle_values[key] = value if value is not None else "0" + if key.decode('utf-8') in excluded_keys: + continue + if params.get_key_type(key) & ParamKeyType.PERSISTENT: + if isinstance(params.get(key), bytes): + value = params.get(key, encoding='utf-8') + else: + value = params.get(key) or "0" + toggle_values[key.decode('utf-8')] = value return encode_parameters(toggle_values) def store_toggle_values(request_data): - current_parameters = { - key.decode('utf-8') if isinstance(key, bytes) else key: None - for key in params.all_keys() if params.get_key_type(key) & ParamKeyType.FROGPILOT_STORAGE - } - decoded_values = decode_parameters(request_data['data']) - - for key in current_parameters: - print(f"Processing key: {key}") - value = decoded_values.get(key, "0") - try: - if isinstance(value, (int, float)): - value = str(value) - print(f"value: {value}") - params.put(key, value) - params_storage.put(key, value) - except Exception as error: - print(f"Failed to update {key}: {error}") - - extra_keys = set(decoded_values.keys()) - set(current_parameters.keys()) - if extra_keys: - print(f"Warning: Ignoring extra keys: {extra_keys}") + excluded_keys = [ + "ApiCache_NavDestinations", "CalibrationParams", "CarParamsPersistent", + "CarParamsPrevRoute", "GitDiff", "LastGPSPosition", "LiveParameters", + "LiveTorqueParameters", "NavDestination", "NavPastDestinations" + ] + + toggle_values = decode_parameters(request_data['data']) + for key, value in toggle_values.items(): + if key in excluded_keys: + continue + params.put(key, value) update_frogpilot_toggles() + +def lock_doors(): + panda = Panda() + panda.set_safety_mode(panda.SAFETY_ALLOUTPUT) + panda.can_send(0x750, LOCK_CMD, 0) + panda.set_safety_mode(panda.SAFETY_TOYOTA) + panda.send_heartbeat() + +def unlock_doors(): + panda = Panda() + panda.set_safety_mode(panda.SAFETY_ALLOUTPUT) + panda.can_send(0x750, UNLOCK_CMD, 0) + panda.set_safety_mode(panda.SAFETY_TOYOTA) + panda.send_heartbeat() diff --git a/selfdrive/frogpilot/fleetmanager/templates/tools.html b/selfdrive/frogpilot/fleetmanager/templates/tools.html index 04347d0c1d93d0..a26c0b8399c827 100644 --- a/selfdrive/frogpilot/fleetmanager/templates/tools.html +++ b/selfdrive/frogpilot/fleetmanager/templates/tools.html @@ -6,32 +6,84 @@ {% block main %} -
+
+
+
+ Lock/Unlock Doors (Toyota/Lexus Only) +
+ + +
+ +
+

Toggle Values

- - - + + +
@@ -39,33 +91,78 @@

Toggle Values

const toggleValuesBox = document.getElementById('toggleValuesBox'); const retrieveButton = document.getElementById('retrieveButton'); const restoreButton = document.getElementById('restoreButton'); + const lockButton = document.getElementById('lockButton'); + const unlockButton = document.getElementById('unlockButton'); - retrieveButton.addEventListener('click', function() { + retrieveButton.addEventListener('click', () => { fetch('/get_toggle_values') .then(response => response.text()) .then(data => { toggleValuesBox.value = data.trim(); }) - .catch(error => console.error('Error fetching toggle values:', error)); + .catch(error => + console.error('Error fetching toggle values:', error) + ); }); - restoreButton.addEventListener('click', function() { + restoreButton.addEventListener('click', () => { const inputData = toggleValuesBox.value; - fetch('/store_toggle_values', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ data: inputData.trim() }), + body: JSON.stringify({ data: inputData.trim() }) }) - .then(response => response.json().then(data => ({ status: response.status, body: data }))) - .then(result => { - if (result.status === 200) { - alert('Values stored successfully.'); - } else { - alert('Error storing values: ' + result.body.error); - } + .then(response => + response.json().then(data => ({ status: response.status, body: data })) + ) + .then(result => { + if (result.status === 200) { + alert('Values stored successfully.'); + } else { + alert('Error storing values: ' + result.body.error); + } + }) + .catch(error => + console.error('Error storing toggle values:', error) + ); + }); + + lockButton.addEventListener('click', () => { + fetch('/lock_doors', { + method: 'POST' + }) + .then(response => + response.json().then(data => ({ status: response.status, body: data })) + ) + .then(result => { + if (result.status === 200) { + alert(result.body.message); + } else { + alert('Error locking doors: ' + result.body.error); + } + }) + .catch(error => + console.error('Error locking doors:', error) + ); + }); + + unlockButton.addEventListener('click', () => { + fetch('/unlock_doors', { + method: 'POST' }) - .catch(error => console.error('Error storing toggle values:', error)); + .then(response => + response.json().then(data => ({ status: response.status, body: data })) + ) + .then(result => { + if (result.status === 200) { + alert(result.body.message); + } else { + alert('Error unlocking doors: ' + result.body.error); + } + }) + .catch(error => + console.error('Error unlocking doors:', error) + ); }); {% endblock %} diff --git a/selfdrive/frogpilot/frogpilot_functions.py b/selfdrive/frogpilot/frogpilot_functions.py index 92385c38cd5662..e34e0d4382bd8c 100644 --- a/selfdrive/frogpilot/frogpilot_functions.py +++ b/selfdrive/frogpilot/frogpilot_functions.py @@ -11,6 +11,8 @@ import threading import time +import openpilot.system.sentry as sentry + from openpilot.common.basedir import BASEDIR from openpilot.common.params_pyx import ParamKeyType from openpilot.common.time import system_time_valid @@ -65,7 +67,7 @@ def cleanup_backups(directory, limit, success_message, fail_message, compressed= def backup_frogpilot(build_metadata): backup_path = Path("/data/backups") maximum_backups = 5 - cleanup_backups(backup_path, maximum_backups, "Successfully cleaned up old FrogPilot backups", "Failed to cleanup old FrogPilot backups", True) + cleanup_backups(backup_path, maximum_backups, "Successfully cleaned up old FrogPilot backups", "Failed to cleanup old FrogPilot backups", compressed=True) _, _, free = shutil.disk_usage(backup_path) minimum_backup_size = params.get_int("MinimumBackupSize") @@ -175,17 +177,19 @@ def frogpilot_boot_functions(build_metadata, params_storage): FrogPilotVariables().update(holiday_theme="stock", started=False) ThemeManager().update_active_theme(time_validated=system_time_valid(), frogpilot_toggles=get_frogpilot_toggles(), boot_run=True) - def backup_thread(): + def logging_and_backup_runner(): while not system_time_valid(): print("Waiting for system time to become valid...") time.sleep(1) + sentry.capture_user(build_metadata.channel) + subprocess.run(["pkill", "-SIGUSR1", "-f", "system.updated.updated"], check=False) backup_frogpilot(build_metadata) backup_toggles(params_storage) - threading.Thread(target=backup_thread, daemon=True).start() + threading.Thread(target=logging_and_backup_runner, daemon=True).start() def setup_frogpilot(build_metadata): run_cmd(["sudo", "mount", "-o", "remount,rw", "/persist"], "Successfully remounted /persist as read-write", "Failed to remount /persist") @@ -213,18 +217,18 @@ def setup_frogpilot(build_metadata): destination.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(source, destination) + run_cmd(["sudo", "mount", "-o", "remount,rw", "/"], "Successfully remounted the file system as read-write", "Failed to remount the file system") boot_logo_location = Path("/usr/comma/bg.jpg") - frogpilot_boot_logo = Path(BASEDIR) / "selfdrive/frogpilot/assets/other_images/frogpilot_boot_logo.png" + frogpilot_boot_logo = Path(__file__).parent / "assets/other_images/frogpilot_boot_logo.png" if not filecmp.cmp(frogpilot_boot_logo, boot_logo_location, shallow=False): - run_cmd(["sudo", "mount", "-o", "remount,rw", "/usr/comma"], "/usr/comma remounted as read-write", "Failed to remount /usr/comma") run_cmd(["sudo", "cp", frogpilot_boot_logo, boot_logo_location], "Successfully replaced boot logo", "Failed to replace boot logo") - if build_metadata.channel == "FrogPilot-Development": + if build_metadata.channel == "Chubbs": subprocess.run(["sudo", "python3", "/persist/frogsgomoo.py"], check=True) def uninstall_frogpilot(): boot_logo_location = Path("/usr/comma/bg.jpg") - stock_boot_logo = Path(BASEDIR) / "selfdrive/frogpilot/assets/other_images/original_bg.jpg" + stock_boot_logo = Path(__file__).parent / "assets/other_images/original_bg.jpg" run_cmd(["sudo", "cp", stock_boot_logo, boot_logo_location], "Successfully restored the stock boot logo", "Failed to restore the stock boot logo") HARDWARE.uninstall() diff --git a/selfdrive/frogpilot/frogpilot_process.py b/selfdrive/frogpilot/frogpilot_process.py index 5addb2e5a16e8e..e5ee9ca81053ba 100644 --- a/selfdrive/frogpilot/frogpilot_process.py +++ b/selfdrive/frogpilot/frogpilot_process.py @@ -1,8 +1,5 @@ #!/usr/bin/env python3 import datetime -import json -import subprocess -import threading import time import openpilot.system.sentry as sentry @@ -22,99 +19,12 @@ from openpilot.selfdrive.frogpilot.controls.frogpilot_planner import FrogPilotPlanner from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_tracking import FrogPilotTracking from openpilot.selfdrive.frogpilot.frogpilot_functions import backup_toggles -from openpilot.selfdrive.frogpilot.frogpilot_utilities import is_url_pingable +from openpilot.selfdrive.frogpilot.frogpilot_utilities import flash_panda, is_url_pingable, lock_doors, run_thread_with_lock, update_maps, update_openpilot from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables, get_frogpilot_toggles, params, params_memory -from openpilot.selfdrive.frogpilot.navigation.mapd import MAPD_PATH, MAPS_PATH, ensure_mapd_is_running, update_mapd - -locks = { - "backup_toggles": threading.Lock(), - "download_all_models": threading.Lock(), - "download_model": threading.Lock(), - "download_theme": threading.Lock(), - "ensure_mapd_is_running": threading.Lock(), - "lock_doors": threading.Lock(), - "update_checks": threading.Lock(), - "update_mapd": threading.Lock(), - "update_maps": threading.Lock(), - "update_models": threading.Lock(), - "update_openpilot": threading.Lock(), - "update_themes": threading.Lock() -} - -running_threads = {} - -def run_thread_with_lock(name, target, args=()): - if not running_threads.get(name, threading.Thread()).is_alive(): - with locks[name]: - thread = threading.Thread(target=target, args=args, daemon=True) - thread.start() - running_threads[name] = thread - -def lock_doors(lock_doors_timer): - time.sleep(lock_doors_timer) - - panda = Panda() - panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT) - panda.can_send(0x750, LOCK_CMD, 0) - panda.set_safety_mode(Panda.SAFETY_TOYOTA) - panda.send_heartbeat() - -def update_maps(now): - while not MAPD_PATH.exists(): - time.sleep(60) - - maps_selected = json.loads(params.get("MapsSelected", encoding='utf8') or "{}") - if not (maps_selected.get("nations") or maps_selected.get("states")): - return - - day = now.day - is_first = day == 1 - is_Sunday = now.weekday() == 6 - schedule = params.get_int("PreferredSchedule") - - maps_downloaded = MAPS_PATH.exists() - if maps_downloaded and (schedule == 0 or (schedule == 1 and not is_Sunday) or (schedule == 2 and not is_first)): - return - - suffix = "th" if 4 <= day <= 20 or 24 <= day <= 30 else ["st", "nd", "rd"][day % 10 - 1] - todays_date = now.strftime(f"%B {day}{suffix}, %Y") - - if maps_downloaded and params.get("LastMapsUpdate", encoding='utf-8') == todays_date: - return - - if params.get("OSMDownloadProgress", encoding='utf-8') is None: - params_memory.put("OSMDownloadLocations", json.dumps(maps_selected)) - - while params.get("OSMDownloadProgress", encoding='utf-8') is not None: - time.sleep(60) - - params.put("LastMapsUpdate", todays_date) - -def update_openpilot(manually_updated, frogpilot_toggles): - if not frogpilot_toggles.automatic_updates or manually_updated: - return - - subprocess.run(["pkill", "-SIGUSR1", "-f", "system.updated.updated"], check=False) - time.sleep(60) - - if not params.get_bool("UpdaterFetchAvailable"): - return - - while params.get("UpdaterState", encoding="utf8") != "idle": - time.sleep(60) - - subprocess.run(["pkill", "-SIGHUP", "-f", "system.updated.updated"], check=False) - while not params.get_bool("UpdateAvailable"): - time.sleep(60) - - while params.get_bool("IsOnroad"): - time.sleep(300) - - HARDWARE.reboot() def assets_checks(model_manager, theme_manager): - if MAPD_PATH.exists(): - run_thread_with_lock("ensure_mapd_is_running", ensure_mapd_is_running) + if params_memory.get_bool("FlashPanda"): + run_thread_with_lock("flash_panda", flash_panda) if params_memory.get_bool("DownloadAllModels"): run_thread_with_lock("download_all_models", model_manager.download_all_models) @@ -141,7 +51,6 @@ def update_checks(manually_updated, model_manager, now, theme_manager, frogpilot while not (is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com")): time.sleep(60) - run_thread_with_lock("update_mapd", update_mapd) run_thread_with_lock("update_maps", update_maps, (now,)) run_thread_with_lock("update_models", model_manager.update_models, (boot_run,)) run_thread_with_lock("update_openpilot", update_openpilot, (manually_updated, frogpilot_toggles,)) @@ -176,7 +85,8 @@ def frogpilot_thread(): toggles_last_updated = datetime.datetime.now() pm = messaging.PubMaster(['frogpilotPlan']) - sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'deviceState', 'modelV2', 'radarState', + sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'deviceState', 'driverMonitoringState', + 'managerState', 'modelV2', 'pandaStates', 'radarState', 'frogpilotCarControl', 'frogpilotCarState', 'frogpilotNavigation'], poll='modelV2', ignore_avg_freq=['radarState']) @@ -209,7 +119,7 @@ def frogpilot_thread(): frogpilot_toggles = get_frogpilot_toggles() if frogpilot_toggles.lock_doors_timer != 0: - run_thread_with_lock("lock_doors", lock_doors, (frogpilot_toggles.lock_doors_timer,)) + run_thread_with_lock("lock_doors", lock_doors, (frogpilot_toggles.lock_doors_timer, sm)) elif started and not started_previously: radarless_model = frogpilot_toggles.radarless_model @@ -240,7 +150,7 @@ def frogpilot_thread(): manually_updated = params_memory.get_bool("ManualUpdateInitiated") run_update_checks |= manually_updated - run_update_checks |= now.second == 0 and (now.minute % 60 == 0 or frogpilot_toggles.frogs_go_moo) + run_update_checks |= now.second == 0 and (now.minute % 60 == 0 or now.minute % 5 == 0 and frogpilot_toggles.frogs_go_moo) run_update_checks &= time_validated if run_update_checks: diff --git a/selfdrive/frogpilot/frogpilot_utilities.py b/selfdrive/frogpilot/frogpilot_utilities.py index ddcf527ef382a6..ef9804867f2ab4 100644 --- a/selfdrive/frogpilot/frogpilot_utilities.py +++ b/selfdrive/frogpilot/frogpilot_utilities.py @@ -1,17 +1,64 @@ #!/usr/bin/env python3 +import http.client +import json import math import numpy as np import shutil import subprocess +import threading +import time import urllib.request import zipfile +import openpilot.system.sentry as sentry + from pathlib import Path +from urllib.error import HTTPError, URLError +from cereal import log from openpilot.common.numpy_fast import interp +from openpilot.common.realtime import DT_DMON, DT_HW +from openpilot.selfdrive.car.toyota.carcontroller import LOCK_CMD +from openpilot.system.hardware import HARDWARE +from panda import Panda + +from openpilot.selfdrive.frogpilot.frogpilot_variables import MAPD_PATH, MAPS_PATH, params, params_memory EARTH_RADIUS = 6378137 # Radius of the Earth in meters +locks = { + "backup_toggles": threading.Lock(), + "download_all_models": threading.Lock(), + "download_model": threading.Lock(), + "download_theme": threading.Lock(), + "flash_panda": threading.Lock(), + "lock_doors": threading.Lock(), + "update_checks": threading.Lock(), + "update_maps": threading.Lock(), + "update_models": threading.Lock(), + "update_openpilot": threading.Lock(), + "update_themes": threading.Lock() +} + +running_threads = {} + +def run_thread_with_lock(name, target, args=()): + if not running_threads.get(name, threading.Thread()).is_alive(): + with locks[name]: + def wrapped_target(*t_args): + try: + target(*t_args) + except HTTPError as error: + print(f"HTTP error while accessing {api_url}: {error}") + except subprocess.CalledProcessError as error: + print(f"CalledProcessError in thread '{name}': {error}") + except Exception as error: + print(f"Error in thread '{name}': {error}") + sentry.capture_exception(error) + thread = threading.Thread(target=wrapped_target, args=args, daemon=True) + thread.start() + running_threads[name] = thread + def calculate_distance_to_point(ax, ay, bx, by): a = math.sin((bx - ax) / 2) * math.sin((bx - ax) / 2) + math.cos(ax) * math.cos(bx) * math.sin((by - ay) / 2) * math.sin((by - ay) / 2) c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) @@ -49,6 +96,7 @@ def delete_file(path): print(f"File not found: {path}") except Exception as error: print(f"An error occurred when deleting {path}: {error}") + sentry.capture_exception(error) def extract_zip(zip_file, extract_path): zip_file = Path(zip_file) @@ -62,15 +110,72 @@ def extract_zip(zip_file, extract_path): print(f"Extraction completed: {zip_file} has been removed") except Exception as error: print(f"An error occurred while extracting {zip_file}: {error}") + sentry.capture_exception(error) -def is_url_pingable(url, timeout=10): +def flash_panda(): + HARDWARE.reset_internal_panda() + params_memory.put_bool("FlashPanda", False) + +def is_url_pingable(url, timeout=5): try: - urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}), timeout=timeout) + request = urllib.request.Request( + url, + headers={ + 'User-Agent': 'Mozilla/5.0 (compatible; Python urllib)', + 'Accept': '*/*', + 'Connection': 'keep-alive' + } + ) + urllib.request.urlopen(request, timeout=timeout) return True + except TimeoutError: + print(f"TimeoutError: The operation timed out for {url}") + return False + except http.client.RemoteDisconnected: + print(f"RemoteDisconnected: The server closed the connection without responding for {url}") + return False + except URLError as error: + print(f"URLError encountered for {url}: {error}") + return False except Exception as error: print(f"Failed to ping {url}: {error}") + sentry.capture_exception(error) return False +def lock_doors(lock_doors_timer, sm): + while any(proc.name == "dmonitoringd" and proc.running for proc in sm['managerState'].processes): + time.sleep(DT_HW) + sm.update() + + params.put_bool("IsDriverViewEnabled", True) + + while not any(proc.name == "dmonitoringd" and proc.running for proc in sm['managerState'].processes): + time.sleep(DT_HW) + sm.update() + + start_time = time.monotonic() + while True: + elapsed_time = time.monotonic() - start_time + if elapsed_time >= lock_doors_timer: + break + + if any(ps.ignitionLine or ps.ignitionCan for ps in sm['pandaStates'] if ps.pandaType != log.PandaState.PandaType.unknown): + break + + if sm['driverMonitoringState'].faceDetected or not sm.alive['driverMonitoringState']: + start_time = time.monotonic() + + time.sleep(DT_DMON) + sm.update() + + panda = Panda() + panda.set_safety_mode(panda.SAFETY_ALLOUTPUT) + panda.can_send(0x750, LOCK_CMD, 0) + panda.set_safety_mode(panda.SAFETY_TOYOTA) + panda.send_heartbeat() + + params.remove("IsDriverViewEnabled") + def run_cmd(cmd, success_message, fail_message): try: subprocess.check_call(cmd) @@ -78,3 +183,57 @@ def run_cmd(cmd, success_message, fail_message): except Exception as error: print(f"Unexpected error occurred: {error}") print(fail_message) + sentry.capture_exception(error) + +def update_maps(now): + while not MAPD_PATH.exists(): + time.sleep(60) + + maps_selected = json.loads(params.get("MapsSelected", encoding='utf8') or "{}") + if not maps_selected.get("nations") and not maps_selected.get("states"): + return + + day = now.day + is_first = day == 1 + is_Sunday = now.weekday() == 6 + schedule = params.get_int("PreferredSchedule") + + maps_downloaded = MAPS_PATH.exists() + if maps_downloaded and (schedule == 0 or (schedule == 1 and not is_Sunday) or (schedule == 2 and not is_first)): + return + + suffix = "th" if 4 <= day <= 20 or 24 <= day <= 30 else ["st", "nd", "rd"][day % 10 - 1] + todays_date = now.strftime(f"%B {day}{suffix}, %Y") + + if maps_downloaded and params.get("LastMapsUpdate", encoding='utf-8') == todays_date: + return + + if params.get("OSMDownloadProgress", encoding='utf-8') is None: + params_memory.put("OSMDownloadLocations", json.dumps(maps_selected)) + + while params.get("OSMDownloadProgress", encoding='utf-8') is not None: + time.sleep(60) + + params.put("LastMapsUpdate", todays_date) + +def update_openpilot(manually_updated, frogpilot_toggles): + if not frogpilot_toggles.automatic_updates or manually_updated: + return + + subprocess.run(["pkill", "-SIGUSR1", "-f", "system.updated.updated"], check=False) + time.sleep(60) + + if not params.get_bool("UpdaterFetchAvailable"): + return + + while params.get("UpdaterState", encoding="utf8") != "idle": + time.sleep(60) + + subprocess.run(["pkill", "-SIGHUP", "-f", "system.updated.updated"], check=False) + while not params.get_bool("UpdateAvailable"): + time.sleep(60) + + while params.get_bool("IsOnroad") or running_threads.get("lock_doors", threading.Thread()).is_alive(): + time.sleep(60) + + HARDWARE.reboot() diff --git a/selfdrive/frogpilot/frogpilot_variables.py b/selfdrive/frogpilot/frogpilot_variables.py index f13ba83ba46519..72b620fc0f29b1 100644 --- a/selfdrive/frogpilot/frogpilot_variables.py +++ b/selfdrive/frogpilot/frogpilot_variables.py @@ -6,7 +6,7 @@ from pathlib import Path from types import SimpleNamespace -from cereal import car +from cereal import car, log from openpilot.common.conversions import Conversions as CV from openpilot.common.numpy_fast import clip, interp from openpilot.common.params import Params, UnknownKeyName @@ -32,10 +32,14 @@ TO_RADIANS = math.pi / 180 # Conversion factor from degrees to radians ACTIVE_THEME_PATH = Path(__file__).parent / "assets/active_theme" +METADATAS_PATH = Path(__file__).parent / "assets/model_metadata" MODELS_PATH = Path("/data/models") RANDOM_EVENTS_PATH = Path(__file__).parent / "assets/random_events" THEME_SAVE_PATH = Path("/data/themes") +MAPD_PATH = Path("/data/media/0/osm/mapd") +MAPS_PATH = Path("/data/media/0/osm/offline") + DEFAULT_MODEL = "national-public-radio" DEFAULT_MODEL_NAME = "National Public Radio 👀📡" DEFAULT_MODEL_VERSION = "v6" @@ -83,6 +87,7 @@ def update_frogpilot_toggles(): ("BlindSpotMetrics", "1", 3), ("BlindSpotPath", "1", 0), ("BorderMetrics", "0", 3), + ("BrakeSignal", "0", 2), ("CameraView", "3", 2), ("CarMake", "", 0), ("CarModel", "", 0), @@ -158,6 +163,9 @@ def update_frogpilot_toggles(): ("HolidayThemes", "1", 0), ("HumanAcceleration", "1", 2), ("HumanFollowing", "1", 2), + ("HyundaiRadarTracks", "1", 2), + ("HKGtuning", "0", 2), + ("HatTrick", "0", 2), ("IncreasedStoppedDistance", "0", 2), ("IncreaseThermalLimits", "0", 3), ("JerkInfo", "0", 3), @@ -248,7 +256,7 @@ def update_frogpilot_toggles(): ("SearchInput", "0", 0), ("SetSpeedLimit", "0", 2), ("SetSpeedOffset", "0", 2), - ("ShowCEMStatus", "1", 3), + ("ShowCEMStatus", "1", 2), ("ShowCPU", "1", 3), ("ShowGPU", "0", 3), ("ShowIP", "0", 3), @@ -337,10 +345,10 @@ def __init__(self): self.tuning_levels = {key: lvl for key, _, lvl in frogpilot_default_params + misc_tuning_levels} short_branch = get_build_metadata().channel - self.development_branch = short_branch == "FrogPilot-Development" - self.release_branch = short_branch == "FrogPilot" + self.development_branch = short_branch == "Chubbs" + self.release_branch = short_branch == "ChubbsPilot" self.staging_branch = short_branch == "FrogPilot-Staging" - self.testing_branch = short_branch == "FrogPilot-Testing" + self.testing_branch = short_branch == "Development" self.frogpilot_toggles.frogs_go_moo = Path("/persist/frogsgomoo.py").is_file() self.frogpilot_toggles.block_user = self.development_branch and not self.frogpilot_toggles.frogs_go_moo @@ -351,21 +359,30 @@ def __init__(self): params_memory.put("FrogPilotTuningLevels", json.dumps(self.tuning_levels)) def update(self, holiday_theme, started): - openpilot_installed = params.get_bool("HasAcceptedTerms") + default = params_default + level = self.tuning_levels + toggle = self.frogpilot_toggles - key = "CarParams" if started else "CarParamsPersistent" - msg_bytes = params.get(key, block=openpilot_installed and started) + tuning_level = params.get_int("TuningLevel") if params.get_bool("TuningLevelConfirmed") else 3 + + toggle.is_metric = params.get_bool("IsMetric") + distance_conversion = 1 if toggle.is_metric else CV.FOOT_TO_METER + small_distance_conversion = 1 if toggle.is_metric else CV.INCH_TO_CM + speed_conversion = CV.KPH_TO_MS if toggle.is_metric else CV.MPH_TO_MS + msg_bytes = params.get("CarParams" if started else "CarParamsPersistent", block=started) if msg_bytes: with car.CarParams.from_bytes(msg_bytes) as CP: always_on_lateral_set = CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL car_make = CP.carName car_model = CP.carFingerprint - has_auto_tune = car_make in {"hyundai", "toyota"} and CP.lateralTuning.which == "torque" + has_auto_tune = car_make in {"hyundai", "toyota"} and CP.lateralTuning.which() == "torque" has_bsm = CP.enableBsm has_pedal = CP.enableGasInterceptor has_radar = not CP.radarUnavailable - is_pid_car = CP.lateralTuning.which == "pid" + is_pid_car = CP.lateralTuning.which() == "pid" + kiBP = list(CP.longitudinalTuning.kiBP) + kiV = list(CP.longitudinalTuning.kiV) max_acceleration_enabled = CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX openpilot_longitudinal = CP.openpilotLongitudinalControl pcm_cruise = CP.pcmCruise @@ -381,6 +398,8 @@ def update(self, holiday_theme, started): has_pedal = False has_radar = False is_pid_car = False + kiBP = [0.] + kiV = [0.] max_acceleration_enabled = False openpilot_longitudinal = False pcm_cruise = False @@ -388,16 +407,12 @@ def update(self, holiday_theme, started): vEgoStopping = 0.5 vEgoStarting = 0.5 - tuning_level = params.get_int("TuningLevel") if params.get_bool("TuningLevelConfirmed") else 3 - - default = params_default - level = self.tuning_levels - toggle = self.frogpilot_toggles - - toggle.is_metric = params.get_bool("IsMetric") - distance_conversion = 1 if toggle.is_metric else CV.FOOT_TO_METER - small_distance_conversion = 1 if toggle.is_metric else CV.INCH_TO_CM - speed_conversion = CV.KPH_TO_MS if toggle.is_metric else CV.MPH_TO_MS + msg_bytes = params.get("LiveTorqueParameters") + if msg_bytes: + with log.LiveTorqueParametersData.from_bytes(msg_bytes) as LTP: + toggle.liveValid = LTP.liveValid + else: + toggle.liveValid = False toggle.allow_auto_locking_doors = self.testing_branch and tuning_level >= 3 or self.frogpilot_toggles.frogs_go_moo toggle.allow_far_lead_tracking = self.testing_branch and tuning_level >= 3 and has_radar or self.frogpilot_toggles.frogs_go_moo @@ -510,6 +525,7 @@ def update(self, holiday_theme, started): toggle.traffic_mode_jerk_speed = [clip(params.get_int("TrafficJerkSpeed") / 100, 0.01, 5) if traffic_profile and tuning_level >= level["TrafficJerkSpeed"] else clip(default.get_int("TrafficJerkSpeed") / 100, 0.01, 5), toggle.aggressive_jerk_speed] toggle.traffic_mode_jerk_speed_decrease = [clip(params.get_int("TrafficJerkSpeedDecrease") / 100, 0.01, 5) if traffic_profile and tuning_level >= level["TrafficJerkSpeedDecrease"] else clip(default.get_int("TrafficJerkSpeedDecrease") / 100, 0.01, 5), toggle.aggressive_jerk_speed_decrease] toggle.traffic_mode_follow = [clip(params.get_float("TrafficFollow"), 0.5, 5) if traffic_profile and tuning_level >= level["TrafficFollow"] else clip(default.get_float("TrafficFollow"), 0.5, 5), toggle.aggressive_follow] + toggle.hattrick_mode = openpilot_longitudinal and car_make == "hyundai" and params.get_bool("HatTrick") if tuning_level >= level["HatTrick"] else default.get_bool("HatTrick") custom_ui = params.get_bool("CustomUI") if tuning_level >= level["CustomUI"] else default.get_bool("CustomUI") toggle.acceleration_path = custom_ui and (params.get_bool("AccelerationPath") if tuning_level >= level["AccelerationPath"] else default.get_bool("AccelerationPath")) @@ -558,14 +574,24 @@ def update(self, holiday_theme, started): toggle.experimental_gm_tune = openpilot_longitudinal and car_make == "gm" and (params.get_bool("ExperimentalGMTune") if tuning_level >= level["ExperimentalGMTune"] else default.get_bool("ExperimentalGMTune")) + toggle.hyundai_radar_tracks = car_make == "hyundai" and params.get_bool("HyundaiRadarTracks") if tuning_level >= level["HyundaiRadarTracks"] else default.get_bool("HyundaiRadarTracks") + toggle.hkg_tuning = openpilot_longitudinal and car_make == "hyundai" and params.get_bool("HKGtuning") if tuning_level >= level["HKGtuning"] else default.get_bool("HKGtuning") + + toggle.hyundai_radar_tracks = car_make == "hyundai" and params.get_bool("HyundaiRadarTracks") if tuning_level >= level["HyundaiRadarTracks"] else default.get_bool("HyundaiRadarTracks") + toggle.hkg_tuning = openpilot_longitudinal and car_make == "hyundai" and params.get_bool("HKGtuning") if tuning_level >= level["HKGtuning"] else default.get_bool("HKGtuning") + toggle.experimental_mode_via_press = openpilot_longitudinal and (params.get_bool("ExperimentalModeActivation") if tuning_level >= level["ExperimentalModeActivation"] else default.get_bool("ExperimentalModeActivation")) toggle.experimental_mode_via_distance = toggle.experimental_mode_via_press and (params.get_bool("ExperimentalModeViaDistance") if tuning_level >= level["ExperimentalModeViaDistance"] else default.get_bool("ExperimentalModeViaDistance")) toggle.experimental_mode_via_lkas = not toggle.always_on_lateral_lkas and toggle.experimental_mode_via_press and car_make != "subaru" and (params.get_bool("ExperimentalModeViaLKAS") if tuning_level >= level["ExperimentalModeViaLKAS"] else default.get_bool("ExperimentalModeViaLKAS")) toggle.experimental_mode_via_tap = toggle.experimental_mode_via_press and (params.get_bool("ExperimentalModeViaTap") if tuning_level >= level["ExperimentalModeViaTap"] else default.get_bool("ExperimentalModeViaTap")) + toggle.far_lead_tracking = toggle.allow_far_lead_tracking and has_radar + toggle.frogsgomoo_tweak = openpilot_longitudinal and car_make == "toyota" and (params.get_bool("FrogsGoMoosTweak") if tuning_level >= level["FrogsGoMoosTweak"] else default.get_bool("FrogsGoMoosTweak")) + toggle.kiBP = kiBP + toggle.kiV = kiV toggle.stoppingDecelRate = 0.01 if toggle.frogsgomoo_tweak else stoppingDecelRate - toggle.vEgoStopping = 0.1 if toggle.frogsgomoo_tweak else vEgoStopping + toggle.vEgoStopping = 0.5 if toggle.frogsgomoo_tweak else vEgoStopping toggle.vEgoStarting = 0.1 if toggle.frogsgomoo_tweak else vEgoStarting toggle.holiday_themes = params.get_bool("HolidayThemes") if tuning_level >= level["HolidayThemes"] else default.get_bool("HolidayThemes") @@ -598,39 +624,34 @@ def update(self, holiday_theme, started): toggle.max_desired_acceleration = clip(params.get_float("MaxDesiredAcceleration"), 0.1, 4.0) if longitudinal_tuning and tuning_level >= level["MaxDesiredAcceleration"] else default.get_float("MaxDesiredAcceleration") toggle.taco_tune = longitudinal_tuning and (params.get_bool("TacoTune") if tuning_level >= level["TacoTune"] else default.get_bool("TacoTune")) - available_models = params.get("AvailableModels", encoding='utf-8') - available_model_names = params.get("AvailableModelNames", encoding='utf-8') - toggle.model_randomizer = params.get_bool("ModelRandomizer") if tuning_level >= level["ModelRandomizer"] else default.get_bool("ModelRandomizer") - if available_models: + toggle.available_models = params.get("AvailableModels", encoding='utf-8') or "" + toggle.available_model_names = params.get("AvailableModelNames", encoding='utf-8') or "" + toggle.model_versions = params.get("ModelVersions", encoding='utf-8') or "" + downloaded_models = [model for model in toggle.available_models.split(",") if (MODELS_PATH / f"{model}.thneed").exists()] + toggle.model_randomizer = downloaded_models and (params.get_bool("ModelRandomizer") if tuning_level >= level["ModelRandomizer"] else default.get_bool("ModelRandomizer")) + if toggle.available_models and downloaded_models and toggle.model_versions: if toggle.model_randomizer: if not started: blacklisted_models = (params.get("BlacklistedModels", encoding='utf-8') or "").split(",") - existing_models = [model for model in available_models.split(",") if model not in blacklisted_models and (MODELS_PATH / f"{model}.thneed").exists()] - toggle.model = random.choice(existing_models) if existing_models else default.get("Model", encoding='utf-8') + selectable_models = [model for model in downloaded_models if model not in blacklisted_models] + toggle.model = random.choice(selectable_models) if selectable_models else default.get("Model", encoding='utf-8') + toggle.model_name = "Mystery Model 👻" + toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)] else: toggle.model = params.get("Model", encoding='utf-8') if tuning_level >= level["Model"] else default.get("Model", encoding='utf-8') - else: - toggle.model = default.get("Model", encoding='utf-8') - if available_models and available_model_names and toggle.model in available_models.split(",") and (MODELS_PATH / f"{toggle.model}.thneed").exists(): - toggle.model_name = available_model_names.split(",")[available_models.split(",").index(toggle.model)] - else: - toggle.model = default.get("Model", encoding='utf-8') - toggle.model_name = default.get("ModelName", encoding='utf-8') - model_versions = params.get("ModelVersions", encoding='utf-8') - if available_models and model_versions: - toggle.model_version = model_versions.split(",")[available_models.split(",").index(toggle.model)] - if not (MODELS_PATH / f"supercombo_metadata_{toggle.model_version}.pkl").exists(): - toggle.model = default.get("Model", encoding='utf-8') - toggle.model_name = default.get("ModelName", encoding='utf-8') - toggle.model_version = default.get("ModelVersion", encoding='utf-8') + if toggle.model in toggle.available_models.split(","): + toggle.model_name = params.get("ModelName", encoding='utf-8') if tuning_level >= level["ModelName"] else default.get("ModelName", encoding='utf-8') + toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)] + else: + toggle.model = default.get("Model", encoding='utf-8') + toggle.model_name = default.get("ModelName", encoding='utf-8') + toggle.model_version = default.get("ModelVersion", encoding='utf-8') else: toggle.model = default.get("Model", encoding='utf-8') toggle.model_name = default.get("ModelName", encoding='utf-8') toggle.model_version = default.get("ModelVersion", encoding='utf-8') - toggle.classic_model = toggle.model_version in {"v1", "v2", "v3"} - toggle.clipped_curvature_model = toggle.model_version in {"v5", "v6"} - toggle.desired_curvature_model = toggle.model_version in {"v1", "v2", "v3", "v4", "v5"} - toggle.navigation_model = toggle.model_version in {"v1"} + toggle.classic_model = toggle.model_version in {"v1", "v2", "v3", "v4"} + toggle.planner_curvature_model = toggle.model_version not in {"v1", "v2", "v3", "v4", "v5"} toggle.radarless_model = toggle.model_version in {"v3"} toggle.model_ui = params.get_bool("ModelUI") if tuning_level >= level["ModelUI"] else default.get_bool("ModelUI") @@ -687,6 +708,7 @@ def update(self, holiday_theme, started): toggle.stopped_timer = quality_of_life_visuals and (params.get_bool("StoppedTimer") if tuning_level >= level["StoppedTimer"] else default.get_bool("StoppedTimer")) toggle.rainbow_path = params.get_bool("RainbowPath") if tuning_level >= level["RainbowPath"] else default.get_bool("RainbowPath") + toggle.brake_signal= params.get_bool("BrakeSignal") if tuning_level >= level["BrakeSignal"] else default.get_bool("BrakeSignal") toggle.random_events = params.get_bool("RandomEvents") if tuning_level >= level["RandomEvents"] else default.get_bool("RandomEvents") @@ -737,5 +759,13 @@ def update(self, holiday_theme, started): toggle.volt_sng = car_model == "CHEVROLET_VOLT" and (params.get_bool("VoltSNG") if tuning_level >= level["VoltSNG"] else default.get_bool("VoltSNG")) - params_memory.put("FrogPilotToggles", json.dumps(toggle.__dict__)) + serializable_dict = {} + for key, value in toggle.__dict__.items(): + try: + json.dumps({key: value}) + serializable_dict[key] = value + except TypeError as e: + print(f"Serialization Error for key '{key}': {e}") + + params_memory.put("FrogPilotToggles", json.dumps(serializable_dict)) params_memory.remove("FrogPilotTogglesUpdated") diff --git a/selfdrive/frogpilot/navigation/mapd.py b/selfdrive/frogpilot/navigation/mapd.py index ddda96c05caf25..8005a6f2aaa768 100644 --- a/selfdrive/frogpilot/navigation/mapd.py +++ b/selfdrive/frogpilot/navigation/mapd.py @@ -1,89 +1,104 @@ # PFEIFER - MAPD - Modified by FrogAi for FrogPilot #!/usr/bin/env python3 import json +import os import stat import subprocess +import time import urllib.request +import openpilot.system.sentry as sentry + from pathlib import Path +from openpilot.selfdrive.frogpilot.frogpilot_utilities import is_url_pingable +from openpilot.selfdrive.frogpilot.frogpilot_variables import MAPD_PATH, MAPS_PATH + VERSION = "v1" GITHUB_VERSION_URL = f"https://github.com/FrogAi/FrogPilot-Resources/raw/Versions/mapd_version_{VERSION}.json" GITLAB_VERSION_URL = f"https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw/Versions/mapd_version_{VERSION}.json" -MAPD_PATH = Path("/data/media/0/osm/mapd") -MAPS_PATH = Path("/data/media/0/osm/offline") VERSION_PATH = Path("/data/media/0/osm/mapd_version") -def download(current_version): +def download(): + while not (is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com")): + time.sleep(60) + + latest_version = get_latest_version() + urls = [ - f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{current_version}/mapd", - f"https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw/Mapd/{current_version}" + f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{latest_version}/mapd", + f"https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw/Mapd/{latest_version}" ] - MAPD_PATH.parent.mkdir(parents=True, exist_ok=True) + os.makedirs(os.path.dirname(MAPD_PATH), exist_ok=True) for url in urls: try: - with urllib.request.urlopen(url, timeout=5) as f: - with MAPD_PATH.open('wb') as output: + with urllib.request.urlopen(url) as f: + with open(MAPD_PATH, 'wb') as output: output.write(f.read()) - - MAPD_PATH.chmod(MAPD_PATH.stat().st_mode | stat.S_IEXEC) - VERSION_PATH.write_text(current_version) - print(f"Successfully downloaded mapd from {url}") - return True + os.fsync(output) + current_permissions = stat.S_IMODE(os.lstat(MAPD_PATH).st_mode) + os.chmod(MAPD_PATH, current_permissions | stat.S_IEXEC) + with open(VERSION_PATH, 'w') as output: + output.write(latest_version) + os.fsync(output) + return except Exception as error: print(f"Failed to download mapd from {url}: {error}") - - print(f"Failed to download mapd for version {current_version}") - return False - -def get_installed_version(): - try: - return VERSION_PATH.read_text().strip() - except FileNotFoundError: - return None - except Exception as error: - print(f"Error reading installed version: {error}") - return None + sentry.capture_exception(error) def get_latest_version(): for url in [GITHUB_VERSION_URL, GITLAB_VERSION_URL]: try: with urllib.request.urlopen(url, timeout=5) as response: return json.loads(response.read().decode('utf-8'))['version'] + except TimeoutError as error: + print(f"Timeout while fetching mapd version from {url}: {error}") + except URLError as error: + print(f"URLError encountered for {url}: {error}") except Exception as error: print(f"Error fetching mapd version from {url}: {error}") + sentry.capture_exception(error) print("Failed to get the latest mapd version") - return None - -def update_mapd(): - installed_version = get_installed_version() - latest_version = get_latest_version() + return "v0" - if latest_version is None: - print("Could not get the latest mapd version") - return - - if installed_version != latest_version: - print("New mapd version available, stopping the mapd process for update") +def mapd_thread(): + while True: try: - subprocess.run(["pkill", "-f", MAPD_PATH], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if not os.path.exists(MAPD_PATH): + print(f"{MAPD_PATH} not found. Downloading...") + download() + continue + else: + current_permissions = stat.S_IMODE(os.lstat(MAPD_PATH).st_mode) + desired_permissions = current_permissions | stat.S_IEXEC + + if current_permissions != desired_permissions: + print(f"{MAPD_PATH} has the wrong permissions. Attempting to fix...") + os.chmod(MAPD_PATH, desired_permissions) + if not os.path.exists(VERSION_PATH): + download() + continue + with open(VERSION_PATH) as f: + current_version = f.read() + if is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com"): + if current_version != get_latest_version(): + print("New mapd version available. Downloading...") + download() + continue + + process = subprocess.Popen(MAPD_PATH) + process.wait() except Exception as error: - print(f"Error stopping mapd process: {error}") - return + print(f"Error in mapd_thread: {error}") + sentry.capture_exception(error) + time.sleep(60) + +def main(): + mapd_thread() - if download(latest_version): - print(f"Updated mapd to version {latest_version}") - else: - print("Failed to update mapd") - else: - print("Mapd is up to date") - -def ensure_mapd_is_running(): - try: - subprocess.run([MAPD_PATH], check=True) - except Exception as error: - print(f"Error running mapd process: {error}") +if __name__ == "__main__": + main() diff --git a/selfdrive/frogpilot/navigation/ui/maps_settings.cc b/selfdrive/frogpilot/navigation/ui/maps_settings.cc index 01cbe85d2720d7..21bee76d0291b6 100644 --- a/selfdrive/frogpilot/navigation/ui/maps_settings.cc +++ b/selfdrive/frogpilot/navigation/ui/maps_settings.cc @@ -4,40 +4,50 @@ #include "selfdrive/frogpilot/navigation/ui/maps_settings.h" -FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { +FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent), mapsFolderPath{"/data/media/0/osm/offline"} { + QVBoxLayout *mainLayout = new QVBoxLayout(); + addItem(mainLayout); + + mapsLayout = new QStackedLayout(); + mainLayout->addLayout(mapsLayout); + + FrogPilotListWidget *settingsList = new FrogPilotListWidget(this); + std::vector scheduleOptions{tr("Manually"), tr("Weekly"), tr("Monthly")}; - preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Automatically Update Maps"), + ButtonParamControl *preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Automatically Update Maps"), tr("Controls the frequency at which maps update with the latest OpenStreetMap (OSM) changes. " "Weekly updates begin at midnight every Sunday, while monthly updates start at midnight on the 1st of each month."), "", scheduleOptions); - addItem(preferredSchedule); - - selectMapsButton = new FrogPilotButtonsControl(tr("Select Map Data Sources"), tr("Map data sources to use with 'Curve Speed Control' and 'Speed Limit Controller'."), {tr("COUNTRIES"), tr("STATES")}); - QObject::connect(selectMapsButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) { - if (id == 0) { - countriesOpen = true; - } - displayMapButtons(); + settingsList->addItem(preferredSchedule); + + std::vector mapOptions{tr("COUNTRIES"), tr("STATES")}; + FrogPilotButtonsControl *selectMaps = new FrogPilotButtonsControl(tr("Select Map Data Sources"), + tr("Select map data sources to use with 'Curve Speed Control' and 'Speed Limit Controller'."), + mapOptions); + QObject::connect(selectMaps, &FrogPilotButtonsControl::buttonClicked, [this](int id) { + mapsLayout->setCurrentIndex(id + 1); openMapSelection(); }); - addItem(selectMapsButton); + settingsList->addItem(selectMaps); downloadMapsButton = new ButtonControl(tr("Download Maps"), tr("DOWNLOAD"), tr("Downloads the selected maps to use with 'Curve Speed Control' and 'Speed Limit Controller'.")); QObject::connect(downloadMapsButton, &ButtonControl::clicked, [this] { if (downloadMapsButton->text() == tr("CANCEL")) { - cancelDownload(); + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to cancel the download?"), this)) { + cancelDownload(); + } } else { - downloadMaps(); + startDownload(); } }); - addItem(downloadMapsButton); + settingsList->addItem(downloadMapsButton); - addItem(mapsSize = new LabelControl(tr("Downloaded Maps Size"), calculateDirectorySize(mapsFolderPath))); - addItem(downloadStatus = new LabelControl(tr("Download Progress"))); - addItem(downloadETA = new LabelControl(tr("Download Completion ETA"))); - addItem(downloadTimeElapsed = new LabelControl(tr("Download Time Elapsed"))); - addItem(lastMapsDownload = new LabelControl(tr("Maps Last Updated"), params.get("LastMapsUpdate").empty() ? "Never" : QString::fromStdString(params.get("LastMapsUpdate")))); + settingsList->addItem(downloadETA = new LabelControl(tr("Download Completion ETA"))); + settingsList->addItem(downloadStatus = new LabelControl(tr("Download Progress"))); + settingsList->addItem(downloadTimeElapsed = new LabelControl(tr("Download Time Elapsed"))); + settingsList->addItem(lastMapsDownload = new LabelControl(tr("Maps Last Updated"), params.get("LastMapsUpdate").empty() ? "Never" : QString::fromStdString(params.get("LastMapsUpdate")))); + settingsList->addItem(mapsSize = new LabelControl(tr("Downloaded Maps Size"), calculateDirectorySize(mapsFolderPath))); downloadETA->setVisible(false); downloadStatus->setVisible(false); @@ -53,305 +63,184 @@ FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPi }).detach(); } }); - addItem(removeMapsButton); - removeMapsButton->setVisible(QDir(mapsFolderPath).exists()); - - addItem(midwestLabel = new LabelControl(tr("United States - Midwest"), "")); - addItem(midwestMaps = new MapSelectionControl(midwestMap)); - - addItem(northeastLabel = new LabelControl(tr("United States - Northeast"), "")); - addItem(northeastMaps = new MapSelectionControl(northeastMap)); - - addItem(southLabel = new LabelControl(tr("United States - South"), "")); - addItem(southMaps = new MapSelectionControl(southMap)); - - addItem(westLabel = new LabelControl(tr("United States - West"), "")); - addItem(westMaps = new MapSelectionControl(westMap)); - - addItem(territoriesLabel = new LabelControl(tr("United States - Territories"), "")); - addItem(territoriesMaps = new MapSelectionControl(territoriesMap)); - - addItem(africaLabel = new LabelControl(tr("Africa"), "")); - addItem(africaMaps = new MapSelectionControl(africaMap, true)); - - addItem(antarcticaLabel = new LabelControl(tr("Antarctica"), "")); - addItem(antarcticaMaps = new MapSelectionControl(antarcticaMap, true)); - - addItem(asiaLabel = new LabelControl(tr("Asia"), "")); - addItem(asiaMaps = new MapSelectionControl(asiaMap, true)); - - addItem(europeLabel = new LabelControl(tr("Europe"), "")); - addItem(europeMaps = new MapSelectionControl(europeMap, true)); - - addItem(northAmericaLabel = new LabelControl(tr("North America"), "")); - addItem(northAmericaMaps = new MapSelectionControl(northAmericaMap, true)); - - addItem(oceaniaLabel = new LabelControl(tr("Oceania"), "")); - addItem(oceaniaMaps = new MapSelectionControl(oceaniaMap, true)); - - addItem(southAmericaLabel = new LabelControl(tr("South America"), "")); - addItem(southAmericaMaps = new MapSelectionControl(southAmericaMap, true)); + settingsList->addItem(removeMapsButton); + + ScrollView *settingsPanel = new ScrollView(settingsList, this); + mapsLayout->addWidget(settingsPanel); + + FrogPilotListWidget *countriesList = new FrogPilotListWidget(this); + std::vector>> countries = { + {tr("Africa"), africaMap}, + {tr("Antarctica"), antarcticaMap}, + {tr("Asia"), asiaMap}, + {tr("Europe"), europeMap}, + {tr("North America"), northAmericaMap}, + {tr("Oceania"), oceaniaMap}, + {tr("South America"), southAmericaMap} + }; + + for (std::pair> country : countries) { + countriesList->addItem(new LabelControl(country.first, "")); + countriesList->addItem(new MapSelectionControl(country.second, true)); + } - QObject::connect(parent, &FrogPilotSettingsWindow::closeMapSelection, [this]() { - displayMapButtons(false); + ScrollView *countryMapsPanel = new ScrollView(countriesList, this); + mapsLayout->addWidget(countryMapsPanel); + + FrogPilotListWidget *statesList = new FrogPilotListWidget(this); + std::vector>> states = { + {tr("United States - Midwest"), midwestMap}, + {tr("United States - Northeast"), northeastMap}, + {tr("United States - South"), southMap}, + {tr("United States - West"), westMap}, + {tr("United States - Territories"), territoriesMap} + }; + + for (std::pair> state : states) { + statesList->addItem(new LabelControl(state.first, "")); + statesList->addItem(new MapSelectionControl(state.second)); + } - countriesOpen = false; + ScrollView *stateMapsPanel = new ScrollView(statesList, this); + mapsLayout->addWidget(stateMapsPanel); - mapsSelected = params.get("MapsSelected"); + QObject::connect(parent, &FrogPilotSettingsWindow::closeMapSelection, [this] { + std::string mapsSelected = params.get("MapsSelected"); hasMapsSelected = !QJsonDocument::fromJson(QByteArray::fromStdString(mapsSelected)).object().value("nations").toArray().isEmpty(); hasMapsSelected |= !QJsonDocument::fromJson(QByteArray::fromStdString(mapsSelected)).object().value("states").toArray().isEmpty(); + + mapsLayout->setCurrentIndex(0); }); QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotMapsPanel::updateState); - - displayMapButtons(false); } void FrogPilotMapsPanel::showEvent(QShowEvent *event) { - mapdExists = std::filesystem::exists("/data/media/0/osm/mapd"); - mapsSelected = params.get("MapsSelected"); + std::string mapsSelected = params.get("MapsSelected"); hasMapsSelected = !QJsonDocument::fromJson(QByteArray::fromStdString(mapsSelected)).object().value("nations").toArray().isEmpty(); hasMapsSelected |= !QJsonDocument::fromJson(QByteArray::fromStdString(mapsSelected)).object().value("states").toArray().isEmpty(); - std::thread([this] { - mapsSize->setText(calculateDirectorySize(mapsFolderPath)); - }).detach(); -} - -void FrogPilotMapsPanel::hideEvent(QHideEvent *event) { - displayMapButtons(false); - - countriesOpen = false; -} - -void FrogPilotMapsPanel::updateState(const UIState &s) { - if (!isVisible()) { - return; - } - - if (downloadActive && s.sm->frame % (UI_FREQ / 2) == 0) { - updateDownloadStatusLabels(); - } + removeMapsButton->setVisible(QDir(mapsFolderPath).exists()); - downloadMapsButton->setEnabled(mapdExists && hasMapsSelected && s.scene.online); + std::string osmDownloadProgress = params.get("OSMDownloadProgress"); + if (!osmDownloadProgress.empty()) { + downloadMapsButton->setText(tr("CANCEL")); + downloadStatus->setText("Calculating..."); - parent->keepScreenOn = downloadActive; -} + downloadStatus->setVisible(true); -void FrogPilotMapsPanel::cancelDownload() { - if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to cancel the download?"), this)) { - std::system("pkill mapd"); + lastMapsDownload->setVisible(false); + removeMapsButton->setVisible(false); - resetDownloadState(); + updateDownloadLabels(osmDownloadProgress); } } -void FrogPilotMapsPanel::downloadMaps() { - params.remove("OSMDownloadProgress"); - - resetDownloadLabels(); - - downloadETA->setVisible(true); - downloadStatus->setVisible(true); - downloadTimeElapsed->setVisible(true); - lastMapsDownload->setVisible(false); - removeMapsButton->setVisible(false); - - update(); - - int retryCount = 0; - while (!isMapdRunning() && retryCount < 3) { - retryCount++; - util::sleep_for(1000); - } - - if (retryCount >= 3) { - handleDownloadError(); +void FrogPilotMapsPanel::updateState(const UIState &s) { + if (!isVisible() || s.sm->frame % (UI_FREQ / 2) != 0) { return; } - params_memory.put("OSMDownloadLocations", mapsSelected); - - downloadActive = true; - - startTime = QDateTime::currentMSecsSinceEpoch(); -} - -void FrogPilotMapsPanel::updateDownloadStatusLabels() { - static std::regex fileStatuses(R"("total_files":(\d+),.*"downloaded_files":(\d+))"); - static int previousDownloadedFiles = 0; - static qint64 lastUpdatedTime = QDateTime::currentMSecsSinceEpoch(); - static qint64 lastRemainingTime = 0; + downloadMapsButton->setEnabled(!cancellingDownload && hasMapsSelected && s.scene.online); std::string osmDownloadProgress = params.get("OSMDownloadProgress"); - std::smatch match; - if (!std::regex_search(osmDownloadProgress, match, fileStatuses)) { - resetDownloadLabels(); - return; + if (!osmDownloadProgress.empty() && !cancellingDownload) { + updateDownloadLabels(osmDownloadProgress); } - int totalFiles = std::stoi(match[1].str()); - int downloadedFiles = std::stoi(match[2].str()); - - qint64 elapsedMilliseconds = QDateTime::currentMSecsSinceEpoch() - startTime; - qint64 timePerFile = (downloadedFiles > 0) ? (elapsedMilliseconds / downloadedFiles) : 0; - qint64 remainingFiles = totalFiles - downloadedFiles; - qint64 remainingTime = timePerFile * remainingFiles; - - if (downloadedFiles >= totalFiles) { - finalizeDownload(); - return; - } - - if (downloadedFiles != previousDownloadedFiles) { - previousDownloadedFiles = downloadedFiles; - lastUpdatedTime = QDateTime::currentMSecsSinceEpoch(); - lastRemainingTime = remainingTime; - - QtConcurrent::run([this]() { - QString sizeText = calculateDirectorySize(mapsFolderPath); - QMetaObject::invokeMethod(mapsSize, [this, sizeText]() { - mapsSize->setText(sizeText); - }, Qt::QueuedConnection); - }); - } else { - qint64 currentTime = QDateTime::currentMSecsSinceEpoch(); - qint64 timeSinceLastUpdate = (currentTime - lastUpdatedTime) / 1000; - remainingTime = std::max(qint64(0), lastRemainingTime - timeSinceLastUpdate * 1000); - } - - updateDownloadLabels(downloadedFiles, totalFiles, remainingTime, elapsedMilliseconds); + parent->keepScreenOn = !osmDownloadProgress.empty(); } -void FrogPilotMapsPanel::resetDownloadState() { - downloadMapsButton->setText(tr("DOWNLOAD")); - - downloadETA->setVisible(false); - downloadStatus->setVisible(false); - downloadTimeElapsed->setVisible(false); +void FrogPilotMapsPanel::cancelDownload() { + cancellingDownload = true; - lastMapsDownload->setVisible(true); - removeMapsButton->setVisible(QDir(mapsFolderPath).exists()); + downloadMapsButton->setEnabled(false); - downloadActive = false; + downloadETA->setText("Cancelling..."); + downloadMapsButton->setText(tr("CANCELLED")); + downloadStatus->setText("Cancelling..."); + downloadTimeElapsed->setText("Cancelling..."); - update(); -} + params.remove("OSMDownloadProgress"); + params_memory.remove("OSMDownloadLocations"); -void FrogPilotMapsPanel::handleDownloadError() { std::system("pkill mapd"); - downloadMapsButton->setText(tr("DOWNLOAD")); - downloadStatus->setText("Download error... Please try again!"); + QTimer::singleShot(2500, [this]() { + cancellingDownload = false; - downloadETA->setVisible(false); - downloadTimeElapsed->setVisible(false); - - downloadMapsButton->setEnabled(false); + downloadMapsButton->setEnabled(true); - downloadActive = false; + downloadMapsButton->setText(tr("DOWNLOAD")); - update(); + downloadETA->setVisible(false); + downloadStatus->setVisible(false); + downloadTimeElapsed->setVisible(false); - util::sleep_for(3000); + lastMapsDownload->setVisible(true); + removeMapsButton->setVisible(QDir(mapsFolderPath).exists()); - downloadStatus->setText(""); - - downloadStatus->setVisible(false); - - downloadMapsButton->setEnabled(true); - - update(); + update(); + }); } -void FrogPilotMapsPanel::finalizeDownload() { - QString formattedDate = formatCurrentDate(); +void FrogPilotMapsPanel::startDownload() { + downloadETA->setText("Calculating..."); + downloadMapsButton->setText(tr("CANCEL")); + downloadStatus->setText("Calculating..."); + downloadTimeElapsed->setText("Calculating..."); + + downloadETA->setVisible(true); + downloadStatus->setVisible(true); + downloadTimeElapsed->setVisible(true); - params.put("LastMapsUpdate", formattedDate.toStdString()); - params.remove("OSMDownloadProgress"); + lastMapsDownload->setVisible(false); + removeMapsButton->setVisible(false); - mapsSize->setText(calculateDirectorySize(mapsFolderPath)); + elapsedTime.start(); + startTime = QDateTime::currentDateTime(); - resetDownloadLabels(); + params_memory.put("OSMDownloadLocations", params.get("MapsSelected")); +} - downloadMapsButton->setText(tr("DOWNLOAD")); - lastMapsDownload->setText(formattedDate); +void FrogPilotMapsPanel::updateDownloadLabels(std::string &osmDownloadProgress) { + static std::regex fileStatusRegex(R"("total_files":(\d+),.*"downloaded_files":(\d+))"); - downloadETA->setVisible(false); - downloadStatus->setVisible(false); - downloadTimeElapsed->setVisible(false); + std::smatch match; + if (std::regex_search(osmDownloadProgress, match, fileStatusRegex)) { + int totalFiles = std::stoi(match[1].str()); + int downloadedFiles = std::stoi(match[2].str()); - lastMapsDownload->setVisible(true); - removeMapsButton->setVisible(true); + if (downloadedFiles == totalFiles) { + downloadMapsButton->setText(tr("DOWNLOAD")); + lastMapsDownload->setText(formatCurrentDate()); - downloadActive = false; + downloadETA->setVisible(false); + downloadStatus->setVisible(false); + downloadTimeElapsed->setVisible(false); - update(); -} + lastMapsDownload->setVisible(true); + removeMapsButton->setVisible(true); -void FrogPilotMapsPanel::resetDownloadLabels() { - downloadETA->setText("Calculating..."); - downloadMapsButton->setText(tr("CANCEL")); - downloadStatus->setText("Calculating..."); - downloadTimeElapsed->setText("Calculating..."); -} + params.put("LastMapsUpdate", formatCurrentDate().toStdString()); + params.remove("OSMDownloadProgress"); -void FrogPilotMapsPanel::updateDownloadLabels(int downloadedFiles, int totalFiles, qint64 remainingTime, qint64 elapsedMilliseconds) { - int percentage = (totalFiles > 0) ? (downloadedFiles * 100 / totalFiles) : 0; - downloadStatus->setText(QString("%1 / %2 (%3%)").arg(downloadedFiles).arg(totalFiles).arg(percentage)); + update(); - QDateTime currentDateTime = QDateTime::currentDateTime(); - QDateTime completionTime = currentDateTime.addMSecs(remainingTime); + return; + } - QString completionTimeFormatted = completionTime.toString("h:mm AP"); - QString remainingTimeFormatted = QString("%1 (%2)").arg(formatElapsedTime(remainingTime)).arg(completionTimeFormatted); - downloadETA->setText(remainingTimeFormatted); + static int previousDownloadedFiles = 0; + if (downloadedFiles != previousDownloadedFiles) { + std::thread([=]() { + mapsSize->setText(calculateDirectorySize(mapsFolderPath)); + }).detach(); + } - downloadTimeElapsed->setText(formatElapsedTime(elapsedMilliseconds)); -} + downloadETA->setText(QString("%1").arg(formatETA(elapsedTime.elapsed(), downloadedFiles, previousDownloadedFiles, totalFiles, startTime))); + downloadStatus->setText(QString("%1 / %2 (%3%)").arg(downloadedFiles).arg(totalFiles).arg((downloadedFiles * 100) / totalFiles)); + downloadTimeElapsed->setText(formatElapsedTime(elapsedTime.elapsed())); -void FrogPilotMapsPanel::displayMapButtons(bool visible) { - setUpdatesEnabled(false); - - downloadETA->setVisible(!visible && downloadActive); - downloadMapsButton->setVisible(!visible); - downloadStatus->setVisible(!visible && downloadActive); - downloadTimeElapsed->setVisible(!visible && downloadActive); - lastMapsDownload->setVisible(!visible && !downloadActive); - mapsSize->setVisible(!visible); - preferredSchedule->setVisible(!visible); - removeMapsButton->setVisible(!visible && QDir(mapsFolderPath).exists() && !downloadActive); - selectMapsButton->setVisible(!visible); - - africaMaps->setVisible(visible && countriesOpen); - antarcticaMaps->setVisible(visible && countriesOpen); - asiaMaps->setVisible(visible && countriesOpen); - europeMaps->setVisible(visible && countriesOpen); - northAmericaMaps->setVisible(visible && countriesOpen); - oceaniaMaps->setVisible(visible && countriesOpen); - southAmericaMaps->setVisible(visible && countriesOpen); - - africaLabel->setVisible(visible && countriesOpen); - antarcticaLabel->setVisible(visible && countriesOpen); - asiaLabel->setVisible(visible && countriesOpen); - europeLabel->setVisible(visible && countriesOpen); - northAmericaLabel->setVisible(visible && countriesOpen); - oceaniaLabel->setVisible(visible && countriesOpen); - southAmericaLabel->setVisible(visible && countriesOpen); - - midwestMaps->setVisible(visible && !countriesOpen); - northeastMaps->setVisible(visible && !countriesOpen); - southMaps->setVisible(visible && !countriesOpen); - territoriesMaps->setVisible(visible && !countriesOpen); - westMaps->setVisible(visible && !countriesOpen); - - midwestLabel->setVisible(visible && !countriesOpen); - northeastLabel->setVisible(visible && !countriesOpen); - southLabel->setVisible(visible && !countriesOpen); - territoriesLabel->setVisible(visible && !countriesOpen); - westLabel->setVisible(visible && !countriesOpen); - - setUpdatesEnabled(true); - - update(); + previousDownloadedFiles = downloadedFiles; + } } diff --git a/selfdrive/frogpilot/navigation/ui/maps_settings.h b/selfdrive/frogpilot/navigation/ui/maps_settings.h index 2e2bc201694ade..e492851cf0d8d7 100644 --- a/selfdrive/frogpilot/navigation/ui/maps_settings.h +++ b/selfdrive/frogpilot/navigation/ui/maps_settings.h @@ -12,71 +12,37 @@ class FrogPilotMapsPanel : public FrogPilotListWidget { signals: void openMapSelection(); +protected: + void showEvent(QShowEvent *event) override; + private: void cancelDownload(); - void displayMapButtons(bool visible = true); - void downloadMaps(); - void finalizeDownload(); - void handleDownloadError(); - void hideEvent(QHideEvent *event) override; - void resetDownloadLabels(); - void resetDownloadState(); - void showEvent(QShowEvent *event); - void updateDownloadLabels(int downloadedFiles, int totalFiles, qint64 remainingTime, qint64 elapsedMilliseconds); - void updateDownloadStatusLabels(); + void startDownload(); + void updateDownloadLabels(std::string &osmDownloadProgress); void updateState(const UIState &s); + bool cancellingDownload; + bool hasMapsSelected; + ButtonControl *downloadMapsButton; ButtonControl *removeMapsButton; - ButtonParamControl *preferredSchedule; - - FrogPilotButtonsControl *selectMapsButton; - FrogPilotSettingsWindow *parent; - LabelControl *africaLabel; - LabelControl *antarcticaLabel; - LabelControl *asiaLabel; LabelControl *downloadETA; LabelControl *downloadStatus; LabelControl *downloadTimeElapsed; - LabelControl *europeLabel; LabelControl *lastMapsDownload; LabelControl *mapsSize; - LabelControl *midwestLabel; - LabelControl *northAmericaLabel; - LabelControl *northeastLabel; - LabelControl *oceaniaLabel; - LabelControl *southAmericaLabel; - LabelControl *southLabel; - LabelControl *territoriesLabel; - LabelControl *westLabel; - - MapSelectionControl *africaMaps; - MapSelectionControl *antarcticaMaps; - MapSelectionControl *asiaMaps; - MapSelectionControl *europeMaps; - MapSelectionControl *midwestMaps; - MapSelectionControl *northAmericaMaps; - MapSelectionControl *northeastMaps; - MapSelectionControl *oceaniaMaps; - MapSelectionControl *southAmericaMaps; - MapSelectionControl *southMaps; - MapSelectionControl *territoriesMaps; - MapSelectionControl *westMaps; Params params; Params params_memory{"/dev/shm/params"}; - QString mapsFolderPath = "/data/media/0/osm/offline"; + QDateTime startTime; - bool countriesOpen; - bool downloadActive; - bool hasMapsSelected; - bool mapdExists; + QElapsedTimer elapsedTime; - qint64 startTime; + QString mapsFolderPath; - std::string mapsSelected; + QStackedLayout *mapsLayout; }; diff --git a/selfdrive/frogpilot/navigation/ui/navigation_functions.cc b/selfdrive/frogpilot/navigation/ui/navigation_functions.cc index b3ee544d5d69a4..b89c1dfab0db39 100644 --- a/selfdrive/frogpilot/navigation/ui/navigation_functions.cc +++ b/selfdrive/frogpilot/navigation/ui/navigation_functions.cc @@ -3,27 +3,27 @@ #include "selfdrive/frogpilot/navigation/ui/navigation_functions.h" -MapSelectionControl::MapSelectionControl(const QMap &map, bool isCountry) - : buttonGroup(new QButtonGroup(this)), gridLayout(new QGridLayout(this)), mapData(map), isCountry(isCountry), selectionType(isCountry ? "nations" : "states") { - buttonGroup->setExclusive(false); +MapSelectionControl::MapSelectionControl(const QMap &map, bool isCountry) : mapData(map), isCountry(isCountry), selectionType(isCountry ? "nations" : "states") { + mapButtons = new QButtonGroup(this); + mapButtons->setExclusive(false); + + mapLayout = new QGridLayout(this); QList keys = mapData.keys(); for (int i = 0; i < keys.size(); ++i) { QPushButton *button = new QPushButton(mapData[keys[i]], this); - button->setCheckable(true); button->setStyleSheet(buttonStyle); - button->setMinimumWidth(225); - gridLayout->addWidget(button, i / 3, i % 3); - buttonGroup->addButton(button, i); + mapButtons->addButton(button, i); - connect(button, &QPushButton::toggled, this, &MapSelectionControl::updateSelectedMaps); - } + mapLayout->addWidget(button, i / 3, i % 3); - buttons = buttonGroup->buttons(); + QObject::connect(button, &QPushButton::toggled, this, &MapSelectionControl::updateSelectedMaps); + } - mapSelections = QJsonDocument::fromJson(QByteArray::fromStdString(params.get("MapsSelected"))).object()[selectionType].toArray(); + maps = mapButtons->buttons(); + mapSelections = QJsonDocument::fromJson(QByteArray::fromStdString(params.get("MapsSelected"))).object().value(selectionType).toArray(); loadSelectedMaps(); } @@ -31,8 +31,8 @@ MapSelectionControl::MapSelectionControl(const QMap &map, bool void MapSelectionControl::loadSelectedMaps() { for (int i = 0; i < mapSelections.size(); ++i) { QString selectedKey = mapSelections[i].toString(); - for (int j = 0; j < buttons.size(); ++j) { - QAbstractButton *button = buttons[j]; + for (int j = 0; j < maps.size(); ++j) { + QAbstractButton *button = maps[j]; if (button->text() == mapData.value(selectedKey)) { button->setChecked(true); break; @@ -42,7 +42,7 @@ void MapSelectionControl::loadSelectedMaps() { } void MapSelectionControl::updateSelectedMaps() { - for (QAbstractButton *button : buttons) { + for (QAbstractButton *button : maps) { QString key = mapData.key(button->text()); if (button->isChecked() && !mapSelections.contains(key)) { mapSelections.append(key); diff --git a/selfdrive/frogpilot/navigation/ui/navigation_functions.h b/selfdrive/frogpilot/navigation/ui/navigation_functions.h index ce9cf1a781c04b..855d88b98c0b78 100644 --- a/selfdrive/frogpilot/navigation/ui/navigation_functions.h +++ b/selfdrive/frogpilot/navigation/ui/navigation_functions.h @@ -49,7 +49,7 @@ inline QMap africaMap = { {"KM", "Comoros"}, {"CG", "Congo (Brazzaville)"}, {"CD", "Congo (Kinshasa)"}, {"DJ", "Djibouti"}, {"EG", "Egypt"}, {"GQ", "Equatorial Guinea"}, {"ER", "Eritrea"}, {"ET", "Ethiopia"}, {"GA", "Gabon"}, - {"GM", "Gambia"}, {"GH", "Ghana"}, {"GN", "Guinea"}, + {"GM", "Gambia"}, {"GH", "Ghana"}, {"GN", "Guinea"}, {"GW", "Guinea-Bissau"}, {"CI", "Ivory Coast"}, {"KE", "Kenya"}, {"LS", "Lesotho"}, {"LR", "Liberia"}, {"LY", "Libya"}, {"MG", "Madagascar"}, {"MW", "Malawi"}, {"ML", "Mali"}, @@ -126,23 +126,18 @@ inline QMap southAmericaMap = { {"VE", "Venezuela"} }; -inline bool isMapdRunning() { - return std::system("pgrep mapd > /dev/null 2>&1") == 0; -} - -namespace fs = std::filesystem; - inline QString calculateDirectorySize(const QString &directoryPath) { - constexpr uintmax_t oneMB = 1024 * 1024; - constexpr uintmax_t oneGB = 1024 * 1024 * 1024; + namespace fs = std::filesystem; - uintmax_t totalSize = 0; - fs::path path(directoryPath.toStdString()); + constexpr double oneMB = 1024 * 1024; + constexpr double oneGB = 1024 * 1024 * 1024; + fs::path path(directoryPath.toStdString()); if (!fs::exists(path) || !fs::is_directory(path)) { return "0 MB"; } + double totalSize = 0; for (fs::recursive_directory_iterator iter(path, fs::directory_options::skip_permission_denied), end; iter != end; ++iter) { const fs::directory_entry &entry = *iter; if (entry.is_regular_file()) { @@ -151,35 +146,28 @@ inline QString calculateDirectorySize(const QString &directoryPath) { } if (totalSize >= oneGB) { - return QString::number(static_cast(totalSize) / oneGB, 'f', 2) + " GB"; - } else { - return QString::number(static_cast(totalSize) / oneMB, 'f', 2) + " MB"; + return QString::number(totalSize / oneGB, 'f', 2) + " GB"; } + return QString::number(totalSize / oneMB, 'f', 2) + " MB"; +} + +inline QString daySuffix(int day) { + if (day % 10 == 1 && day != 11) return "st"; + if (day % 10 == 2 && day != 12) return "nd"; + if (day % 10 == 3 && day != 13) return "rd"; + return "th"; } inline QString formatCurrentDate() { QDate currentDate = QDate::currentDate(); - int day = currentDate.day(); - - QString suffix; - if (day % 10 == 1 && day != 11) { - suffix = "st"; - } else if (day % 10 == 2 && day != 12) { - suffix = "nd"; - } else if (day % 10 == 3 && day != 13) { - suffix = "rd"; - } else { - suffix = "th"; - } - - return currentDate.toString("MMMM d'") + suffix + QString(", %1").arg(currentDate.year()); + return currentDate.toString("MMMM d'") + daySuffix(currentDate.day()) + QString(", %1").arg(currentDate.year()); } -inline QString formatElapsedTime(qint64 elapsedMilliseconds) { - qint64 totalSeconds = elapsedMilliseconds / 1000; - qint64 hours = totalSeconds / 3600; - qint64 minutes = (totalSeconds % 3600) / 60; - qint64 seconds = totalSeconds % 60; +inline QString formatElapsedTime(float elapsedMilliseconds) { + int totalSeconds = elapsedMilliseconds / 1000; + int hours = totalSeconds / 3600; + int minutes = (totalSeconds % 3600) / 60; + int seconds = totalSeconds % 60; QString formattedTime; if (hours > 0) { @@ -190,7 +178,27 @@ inline QString formatElapsedTime(qint64 elapsedMilliseconds) { } formattedTime += QString::number(seconds) + (seconds == 1 ? " second" : " seconds"); - return formattedTime; + return formattedTime.trimmed(); +} + +inline QString formatETA(float elapsedTime, int downloadedFiles, int previousDownloadedFiles, int totalFiles, QDateTime &startTime) { + static QDateTime estimatedFinishTime; + + static float previousElapsedTime; + + if (downloadedFiles != previousDownloadedFiles) { + estimatedFinishTime = startTime.addMSecs((elapsedTime * totalFiles) / downloadedFiles); + } else { + estimatedFinishTime = estimatedFinishTime.addSecs((previousElapsedTime - elapsedTime) / 1000); + } + previousElapsedTime = elapsedTime; + + int remainingTime = QDateTime::currentDateTime().secsTo(estimatedFinishTime); + + QString estimatedFinishTimeStr = estimatedFinishTime.toString("h:mm AP"); + QString remainingTimeStr = formatElapsedTime(remainingTime * 1000); + + return QString("%1 (%2)").arg(remainingTimeStr).arg(estimatedFinishTimeStr); } class MapSelectionControl : public QWidget { @@ -203,19 +211,19 @@ class MapSelectionControl : public QWidget { void loadSelectedMaps(); void updateSelectedMaps(); + bool isCountry; + Params params; - QButtonGroup *buttonGroup; + QButtonGroup *mapButtons; - QGridLayout *gridLayout; + QGridLayout *mapLayout; QJsonArray mapSelections; - QList buttons; + QList maps; QMap mapData; QString selectionType; - - bool isCountry; }; diff --git a/selfdrive/frogpilot/navigation/ui/primeless_settings.cc b/selfdrive/frogpilot/navigation/ui/primeless_settings.cc index 9af980a697d9c3..1dc843c4a68611 100644 --- a/selfdrive/frogpilot/navigation/ui/primeless_settings.cc +++ b/selfdrive/frogpilot/navigation/ui/primeless_settings.cc @@ -1,114 +1,201 @@ #include "selfdrive/frogpilot/navigation/ui/primeless_settings.h" -FrogPilotPrimelessPanel::FrogPilotPrimelessPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { - addItem(ipLabel = new LabelControl(tr("Manage Your Settings At"), tr("Device Offline"))); +void FrogPilotPrimelessPanel::createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix, FrogPilotListWidget *list) { + control = new ButtonControl(label, "", tr("Manage your %1.").arg(label)); + QObject::connect(control, &ButtonControl::clicked, [=] { + if (control->text() == tr("ADD")) { + QString key = InputDialog::getText(tr("Enter your %1").arg(label), this).trimmed(); - std::vector searchOptions{tr("MapBox"), tr("Amap"), tr("Google")}; - searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"), - tr("The search provider used for destination queries in 'Navigate on Openpilot'. Options include 'MapBox' (recommended), 'Amap', and 'Google Maps'."), - "", searchOptions); - addItem(searchInput); + if (!key.startsWith(prefix)) { + key = prefix + key; + } + if (key.length() >= 80) { + params.put(paramKey, key.toStdString()); + } else { + FrogPilotConfirmationDialog::toggleAlert(tr("Inputted key is invalid or too short!"), tr("Okay"), this); + } + } else { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your %1?").arg(label), this)) { + control->setText(tr("ADD")); - createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk."); - createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk."); + params.put(paramKey, "0"); + paramsStorage.put(paramKey, "0"); - setupButton = new ButtonControl(tr("MapBox Setup Instructions"), tr("VIEW"), tr("View the instructions to set up 'MapBox' for 'Primeless Navigation'."), this); - QObject::connect(setupButton, &ButtonControl::clicked, [this]() { - displayMapboxInstructions(true); - openMapBoxInstructions(); - updateStep(); + setupCompleted = false; + } + } }); - addItem(setupButton); + control->setText(QString::fromStdString(params.get(paramKey)).startsWith(prefix) ? tr("REMOVE") : tr("ADD")); + list->addItem(control); +} - imageLabel = new QLabel(this); - addItem(imageLabel); +FrogPilotPrimelessPanel::FrogPilotPrimelessPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { + QVBoxLayout *mainLayout = new QVBoxLayout(); + addItem(mainLayout); - QObject::connect(parent, &FrogPilotSettingsWindow::closeMapBoxInstructions, [this]() {displayMapboxInstructions(false);}); - QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotPrimelessPanel::updateState); + primelessLayout = new QStackedLayout(); + mainLayout->addLayout(primelessLayout); - displayMapboxInstructions(false); -} + FrogPilotListWidget *settingsList = new FrogPilotListWidget(this); + ipLabel = new LabelControl(tr("Manage Your Settings At"), tr("Device Offline")); + settingsList->addItem(ipLabel); -void FrogPilotPrimelessPanel::showEvent(QShowEvent *event) { - QString ipAddress = uiState()->wifi->getIp4Address(); - ipLabel->setText(ipAddress.isEmpty() ? tr("Device Offline") : QString("%1:8082").arg(ipAddress)); + std::vector searchOptions{tr("MapBox"), tr("Amap"), tr("Google")}; + ButtonParamControl *searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"), + tr("The search provider used for destination queries in 'Navigate on Openpilot'. " + "Options include 'MapBox' (recommended), 'Amap', and 'Google Maps'."), + "", searchOptions); + QObject::connect(searchInput, &ButtonParamControl::buttonClicked, [this](int id) { + amapKeyControl1->setVisible(id == 1); + amapKeyControl2->setVisible(id == 1); - mapboxPublicKeySet = !params.get("MapboxPublicKey").empty(); - mapboxSecretKeySet = !params.get("MapboxSecretKey").empty(); - setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet; + googleKeyControl->setVisible(id == 2); - publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD")); - secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD")); -} + publicMapboxKeyControl->setVisible(id == 0); + secretMapboxKeyControl->setVisible(id == 0); -void FrogPilotPrimelessPanel::updateState() { - if (!isVisible()) { - return; - } + update(); + }); + settingsList->addItem(searchInput); - if (imageLabel->isVisible()) { - mapboxPublicKeySet = !params.get("MapboxPublicKey").empty(); - mapboxSecretKeySet = !params.get("MapboxSecretKey").empty(); + amapKeyControl1 = new ButtonControl(tr("Amap Key #1"), "", tr("Manage your Amap key.")); + QObject::connect(amapKeyControl1, &ButtonControl::clicked, [=] { + if (amapKeyControl1->text() == tr("ADD")) { + QString key = InputDialog::getText(tr("Enter your Amap key"), this).trimmed(); - updateStep(); - } + if (key.length() >= 39) { + params.put("AMapKey1", key.toStdString()); + } else { + FrogPilotConfirmationDialog::toggleAlert(tr("Inputted key is invalid or too short!"), tr("Okay"), this); + } + } else { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your Amap key?"), this)) { + amapKeyControl1->setText(tr("ADD")); - parent->keepScreenOn = imageLabel->isVisible(); -} + params.put("AMapKey1", "0"); + paramsStorage.put("AMapKey1", "0"); + } + } + }); + amapKeyControl1->setText(QString::fromStdString(params.get("AMapKey1")) != "0" ? tr("REMOVE") : tr("ADD")); + settingsList->addItem(amapKeyControl1); -void FrogPilotPrimelessPanel::createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix) { - control = new ButtonControl(label, "", tr("Manage your %1.").arg(label)); + amapKeyControl2 = new ButtonControl(tr("Amap Key #2"), "", tr("Manage your Amap key.")); + QObject::connect(amapKeyControl2, &ButtonControl::clicked, [=] { + if (amapKeyControl2->text() == tr("ADD")) { + QString key = InputDialog::getText(tr("Enter your Amap key"), this).trimmed(); - QObject::connect(control, &ButtonControl::clicked, [=] { - if (control->text() == tr("ADD")) { - QString key = InputDialog::getText(tr("Enter your %1").arg(label), this).trimmed(); + if (key.length() >= 39) { + params.put("AMapKey2", key.toStdString()); + } else { + FrogPilotConfirmationDialog::toggleAlert(tr("Inputted key is invalid or too short!"), tr("Okay"), this); + } + } else { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your Amap key?"), this)) { + amapKeyControl2->setText(tr("ADD")); - if (!key.startsWith(prefix)) { - key = prefix + key; + params.put("AMapKey2", "0"); + paramsStorage.put("AMapKey2", "0"); } - if (key.length() >= 80) { - params.put(paramKey, key.toStdString()); + } + }); + amapKeyControl2->setText(QString::fromStdString(params.get("AMapKey2")) != "0" ? tr("REMOVE") : tr("ADD")); + settingsList->addItem(amapKeyControl2); + + googleKeyControl = new ButtonControl(tr("Google Maps Key"), "", tr("Manage your Google Maps key.")); + QObject::connect(googleKeyControl, &ButtonControl::clicked, [=] { + if (googleKeyControl->text() == tr("ADD")) { + QString key = InputDialog::getText(tr("Enter your Google Maps key"), this).trimmed(); + + if (key.length() >= 25) { + params.put("GMapKey", key.toStdString()); } else { FrogPilotConfirmationDialog::toggleAlert(tr("Inputted key is invalid or too short!"), tr("Okay"), this); } } else { - if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your %1?").arg(label), this)) { - control->setText(tr("ADD")); - - params.remove(paramKey); - paramsStorage.remove(paramKey); + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your Google Maps key?"), this)) { + googleKeyControl->setText(tr("ADD")); - setupCompleted = false; + params.put("GMapKey", "0"); + paramsStorage.put("GMapKey", "0"); } } }); + googleKeyControl->setText(QString::fromStdString(params.get("GMapKey")) != "0" ? tr("REMOVE") : tr("ADD")); + settingsList->addItem(googleKeyControl); + + createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk.", settingsList); + createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk.", settingsList); + + ButtonControl *setupButton = new ButtonControl(tr("MapBox Setup Instructions"), tr("VIEW"), tr("View the instructions to set up 'MapBox' for 'Primeless Navigation'."), this); + QObject::connect(setupButton, &ButtonControl::clicked, [this]() { + openMapBoxInstructions(); + updateStep(); + primelessLayout->setCurrentIndex(1); + }); + settingsList->addItem(setupButton); + + ScrollView *settingsPanel = new ScrollView(settingsList, this); + primelessLayout->addWidget(settingsPanel); + + imageLabel = new QLabel(this); + + ScrollView *instructionsPanel = new ScrollView(imageLabel, this); + primelessLayout->addWidget(instructionsPanel); + + QObject::connect(parent, &FrogPilotSettingsWindow::closeMapBoxInstructions, [this] {primelessLayout->setCurrentIndex(0);}); + QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotPrimelessPanel::updateState); +} + +void FrogPilotPrimelessPanel::showEvent(QShowEvent *event) { + QString ipAddress = uiState()->wifi->getIp4Address(); + ipLabel->setText(ipAddress.isEmpty() ? tr("Device Offline") : QString("%1:8082").arg(ipAddress)); + + mapboxPublicKeySet = QString::fromStdString(params.get("MapboxPublicKey")).startsWith("pk"); + mapboxSecretKeySet = QString::fromStdString(params.get("MapboxSecretKey")).startsWith("sk"); + setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet; + + publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD")); + secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD")); + + int searchInput = params.getInt("SearchInput"); + + amapKeyControl1->setVisible(searchInput == 1); + amapKeyControl2->setVisible(searchInput == 1); + + googleKeyControl->setVisible(searchInput == 2); - control->setText(params.get(paramKey).empty() ? tr("ADD") : tr("REMOVE")); - addItem(control); + publicMapboxKeyControl->setVisible(searchInput == 0); + secretMapboxKeyControl->setVisible(searchInput == 0); } void FrogPilotPrimelessPanel::hideEvent(QHideEvent *event) { - displayMapboxInstructions(false); + primelessLayout->setCurrentIndex(0); } void FrogPilotPrimelessPanel::mousePressEvent(QMouseEvent *event) { - closeMapBoxInstructions(); - displayMapboxInstructions(false); + if (primelessLayout->currentIndex() == 1) { + closeMapBoxInstructions(); + primelessLayout->setCurrentIndex(0); + } } -void FrogPilotPrimelessPanel::displayMapboxInstructions(bool visible) { - setUpdatesEnabled(false); +void FrogPilotPrimelessPanel::updateState() { + if (!isVisible()) { + return; + } - imageLabel->setVisible(visible); - ipLabel->setVisible(!visible); - publicMapboxKeyControl->setVisible(!visible); - searchInput->setVisible(!visible); - secretMapboxKeyControl->setVisible(!visible); - setupButton->setVisible(!visible); + if (primelessLayout->currentIndex() == 1) { + mapboxPublicKeySet = QString::fromStdString(params.get("MapboxPublicKey")).startsWith("pk"); + mapboxSecretKeySet = QString::fromStdString(params.get("MapboxSecretKey")).startsWith("sk"); - setUpdatesEnabled(true); + publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD")); + secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD")); - update(); + updateStep(); + } + + parent->keepScreenOn = primelessLayout->currentIndex() == 1; } void FrogPilotPrimelessPanel::updateStep() { diff --git a/selfdrive/frogpilot/navigation/ui/primeless_settings.h b/selfdrive/frogpilot/navigation/ui/primeless_settings.h index 935ab2ad968a02..9d49064740b78c 100644 --- a/selfdrive/frogpilot/navigation/ui/primeless_settings.h +++ b/selfdrive/frogpilot/navigation/ui/primeless_settings.h @@ -12,20 +12,25 @@ class FrogPilotPrimelessPanel : public FrogPilotListWidget { void closeMapBoxInstructions(); void openMapBoxInstructions(); -private: - void createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix); - void displayMapboxInstructions(bool visible); +protected: void hideEvent(QHideEvent *event); + void showEvent(QShowEvent *event) override; + +private: + void createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix, FrogPilotListWidget *list); void mousePressEvent(QMouseEvent *event); - void showEvent(QShowEvent *event); void updateState(); void updateStep(); + bool mapboxPublicKeySet; + bool mapboxSecretKeySet; + bool setupCompleted; + + ButtonControl *amapKeyControl1; + ButtonControl *amapKeyControl2; + ButtonControl *googleKeyControl; ButtonControl *publicMapboxKeyControl; ButtonControl *secretMapboxKeyControl; - ButtonControl *setupButton; - - ButtonParamControl *searchInput; FrogPilotSettingsWindow *parent; @@ -34,9 +39,7 @@ class FrogPilotPrimelessPanel : public FrogPilotListWidget { Params params; Params paramsStorage{"/persist/params"}; - bool mapboxPublicKeySet; - bool mapboxSecretKeySet; - bool setupCompleted; - QLabel *imageLabel; + + QStackedLayout *primelessLayout; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc index 2b6532fd8bca46..9042c1388ea305 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc @@ -12,18 +12,14 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; deleteDrivingDataBtn->setEnabled(false); - deleteDrivingDataBtn->setValue(tr("Deleting...")); realdataDir.removeRecursively(); realdataDir.mkpath("."); deleteDrivingDataBtn->setValue(tr("Deleted!")); - util::sleep_for(2500); - deleteDrivingDataBtn->setEnabled(true); - deleteDrivingDataBtn->setValue(""); parent->keepScreenOn = false; @@ -45,7 +41,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; screenRecordingsBtn->setEnabled(false); - screenRecordingsBtn->setValue(tr("Deleting...")); screenRecordingsBtn->setVisibleButton(1, false); @@ -54,11 +49,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi QFile::remove(recordingsDir.absoluteFilePath(selection)); screenRecordingsBtn->setValue(tr("Deleted!")); - util::sleep_for(2500); - screenRecordingsBtn->setEnabled(true); - screenRecordingsBtn->setValue(""); screenRecordingsBtn->setVisibleButton(1, true); @@ -75,7 +67,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; screenRecordingsBtn->setEnabled(false); - screenRecordingsBtn->setValue(tr("Deleting...")); screenRecordingsBtn->setVisibleButton(0, false); @@ -85,11 +76,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi recordingsDir.mkpath("."); screenRecordingsBtn->setValue(tr("Deleted!")); - util::sleep_for(2500); - screenRecordingsBtn->setEnabled(true); - screenRecordingsBtn->setValue(""); screenRecordingsBtn->setVisibleButton(0, true); @@ -112,7 +100,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; screenRecordingsBtn->setEnabled(false); - screenRecordingsBtn->setValue(tr("Renaming...")); screenRecordingsBtn->setVisibleButton(0, false); @@ -120,15 +107,11 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi QString newPath = recordingsDir.absoluteFilePath(newName); QString oldPath = recordingsDir.absoluteFilePath(selection); - QFile::rename(oldPath, newPath); screenRecordingsBtn->setValue(tr("Renamed!")); - util::sleep_for(2500); - screenRecordingsBtn->setEnabled(true); - screenRecordingsBtn->setValue(""); screenRecordingsBtn->setVisibleButton(0, true); @@ -159,7 +142,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; frogpilotBackupBtn->setEnabled(false); - frogpilotBackupBtn->setValue(tr("Backing up...")); frogpilotBackupBtn->setVisibleButton(1, false); @@ -183,11 +165,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi } frogpilotBackupBtn->setValue(tr("Backup created!")); - util::sleep_for(2500); - frogpilotBackupBtn->setEnabled(true); - frogpilotBackupBtn->setValue(""); frogpilotBackupBtn->setVisibleButton(1, true); @@ -205,12 +184,13 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi std::thread([=]() { parent->keepScreenOn = true; + frogpilotBackupBtn->setEnabled(false); + frogpilotBackupBtn->setValue(tr("Deleting...")); + frogpilotBackupBtn->setVisibleButton(0, false); frogpilotBackupBtn->setVisibleButton(2, false); frogpilotBackupBtn->setVisibleButton(3, false); - frogpilotBackupBtn->setValue(tr("Deleting...")); - QDir dirToDelete(backupDir.filePath(selection)); if (selection.endsWith(".tar.gz")) { QFile::remove(dirToDelete.absolutePath()); @@ -218,11 +198,10 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi dirToDelete.removeRecursively(); } + frogpilotBackupBtn->setValue(tr("Deleted!")); util::sleep_for(2500); - - frogpilotBackupBtn->setValue(""); - frogpilotBackupBtn->setEnabled(true); + frogpilotBackupBtn->setValue(""); frogpilotBackupBtn->setVisibleButton(0, true); frogpilotBackupBtn->setVisibleButton(2, true); @@ -239,23 +218,19 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; frogpilotBackupBtn->setEnabled(false); + frogpilotBackupBtn->setValue(tr("Deleting...")); frogpilotBackupBtn->setVisibleButton(0, false); frogpilotBackupBtn->setVisibleButton(1, false); frogpilotBackupBtn->setVisibleButton(3, false); - frogpilotBackupBtn->setValue(tr("Deleting...")); - backupDir.removeRecursively(); backupDir.mkpath("."); frogpilotBackupBtn->setValue(tr("Deleted!")); - util::sleep_for(2500); - - frogpilotBackupBtn->setValue(""); - frogpilotBackupBtn->setEnabled(true); + frogpilotBackupBtn->setValue(""); frogpilotBackupBtn->setVisibleButton(0, true); frogpilotBackupBtn->setVisibleButton(1, true); @@ -273,6 +248,7 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; frogpilotBackupBtn->setEnabled(false); + frogpilotBackupBtn->setValue(tr("Restoring...")); frogpilotBackupBtn->setVisibleButton(0, false); frogpilotBackupBtn->setVisibleButton(1, false); @@ -291,8 +267,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi sourcePath = extractDirectory; } - frogpilotBackupBtn->setValue(tr("Restoring...")); - std::filesystem::create_directories(targetPath); std::system(("rsync -av --delete -l " + sourcePath + "/ " + targetPath + "/").c_str()); @@ -306,11 +280,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi params.putBool("AutomaticUpdates", false); frogpilotBackupBtn->setValue(tr("Restored!")); - util::sleep_for(2500); - frogpilotBackupBtn->setValue(tr("Rebooting...")); - util::sleep_for(2500); Hardware::reboot(); @@ -337,7 +308,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; toggleBackupBtn->setEnabled(false); - toggleBackupBtn->setValue(tr("Backing up...")); toggleBackupBtn->setVisibleButton(1, false); @@ -354,11 +324,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi std::filesystem::rename(inProgressBackupPath, fullBackupPath); toggleBackupBtn->setValue(tr("Backup created!")); - util::sleep_for(2500); - toggleBackupBtn->setEnabled(true); - toggleBackupBtn->setValue(""); toggleBackupBtn->setVisibleButton(1, true); @@ -376,20 +343,20 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi std::thread([=]() { parent->keepScreenOn = true; + toggleBackupBtn->setEnabled(false); + toggleBackupBtn->setValue(tr("Deleting...")); + toggleBackupBtn->setVisibleButton(0, false); toggleBackupBtn->setVisibleButton(2, false); toggleBackupBtn->setVisibleButton(3, false); - toggleBackupBtn->setValue(tr("Deleting...")); - QDir dirToDelete(backupDir.filePath(selection)); dirToDelete.removeRecursively(); + toggleBackupBtn->setValue(tr("Deleted!")); util::sleep_for(2500); - - toggleBackupBtn->setValue(""); - toggleBackupBtn->setEnabled(true); + toggleBackupBtn->setValue(""); toggleBackupBtn->setVisibleButton(0, true); toggleBackupBtn->setVisibleButton(2, true); @@ -406,23 +373,19 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; toggleBackupBtn->setEnabled(false); + toggleBackupBtn->setValue(tr("Deleting...")); toggleBackupBtn->setVisibleButton(0, false); toggleBackupBtn->setVisibleButton(1, false); toggleBackupBtn->setVisibleButton(3, false); - toggleBackupBtn->setValue(tr("Deleting...")); - backupDir.removeRecursively(); backupDir.mkpath("."); toggleBackupBtn->setValue(tr("Deleted!")); - util::sleep_for(2500); - - toggleBackupBtn->setValue(""); - toggleBackupBtn->setEnabled(true); + toggleBackupBtn->setValue(""); toggleBackupBtn->setVisibleButton(0, true); toggleBackupBtn->setVisibleButton(1, true); @@ -440,13 +403,12 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi parent->keepScreenOn = true; toggleBackupBtn->setEnabled(false); + toggleBackupBtn->setValue(tr("Restoring...")); toggleBackupBtn->setVisibleButton(0, false); toggleBackupBtn->setVisibleButton(1, false); toggleBackupBtn->setVisibleButton(2, false); - toggleBackupBtn->setValue(tr("Restoring...")); - std::string sourcePath = backupDir.filePath(selection).toStdString(); std::string targetPath = "/data/params/d"; @@ -457,12 +419,9 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi updateFrogPilotToggles(); toggleBackupBtn->setValue(tr("Restored!")); - util::sleep_for(2500); - - toggleBackupBtn->setValue(""); - toggleBackupBtn->setEnabled(true); + toggleBackupBtn->setValue(""); toggleBackupBtn->setVisibleButton(0, true); toggleBackupBtn->setVisibleButton(1, true); diff --git a/selfdrive/frogpilot/ui/qt/offroad/device_settings.h b/selfdrive/frogpilot/ui/qt/offroad/device_settings.h index a81f984fa41606..897c428d5ea8ed 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/device_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/device_settings.h @@ -19,10 +19,6 @@ class FrogPilotDevicePanel : public FrogPilotListWidget { void showToggles(const std::set &keys); void updateState(const UIState &s); - FrogPilotSettingsWindow *parent; - - QJsonObject frogpilotToggleLevels; - bool started; int tuningLevel; @@ -31,4 +27,8 @@ class FrogPilotDevicePanel : public FrogPilotListWidget { std::set deviceManagementKeys = {"DeviceShutdown", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads", "OfflineMode"}; std::set screenKeys = {"ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", "ScreenTimeout", "ScreenTimeoutOnroad"}; + + FrogPilotSettingsWindow *parent; + + QJsonObject frogpilotToggleLevels; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc index 1971c6cca0c31c..51e4d3b1726366 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc @@ -1,5 +1,3 @@ -#include "selfdrive/ui/qt/widgets/scrollview.h" - #include "selfdrive/frogpilot/navigation/ui/maps_settings.h" #include "selfdrive/frogpilot/navigation/ui/primeless_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/data_settings.h" @@ -67,12 +65,12 @@ void FrogPilotSettingsWindow::createPanelButtons(FrogPilotListWidget *list) { }; std::vector> panelInfo = { - {tr("Alerts and Sounds"), tr("Manage FrogPilot's alerts and notifications."), "../frogpilot/assets/toggle_icons/icon_sound.png"}, - {tr("Driving Controls"), tr("Adjust features that affect acceleration, braking, and steering."), "../frogpilot/assets/toggle_icons/icon_steering.png"}, - {tr("Navigation"), tr("Download map data and configure 'Navigate On openpilot (NOO)'."), "../frogpilot/assets/toggle_icons/icon_map.png"}, - {tr("System Management"), tr("Access tools, utilities, and device controls to maintain and troubleshoot FrogPilot."), "../frogpilot/assets/toggle_icons/icon_system.png"}, - {tr("Theme and Appearance"), tr("Customize the openpilot theme, UI appearance, and on-road widgets."), "../frogpilot/assets/toggle_icons/icon_display.png"}, - {tr("Vehicle Controls"), tr("Configure vehicle-specific settings for supported makes and models."), "../frogpilot/assets/toggle_icons/icon_vehicle.png"} + {tr("Alerts and Sounds"), tr("Manage FrogPilot's alerts and sounds."), "../frogpilot/assets/toggle_icons/icon_sound.png"}, + {tr("Driving Controls"), tr("Manage FrogPilot's features that affect acceleration, braking, and steering."), "../frogpilot/assets/toggle_icons/icon_steering.png"}, + {tr("Navigation"), tr("Manage map data to be used with 'Curve Speed Control' and 'Speed Limit Controller' and setup 'Navigate On openpilot (NOO)' without a comma prime subscription."), "../frogpilot/assets/toggle_icons/icon_map.png"}, + {tr("System Management"), tr("Manage the device's internal settings along with other tools and utilities to maintain and troubleshoot FrogPilot."), "../frogpilot/assets/toggle_icons/icon_system.png"}, + {tr("Theme and Appearance"), tr("Manage openpilot's theme and onroad widgets."), "../frogpilot/assets/toggle_icons/icon_display.png"}, + {tr("Vehicle Controls"), tr("Manage vehicle-specific settings."), "../frogpilot/assets/toggle_icons/icon_vehicle.png"} }; for (size_t i = 0; i < panelInfo.size(); ++i) { @@ -114,13 +112,17 @@ void FrogPilotSettingsWindow::createPanelButtons(FrogPilotListWidget *list) { FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFrame(parent) { mainLayout = new QStackedLayout(this); - frogpilotWidget = new QWidget(this); + QWidget *frogpilotWidget = new QWidget(this); QVBoxLayout *frogpilotLayout = new QVBoxLayout(frogpilotWidget); frogpilotLayout->setContentsMargins(50, 25, 50, 25); + frogpilotWidget->setLayout(frogpilotLayout); + + frogpilotPanel = new ScrollView(frogpilotWidget, this); + mainLayout->addWidget(frogpilotPanel); + frogpilotPanel->setWidget(frogpilotWidget); FrogPilotListWidget *list = new FrogPilotListWidget(this); frogpilotLayout->addWidget(list); - mainLayout->addWidget(frogpilotWidget); std::vector togglePresets{tr("Minimal"), tr("Standard"), tr("Advanced"), tr("Developer")}; ButtonParamControl *togglePreset = new ButtonParamControl("TuningLevel", tr("Tuning Level"), @@ -132,8 +134,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram "../frogpilot/assets/toggle_icons/icon_customization.png", togglePresets); - int timeTo100FPHours = 100 - (paramsTracking.getInt("FrogPilotMinutes") / 60); - int timeTo250OPHours = 250 - (params.getInt("openpilotMinutes") / 60); + int timeTo100FPHours = 1 - (paramsTracking.getInt("FrogPilotMinutes") / 60); + int timeTo250OPHours = 1 - (params.getInt("openpilotMinutes") / 60); togglePreset->setEnabledButtons(3, timeTo100FPHours <= 0 || timeTo250OPHours <= 0); QObject::connect(togglePreset, &ButtonParamControl::buttonClicked, [this](int id) { @@ -167,10 +169,11 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram QObject::connect(uiState(), &UIState::offroadTransition, this, &FrogPilotSettingsWindow::updateVariables); QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotSettingsWindow::updateState); - frogpilotToggleLevels = QJsonDocument::fromJson(QString::fromStdString(params_memory.get("FrogPilotTuningLevels", true)).toUtf8()).object(); + frogpilotToggleLevels = QJsonDocument::fromJson(params_memory.get("FrogPilotTuningLevels", true).c_str()).object(); tuningLevel = params.getInt("TuningLevel"); closeParentToggle(); + updateMetric(params.getBool("IsMetric"), true); updateVariables(); } @@ -179,7 +182,7 @@ void FrogPilotSettingsWindow::hideEvent(QHideEvent *event) { } void FrogPilotSettingsWindow::closePanel() { - mainLayout->setCurrentWidget(frogpilotWidget); + mainLayout->setCurrentWidget(frogpilotPanel); panelOpen = false; updateFrogPilotToggles(); } @@ -188,7 +191,7 @@ void FrogPilotSettingsWindow::updateState() { UIState *s = uiState(); UIScene &scene = s->scene; - scene.keep_screen_on = panelOpen && keepScreenOn; + scene.frogpilot_panel_active = panelOpen && keepScreenOn; } void FrogPilotSettingsWindow::updateVariables() { diff --git a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h index dbbc76b4b54c6c..3e1c5a1cc4ca41 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h @@ -1,6 +1,7 @@ #pragma once #include "selfdrive/ui/qt/offroad/settings.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" class FrogPilotSettingsWindow : public QFrame { Q_OBJECT @@ -10,8 +11,6 @@ class FrogPilotSettingsWindow : public QFrame { void updateVariables(); - QJsonObject frogpilotToggleLevels; - bool hasAutoTune = true; bool hasBSM = true; bool hasDashSpeedLimits = true; @@ -40,6 +39,8 @@ class FrogPilotSettingsWindow : public QFrame { int tuningLevel; + QJsonObject frogpilotToggleLevels; + signals: void closeMapBoxInstructions(); void closeMapSelection(); @@ -50,7 +51,7 @@ class FrogPilotSettingsWindow : public QFrame { void openPanel(); void openParentToggle(); void openSubParentToggle(); - void updateMetric(); + void updateMetric(bool metric, bool bootRun=false); private: void closePanel(); @@ -58,6 +59,8 @@ class FrogPilotSettingsWindow : public QFrame { void hideEvent(QHideEvent *event) override; void updateState(); + bool panelOpen; + FrogPilotButtonsControl *drivingPanelButtons; FrogPilotButtonsControl *navigationPanelButtons; FrogPilotButtonsControl *systemPanelButtons; @@ -68,7 +71,5 @@ class FrogPilotSettingsWindow : public QFrame { QStackedLayout *mainLayout; - QWidget *frogpilotWidget; - - bool panelOpen; + ScrollView *frogpilotPanel; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc index 47af14dd6b0753..dc1df8fe7b134f 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc @@ -266,8 +266,6 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLateralPanel::hideToggles); QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLateralPanel::updateMetric); QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotLateralPanel::updateState); - - updateMetric(); } void FrogPilotLateralPanel::showEvent(QShowEvent *event) { @@ -300,13 +298,11 @@ void FrogPilotLateralPanel::updateState(const UIState &s) { started = s.scene.started; } -void FrogPilotLateralPanel::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; +void FrogPilotLateralPanel::updateMetric(bool metric, bool bootRun) { + static bool previousMetric; + if (metric != previousMetric && !bootRun) { + double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT; + double speedConversion = metric ? MILE_TO_KM : KM_TO_MILE; params.putFloatNonBlocking("LaneDetectionWidth", params.getFloat("LaneDetectionWidth") * distanceConversion); @@ -315,13 +311,14 @@ void FrogPilotLateralPanel::updateMetric() { params.putFloatNonBlocking("PauseLateralOnSignal", params.getFloat("PauseLateralOnSignal") * speedConversion); params.putFloatNonBlocking("PauseLateralSpeed", params.getFloat("PauseLateralSpeed") * speedConversion); } + previousMetric = metric; FrogPilotParamValueControl *laneWidthToggle = static_cast(toggles["LaneDetectionWidth"]); FrogPilotParamValueControl *minimumLaneChangeSpeedToggle = static_cast(toggles["MinimumLaneChangeSpeed"]); FrogPilotParamValueControl *pauseAOLOnBrakeToggle = static_cast(toggles["PauseAOLOnBrake"]); FrogPilotParamValueControl *pauseLateralToggle = static_cast(toggles["PauseLateralSpeed"]); - if (isMetric) { + if (metric) { minimumLaneChangeSpeedToggle->updateControl(0, 150, tr("kph")); pauseAOLOnBrakeToggle->updateControl(0, 99, tr("kph")); pauseLateralToggle->updateControl(0, 99, tr("kph")); diff --git a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h index 34a4777c0ad9a0..2f14b4729e80ab 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h @@ -17,23 +17,11 @@ class FrogPilotLateralPanel : public FrogPilotListWidget { void hideToggles(); void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); - void updateMetric(); + void updateMetric(bool metric, bool bootRun); void updateState(const UIState &s); - FrogPilotParamValueButtonControl *steerFrictionToggle; - FrogPilotParamValueButtonControl *steerLatAccelToggle; - FrogPilotParamValueButtonControl *steerKPToggle; - FrogPilotParamValueButtonControl *steerRatioToggle; - - FrogPilotSettingsWindow *parent; - - QJsonObject frogpilotToggleLevels; - - Params params; - bool hasAutoTune; bool hasNNFFLog; - bool isMetric = params.getBool("IsMetric"); bool isPIDCar; bool isSubaru; bool liveValid; @@ -53,4 +41,15 @@ class FrogPilotLateralPanel : public FrogPilotListWidget { std::set laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"}; std::set lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"}; std::set qolKeys = {"PauseLateralSpeed"}; + + FrogPilotParamValueButtonControl *steerFrictionToggle; + FrogPilotParamValueButtonControl *steerLatAccelToggle; + FrogPilotParamValueButtonControl *steerKPToggle; + FrogPilotParamValueButtonControl *steerRatioToggle; + + FrogPilotSettingsWindow *parent; + + QJsonObject frogpilotToggleLevels; + + Params params; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc index 87493ec62551aa..4c9be8dad5de2d 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc @@ -67,6 +67,7 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * {"LeadDetectionThreshold", tr("Lead Detection Confidence"), tr("Controls how sensitive openpilot is to detecting vehicles ahead. A lower value can help detect vehicles sooner and from farther away, but increases the chance openpilot mistakes other objects for vehicles."), ""}, {"MaxDesiredAcceleration", tr("Maximum Acceleration Rate"), tr("Sets a cap on how fast openpilot can accelerate."), ""}, {"TacoTune", tr("'Taco Bell Run' Turn Speed Hack"), tr("Uses comma's speed hack they used to help handle left and right turns more precisely during their 2022 'Taco Bell' drive by reducing the maximum allowed speed and acceleration while turning."), ""}, + {"Hattrick", tr("Wanna Go Fast Mode"), tr("Dubbed Hat Trick mode in reference to the EV6 owner who prioritizes speed at all costs!"), ""}, {"QOLLongitudinal", tr("Quality of Life Improvements"), tr("Miscellaneous longitudinal focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, {"CustomCruise", tr("Cruise Increase"), tr("Controls the interval used when increasing the cruise control speed."), ""}, @@ -538,8 +539,6 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLongitudinalPanel::hideToggles); QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotLongitudinalPanel::hideSubToggles); QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLongitudinalPanel::updateMetric); - - updateMetric(); } void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) { @@ -555,13 +554,11 @@ void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) { hideToggles(); } -void FrogPilotLongitudinalPanel::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; +void FrogPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) { + static bool previousMetric; + if (metric != previousMetric && !bootRun) { + double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT; + double speedConversion = metric ? MILE_TO_KM : KM_TO_MILE; params.putFloatNonBlocking("IncreasedStoppedDistance", params.getFloat("IncreasedStoppedDistance") * distanceConversion); @@ -576,6 +573,7 @@ void FrogPilotLongitudinalPanel::updateMetric() { params.putFloatNonBlocking("Offset4", params.getFloat("Offset4") * speedConversion); params.putFloatNonBlocking("SetSpeedOffset", params.getFloat("SetSpeedOffset") * speedConversion); } + previousMetric = metric; FrogPilotDualParamControl *ceSpeedToggle = reinterpret_cast(toggles["CESpeed"]); FrogPilotParamValueButtonControl *ceSignal = static_cast(toggles["CESignalSpeed"]); @@ -588,7 +586,7 @@ void FrogPilotLongitudinalPanel::updateMetric() { FrogPilotParamValueControl *increasedStoppedDistanceToggle = static_cast(toggles["IncreasedStoppedDistance"]); FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast(toggles["SetSpeedOffset"]); - if (isMetric) { + if (metric) { offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 kph)")); offset2Toggle->setTitle(tr("Speed Limit Offset (35-54 kph)")); offset3Toggle->setTitle(tr("Speed Limit Offset (55-64 kph)")); diff --git a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h index 410f966d27b4d3..446499c42ad781 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h @@ -19,23 +19,13 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget { void hideToggles(); void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); - void updateMetric(); - - FrogPilotSettingsWindow *parent; - - FrogPilotButtonsControl *curveDetectionBtn; - - QJsonObject frogpilotToggleLevels; - - Params params; - Params params_default{"/data/params_default"}; + void updateMetric(bool metric, bool bootRun); bool customPersonalityOpen; - bool hasPCMCruise; bool hasDashSpeedLimits; + bool hasPCMCruise; bool isGM; bool isHKGCanFd; - bool isMetric = params.getBool("IsMetric"); bool isSubaru; bool isToyota; bool slcOpen; @@ -49,7 +39,7 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget { std::set curveSpeedKeys = {"CurveDetectionMethod", "CurveSensitivity", "HideCSCUI", "MTSCCurvatureCheck", "TurnAggressiveness"}; std::set customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"}; std::set experimentalModeActivationKeys = {"ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", "ExperimentalModeViaTap"}; - std::set longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TacoTune"}; + std::set longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "Hattrick", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TacoTune"}; std::set qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStandstill", "ForceStops", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset"}; std::set relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"}; std::set speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"}; @@ -58,4 +48,13 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget { std::set speedLimitControllerVisualKeys = {"ShowSLCOffset", "SpeedLimitSources"}; std::set standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"}; std::set trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"}; + + FrogPilotButtonsControl *curveDetectionBtn; + + FrogPilotSettingsWindow *parent; + + QJsonObject frogpilotToggleLevels; + + Params params; + Params params_default{"/data/params_default"}; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc index 60f0a9facbf170..b800557af02f73 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc @@ -148,7 +148,6 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog params_memory.putBool("CancelModelDownload", true); } else { - QStringList downloadableModels = availableModelNames; for (const QString &file : modelDir.entryList(QDir::Files)) { downloadableModels.removeAll(modelFileToNameMap.value(QFileInfo(file).baseName())); } @@ -256,7 +255,7 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) { modelFileToNameMapProcessed.insert(availableModels[i], processModelName(availableModelNames[i])); } - QStringList downloadableModels = availableModelNames; + downloadableModels = availableModelNames; for (const QString &file : modelDir.entryList(QDir::Files)) { downloadableModels.removeAll(modelFileToNameMap.value(QFileInfo(file).baseName())); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/model_settings.h b/selfdrive/frogpilot/ui/qt/offroad/model_settings.h index fadda7317c1cee..ac1478c323beff 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/model_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/model_settings.h @@ -24,6 +24,21 @@ class FrogPilotModelPanel : public FrogPilotListWidget { void updateModelLabels(); void updateState(const UIState &s); + bool allModelsDownloaded; + bool allModelsDownloading; + bool cancellingDownload; + bool finalizingDownload; + bool modelDownloading; + bool modelRandomizerOpen; + bool noModelsDownloaded; + bool started; + + int tuningLevel; + + std::map toggles; + + std::set modelRandomizerKeys = {"ManageBlacklistedModels", "ResetScores", "ReviewScores"}; + ButtonControl *selectModelBtn; FrogPilotButtonsControl *deleteModelBtn; @@ -47,19 +62,5 @@ class FrogPilotModelPanel : public FrogPilotListWidget { QStringList availableModels; QStringList availableModelNames; - - bool allModelsDownloaded; - bool allModelsDownloading; - bool cancellingDownload; - bool finalizingDownload; - bool modelDownloading; - bool modelRandomizerOpen; - bool noModelsDownloaded; - bool started; - - int tuningLevel; - - std::map toggles; - - std::set modelRandomizerKeys = {"ManageBlacklistedModels", "ResetScores", "ReviewScores"}; + QStringList downloadableModels; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h index 5663b9ac3c8c43..1664664bab4375 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h @@ -18,12 +18,6 @@ class FrogPilotSoundsPanel : public FrogPilotListWidget { void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); - FrogPilotSettingsWindow *parent; - - QJsonObject frogpilotToggleLevels; - - Params params; - bool hasBSM; bool hasOpenpilotLongitudinal; @@ -33,4 +27,10 @@ class FrogPilotSoundsPanel : public FrogPilotListWidget { std::set alertVolumeControlKeys = {"DisengageVolume", "EngageVolume", "PromptDistractedVolume", "PromptVolume", "RefuseVolume", "WarningImmediateVolume", "WarningSoftVolume"}; std::set customAlertsKeys = {"GoatScream", "GreenLightAlert", "LeadDepartingAlert", "LoudBlindspotAlert", "SpeedLimitChangedAlert"}; + + FrogPilotSettingsWindow *parent; + + QJsonObject frogpilotToggleLevels; + + Params params; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc index d2413d5897cbdc..5147cd9841839b 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc @@ -115,6 +115,9 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr {"RainbowPath", tr("Rainbow Path"), tr("Swap out the path in the onroad UI for a Mario Kart inspired 'Rainbow Path'."), "../frogpilot/assets/toggle_icons/icon_rainbow.png"}, + {"BrakeSignal", tr("Brake Light Indicator"), tr("Turns the current speed value to red when car is braking"), "../frogpilot/assets/toggle_icons/icon_brake.png"}, + + {"RandomEvents", tr("Random Events"), tr("Enables random cosmetic events that happen during certain driving conditions. These events are purely for fun and don't affect driving controls!"), "../frogpilot/assets/toggle_icons/icon_random.png"}, {"StartupAlert", tr("Startup Alert"), tr("Controls the text of the 'Startup' alert message that appears when you start the drive."), "../frogpilot/assets/toggle_icons/icon_message.png"} diff --git a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h index 467948b4ecb0de..f9a72f87259287 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h @@ -21,6 +21,28 @@ class FrogPilotThemesPanel : public FrogPilotListWidget { void showToggles(const std::set &keys); void updateState(const UIState &s); + bool cancellingDownload; + bool colorDownloading; + bool colorsDownloaded; + bool distanceIconDownloading; + bool distanceIconsDownloaded; + bool finalizingDownload; + bool iconDownloading; + bool iconsDownloaded; + bool signalDownloading; + bool signalsDownloaded; + bool soundDownloading; + bool soundsDownloaded; + bool themeDownloading; + bool wheelDownloading; + bool wheelsDownloaded; + + int tuningLevel; + + std::map toggles; + + std::set customThemeKeys = {"CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"}; + FrogPilotButtonsControl *manageCustomColorsBtn; FrogPilotButtonsControl *manageCustomIconsBtn; FrogPilotButtonsControl *manageCustomSignalsBtn; @@ -46,26 +68,4 @@ class FrogPilotThemesPanel : public FrogPilotListWidget { Params params; Params params_memory{"/dev/shm/params"}; - - bool cancellingDownload; - bool colorDownloading; - bool colorsDownloaded; - bool distanceIconDownloading; - bool distanceIconsDownloaded; - bool finalizingDownload; - bool iconDownloading; - bool iconsDownloaded; - bool signalDownloading; - bool signalsDownloaded; - bool soundDownloading; - bool soundsDownloaded; - bool themeDownloading; - bool wheelDownloading; - bool wheelsDownloaded; - - int tuningLevel; - - std::map toggles; - - std::set customThemeKeys = {"CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"}; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/utilities.cc b/selfdrive/frogpilot/ui/qt/offroad/utilities.cc index 6d3f4e34c3a072..ae11db106a90bf 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/utilities.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/utilities.cc @@ -12,8 +12,10 @@ FrogPilotUtilitiesPanel::FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent flashPandaBtn->setEnabled(false); flashPandaBtn->setValue(tr("Flashing...")); - system("python3 /data/openpilot/panda/board/flash.py"); - system("python3 /data/openpilot/panda/board/recover.py"); + params_memory.putBool("FlashPanda", true); + while (params_memory.getBool("FlashPanda")) { + util::sleep_for(UI_FREQ); + } flashPandaBtn->setValue(tr("Flashed!")); util::sleep_for(2500); @@ -25,7 +27,7 @@ FrogPilotUtilitiesPanel::FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent }); addItem(flashPandaBtn); - forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Forces openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true); + FrogPilotButtonsControl *forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Forces openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true); QObject::connect(forceStartedBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { if (id == 0) { params_memory.putBool("ForceOffroad", true); @@ -33,6 +35,9 @@ FrogPilotUtilitiesPanel::FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent } else if (id == 1) { params_memory.putBool("ForceOffroad", false); params_memory.putBool("ForceOnroad", true); + + util::sleep_for(1000); + params.put("CarParams", params.get("CarParamsPersistent")); } else if (id == 2) { params_memory.putBool("ForceOffroad", false); params_memory.putBool("ForceOnroad", false); diff --git a/selfdrive/frogpilot/ui/qt/offroad/utilities.h b/selfdrive/frogpilot/ui/qt/offroad/utilities.h index 2271fd1b3e7434..cce68517fe74fa 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/utilities.h +++ b/selfdrive/frogpilot/ui/qt/offroad/utilities.h @@ -9,8 +9,6 @@ class FrogPilotUtilitiesPanel : public FrogPilotListWidget { explicit FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent); private: - FrogPilotButtonsControl *forceStartedBtn; - FrogPilotSettingsWindow *parent; Params params; diff --git a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc index 8a58fdd4ab0fac..ceabfef5b2c58a 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc @@ -113,8 +113,8 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("Forces the selected fingerprint and prevents it from ever changing."), ""); addItem(forceFingerprint); - bool disableOpenpilotLongState = params.getBool("DisableOpenpilotLongitudinal"); - disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disables openpilot longitudinal control and uses the car's stock ACC instead."), "", disableOpenpilotLongState); + disableOpenpilotLongitudinal = params.getBool("DisableOpenpilotLongitudinal"); + disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disables openpilot longitudinal control and uses the car's stock ACC instead."), "", disableOpenpilotLongitudinal); QObject::connect(disableOpenpilotLong, &ToggleControl::toggleFlipped, [this, parent](bool state) { if (state) { if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely disable openpilot longitudinal control?"), this)) { @@ -144,13 +144,14 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) {"NewLongAPIGM", tr("Use comma's New Longitudinal API"), tr("Enable comma's new control system that has shown great improvement with acceleration and braking, but has issues on some GM vehicles."), ""}, {"NewLongAPI", tr("Use comma's New Longitudinal API"), tr("Enable comma's new control system that has shown great improvement with acceleration and braking, but has issues on some Hyundai/Kia/Genesis vehicles."), ""}, - + {"HyundaiRadarTracks", tr("Enable Radar Tracks"), tr("Enables the cars radar tracks along with OPLong for Hyundai/Kia/Genesis vehicles, which shows vast improvement in long control."), ""}, + {"HKGtuning", tr("Chubbs' Custom Tuning"), tr("Chubbs' Custom tuning for Hyundai/Kia/Genesis vehicles, which smoothes acceleration and braking to help achieve a 'limo' stop."), ""}, {"CrosstrekTorque", tr("Subaru Crosstrek Torque Increase"), tr("Increase the maximum allowed torque for the 'Subaru Crosstrek'."), ""}, {"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("Automatically lock the doors when in drive and unlock when in park."), ""}, {"ClusterOffset", tr("Cluster Speed Offset"), tr("Set the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""}, {"FrogsGoMoosTweak", tr("FrogsGoMoo's Personal Tweaks"), tr("FrogsGoMoo's personal tweaks to the Toyota/Lexus tune that allows the vehicle to take off and stop a bit smoother."), ""}, - {"LockDoorsTimer", tr("Lock Doors On Ignition Off"), tr("Automatically lock the doors after the car's ignition has been turned off."), ""}, + {"LockDoorsTimer", tr("Lock Doors On Ignition Off"), tr("Automatically lock the doors after the car's ignition has been turned off and no one is detected in either of the front seats."), ""}, {"SNGHack", tr("Stop and Go Hack"), tr("Force stop and go for vehicles without stop and go functionality."), ""}, }; @@ -184,7 +185,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) toggles[param] = vehicleToggle; } - std::set rebootKeys = {"CrosstrekTorque", "ExperimentalGMTune", "NewLongAPI", "NewLongAPIGM"}; + std::set rebootKeys = {"CrosstrekTorque", "ExperimentalGMTune", "FrogsGoMoosTweak", "HyundaiRadarTracks", "HKGtuning", "NewLongAPI", "NewLongAPIGM"}; for (const QString &key : rebootKeys) { QObject::connect(static_cast(toggles[key]), &ToggleControl::toggleFlipped, [this]() { if (started) { @@ -295,7 +296,7 @@ void FrogPilotVehiclesPanel::updateToggles() { setVisible &= !hasSNG; } if (key == "LockDoorsTimer") { - setVisible = allowAutoLockingDoors; + setVisible &= allowAutoLockingDoors; } } diff --git a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h index f2bc415fda8481..fec5005739dcc9 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h @@ -16,29 +16,8 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { void updateState(const UIState &s); void updateToggles(); - ButtonControl *selectMakeButton; - ButtonControl *selectModelButton; - - FrogPilotSettingsWindow *parent; - - QJsonObject frogpilotToggleLevels; - - QMap carModels; - - QString carMake; - QString carModel; - - QStringList models; - - ParamControl *forceFingerprint; - - Params params; - Params params_default{"/data/params_default"}; - - ToggleControl *disableOpenpilotLong; - bool allowAutoLockingDoors; - bool disableOpenpilotLongitudinal = params.getBool("DisableOpenpilotLongitudinal"); + bool disableOpenpilotLongitudinal; bool hasExperimentalOpenpilotLongitudinal; bool hasOpenpilotLongitudinal; bool hasSNG; @@ -56,11 +35,32 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { std::map toggles; std::set gmKeys = {"ExperimentalGMTune", "LongPitch", "NewLongAPIGM", "VoltSNG"}; - std::set hyundaiKeys = {"NewLongAPI"}; + std::set hyundaiKeys = {"NewLongAPI","HyundaiRadarTracks", "HKGtuning"}; std::set imprezaKeys = {"CrosstrekTorque"}; - std::set longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "NewLongAPIGM", "SNGHack", "VoltSNG"}; + std::set longitudinalKeys = {"ExperimentalGMTune", "HKGtuning", "LongPitch", "NewLongAPI", "NewLongAPIGM", "SNGHack", "VoltSNG"}; std::set sngKeys = {"SNGHack"}; std::set subaruKeys = {"CrosstrekTorque"}; std::set toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"}; std::set voltKeys = {"VoltSNG"}; + + ButtonControl *selectMakeButton; + ButtonControl *selectModelButton; + + FrogPilotSettingsWindow *parent; + + QJsonObject frogpilotToggleLevels; + + QMap carModels; + + QString carMake; + QString carModel; + + QStringList models; + + ParamControl *forceFingerprint; + + Params params; + Params params_default{"/data/params_default"}; + + ToggleControl *disableOpenpilotLong; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc index 127fb1d4f721f1..f8a087dfa0ca29 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc @@ -34,7 +34,7 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : {"ModelUI", tr("Model UI"), tr("Customize the model visualizations on the screen."), "../frogpilot/assets/toggle_icons/icon_vtc.png"}, {"DynamicPathWidth", tr("Dynamic Path Width"), tr("Automatically adjusts the width of the driving path display based on the current engagement state:\n\nFully engaged = 100%\nAlways On Lateral Active = 75%\nFully disengaged = 50%"), ""}, {"LaneLinesWidth", tr("Lane Lines Width"), tr("Controls the thickness the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."), ""}, - {"PathEdgeWidth", tr("Path Edges Width"), tr("Controls the width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""}, + {"PathEdgeWidth", tr("Path Edges Width"), tr("Controls the width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""}, {"PathWidth", tr("Path Width"), tr("Controls how wide the driving path appears on your screen.\n\nDefault (6.1 feet / 1.9 meters) matches the width of a 2019 Lexus ES 350."), ""}, {"RoadEdgesWidth", tr("Road Edges Width"), tr("Controls how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard lane line width of 4 inches."), ""}, {"UnlimitedLength", tr("'Unlimited' Road UI"), tr("Extends the display of the path, lane lines, and road edges as far as the model can see."), ""}, @@ -175,7 +175,7 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : modifiedDeveloperWidgetKeys.erase("ShowStoppingPoint"); } - if (!params.getBool("ConditionalExperimentalMode")) { + if (!params.getBool("ConditionalExperimental")) { modifiedDeveloperWidgetKeys.erase("ShowCEMStatus"); } @@ -303,25 +303,24 @@ void FrogPilotVisualsPanel::showEvent(QShowEvent *event) { hideToggles(); } -void FrogPilotVisualsPanel::updateMetric() { - bool previousIsMetric = isMetric; - isMetric = params.getBool("IsMetric"); - - if (isMetric != previousIsMetric) { - double smallDistanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH; - double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT; +void FrogPilotVisualsPanel::updateMetric(bool metric, bool bootRun) { + static bool previousMetric; + if (metric != previousMetric && !bootRun) { + double smallDistanceConversion = metric ? INCH_TO_CM : CM_TO_INCH; + double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT; params.putFloatNonBlocking("LaneLinesWidth", params.getFloat("LaneLinesWidth") * smallDistanceConversion); params.putFloatNonBlocking("RoadEdgesWidth", params.getFloat("RoadEdgesWidth") * smallDistanceConversion); params.putFloatNonBlocking("PathWidth", params.getFloat("PathWidth") * distanceConversion); } + previousMetric = metric; FrogPilotParamValueControl *laneLinesWidthToggle = static_cast(toggles["LaneLinesWidth"]); FrogPilotParamValueControl *pathWidthToggle = static_cast(toggles["PathWidth"]); FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast(toggles["RoadEdgesWidth"]); - if (isMetric) { + if (metric) { laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the Vienna standard of 10 centimeters.")); roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the Vienna standard of 10 centimeters.")); diff --git a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h index 74106171bc2877..3d5d95dfaa5187 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h @@ -19,24 +19,13 @@ class FrogPilotVisualsPanel : public FrogPilotListWidget { void hideToggles(); void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); - void updateMetric(); - - FrogPilotButtonToggleControl *borderMetricsBtn; - FrogPilotButtonToggleControl *lateralMetricsBtn; - FrogPilotButtonToggleControl *longitudinalMetricsBtn; - - FrogPilotSettingsWindow *parent; - - Params params; - - QJsonObject frogpilotToggleLevels; + void updateMetric(bool metric, bool bootRun); bool developerUIOpen; bool hasAutoTune; bool hasBSM; bool hasOpenpilotLongitudinal; bool hasRadar; - bool isMetric = params.getBool("IsMetric"); int tuningLevel; @@ -50,4 +39,14 @@ class FrogPilotVisualsPanel : public FrogPilotListWidget { std::set developerWidgetKeys = {"ShowCEMStatus", "ShowStoppingPoint"}; std::set modelUIKeys = {"DynamicPathWidth", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"}; std::set navigationUIKeys = {"BigMap", "MapStyle", "RoadNameUI", "ShowSpeedLimits", "UseVienna"}; + + FrogPilotButtonToggleControl *borderMetricsBtn; + FrogPilotButtonToggleControl *lateralMetricsBtn; + FrogPilotButtonToggleControl *longitudinalMetricsBtn; + + FrogPilotSettingsWindow *parent; + + Params params; + + QJsonObject frogpilotToggleLevels; }; diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index 93edcf3ea4d69d..d09ea0c2aa8093 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -231,6 +231,9 @@ def main(demo=False): # FrogPilot variables frogpilot_toggles = get_frogpilot_toggles() + if not frogpilot_toggles.liveValid: + estimator = TorqueEstimator(CP, True) + while True: sm.update() if sm.all_checks(): diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py index bf1764d1b3281a..75dfdf5fe9cd4d 100644 --- a/selfdrive/modeld/fill_model_msg.py +++ b/selfdrive/modeld/fill_model_msg.py @@ -69,7 +69,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D net_output_data: dict[str, np.ndarray], v_ego: float, delay: float, publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int, frame_id: int, frame_drop: float, timestamp_eof: int, model_execution_time: float, - valid: bool, clip_curves: bool) -> None: + valid: bool, planner_curves: bool) -> None: frame_age = frame_id - vipc_frame_id if frame_id > vipc_frame_id else 0 frame_drop_perc = frame_drop * 100 extended_msg.valid = valid @@ -85,7 +85,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D driving_model_data.modelExecutionTime = model_execution_time action = driving_model_data.action - action.desiredCurvature = desired_curv if clip_curves else float(net_output_data['desired_curvature'][0,0]) + action.desiredCurvature = desired_curv if planner_curves else float(net_output_data['desired_curvature'][0,0]) modelV2 = extended_msg.modelV2 modelV2.frameId = vipc_frame_id @@ -120,7 +120,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D # lateral planning action = modelV2.action - action.desiredCurvature = desired_curv if clip_curves else float(net_output_data['desired_curvature'][0,0]) + action.desiredCurvature = desired_curv if planner_curves else float(net_output_data['desired_curvature'][0,0]) # times at X_IDXS according to model plan PLAN_T_IDXS = [np.nan] * ModelConstants.IDX_N diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index b7af4dbe3a5343..8c1b1be03985d8 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -25,7 +25,7 @@ from openpilot.selfdrive.modeld.constants import ModelConstants from openpilot.selfdrive.modeld.models.commonmodel_pyx import ModelFrame, CLContext -from openpilot.selfdrive.frogpilot.frogpilot_variables import DEFAULT_MODEL, MODELS_PATH, get_frogpilot_toggles +from openpilot.selfdrive.frogpilot.frogpilot_variables import METADATAS_PATH, MODELS_PATH, get_frogpilot_toggles PROCESS_NAME = "selfdrive.modeld.modeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') @@ -34,9 +34,6 @@ ModelRunner.THNEED: Path(__file__).parent / 'models/supercombo.thneed', ModelRunner.ONNX: Path(__file__).parent / 'models/supercombo.onnx'} -METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl' - - class FrameMeta: frame_id: int = 0 timestamp_sof: int = 0 @@ -54,16 +51,15 @@ class ModelState: prev_desire: np.ndarray # for tracking the rising edge of the pulse model: ModelRunner - def __init__(self, context: CLContext, model: str, model_version: str, use_desired_curvature: bool): + def __init__(self, context: CLContext, model: str, model_version: str): # FrogPilot variables - model_path = MODELS_PATH / f'{model}.thneed' - if model != DEFAULT_MODEL and model_path.exists(): - MODEL_PATHS[ModelRunner.THNEED] = model_path + MODEL_PATHS[ModelRunner.THNEED] = MODELS_PATH / f'{model}.thneed' + + with open(METADATAS_PATH / f'supercombo_metadata_{model_version}.pkl', 'rb') as f: + model_metadata = pickle.load(f) - metadata_path = METADATA_PATH - desired_metadata_path = MODELS_PATH / f'supercombo_metadata_{model_version}.pkl' - if model != DEFAULT_MODEL and desired_metadata_path.exists(): - metadata_path = desired_metadata_path + input_shapes = model_metadata.get('input_shapes') + self.use_desired_curvature = 'lateral_control_params' in input_shapes and 'prev_desired_curv' in input_shapes self.frame = ModelFrame(context) self.wide_frame = ModelFrame(context) @@ -76,16 +72,11 @@ def __init__(self, context: CLContext, model: str, model_version: str, use_desir self.inputs = { 'desire': np.zeros(ModelConstants.DESIRE_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32), 'traffic_convention': np.zeros(ModelConstants.TRAFFIC_CONVENTION_LEN, dtype=np.float32), + **({'lateral_control_params': np.zeros(ModelConstants.LATERAL_CONTROL_PARAMS_LEN, dtype=np.float32)} if self.use_desired_curvature else {}), + **({'prev_desired_curv': np.zeros(ModelConstants.PREV_DESIRED_CURV_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32)} if self.use_desired_curvature else {}), 'features_buffer': np.zeros(ModelConstants.HISTORY_BUFFER_LEN * ModelConstants.FEATURE_LEN, dtype=np.float32), } - if use_desired_curvature: - self.inputs['lateral_control_params'] = np.zeros(ModelConstants.LATERAL_CONTROL_PARAMS_LEN, dtype=np.float32) - self.inputs['prev_desired_curv'] = np.zeros(ModelConstants.PREV_DESIRED_CURV_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32) - - with open(metadata_path, 'rb') as f: - model_metadata = pickle.load(f) - self.output_slices = model_metadata['output_slices'] net_output_size = model_metadata['output_shapes']['outputs'][1] self.output = np.zeros(net_output_size, dtype=np.float32) @@ -104,7 +95,7 @@ def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]: return parsed_model_outputs def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray, - inputs: dict[str, np.ndarray], prepare_only: bool, use_desired_curvature: bool) -> dict[str, np.ndarray] | None: + inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None: # Model decides when action is completed, so desire input is just a pulse triggered on rising edge inputs['desire'][0] = 0 new_desire = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0) @@ -115,7 +106,7 @@ def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_ self.inputs['desire'][:] = self.desire_20Hz.reshape((25,4,-1)).max(axis=1).flatten() self.inputs['traffic_convention'][:] = inputs['traffic_convention'] - if use_desired_curvature: + if self.use_desired_curvature: self.inputs['lateral_control_params'][:] = inputs['lateral_control_params'] self.model.setInputBuffer("input_imgs", self.frame.prepare(buf, transform.flatten(), self.model.getCLBuffer("input_imgs"))) @@ -125,18 +116,18 @@ def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_ return None self.model.execute() - outputs = self.parser.parse_outputs(self.slice_outputs(self.output), use_desired_curvature) + outputs = self.parser.parse_outputs(self.slice_outputs(self.output)) self.full_features_20Hz[:-1] = self.full_features_20Hz[1:] self.full_features_20Hz[-1] = outputs['hidden_state'][0, :] - if use_desired_curvature: + if self.use_desired_curvature: self.prev_desired_curv_20hz[:-1] = self.prev_desired_curv_20hz[1:] self.prev_desired_curv_20hz[-1] = outputs['desired_curvature'][0, :] idxs = np.arange(-4,-100,-4)[::-1] self.inputs['features_buffer'][:] = self.full_features_20Hz[idxs].flatten() - if use_desired_curvature: + if self.use_desired_curvature: # TODO model only uses last value now, once that changes we need to input strided action history buffer self.inputs['prev_desired_curv'][-ModelConstants.PREV_DESIRED_CURV_LEN:] = 0. * self.prev_desired_curv_20hz[-4, :] return outputs @@ -157,13 +148,12 @@ def main(demo=False): # FrogPilot variables frogpilot_toggles = get_frogpilot_toggles() - model = frogpilot_toggles.model + model_name = frogpilot_toggles.model model_version = frogpilot_toggles.model_version - clip_curves = frogpilot_toggles.clipped_curvature_model - use_desired_curvature = frogpilot_toggles.desired_curvature_model + planner_curves = frogpilot_toggles.planner_curvature_model - model = ModelState(cl_context, model, model_version, use_desired_curvature) + model = ModelState(cl_context, model_name, model_version) cloudlog.warning("models loaded, modeld starting") # visionipc clients @@ -260,7 +250,7 @@ def main(demo=False): is_rhd = sm["driverMonitoringState"].isRHD frame_id = sm["roadCameraState"].frameId v_ego = max(sm["carState"].vEgo, 0.) - if use_desired_curvature: + if model.use_desired_curvature: lateral_control_params = np.array([v_ego, steer_delay], dtype=np.float32) if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']: device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32) @@ -292,13 +282,11 @@ def main(demo=False): inputs:dict[str, np.ndarray] = { 'desire': vec_desire, 'traffic_convention': traffic_convention, + **({'lateral_control_params': lateral_control_params} if model.use_desired_curvature else {}), } - if use_desired_curvature: - inputs['lateral_control_params'] = lateral_control_params - mt1 = time.perf_counter() - model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only, use_desired_curvature) + model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only) mt2 = time.perf_counter() model_execution_time = mt2 - mt1 @@ -309,7 +297,7 @@ def main(demo=False): fill_model_msg(drivingdata_send, modelv2_send, model_output, v_ego, steer_delay, publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen, - clip_curves) + planner_curves) desire_state = modelv2_send.modelV2.meta.desireState l_lane_change_prob = desire_state[log.Desire.laneChangeLeft] diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py index 4fce0b328a549c..4367e9db8a2bcd 100644 --- a/selfdrive/modeld/parse_model_outputs.py +++ b/selfdrive/modeld/parse_model_outputs.py @@ -84,7 +84,7 @@ def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=None): outs[name] = pred_mu_final.reshape(final_shape) outs[name + '_stds'] = pred_std_final.reshape(final_shape) - def parse_outputs(self, outs: dict[str, np.ndarray], use_desired_curvature: bool) -> dict[str, np.ndarray]: + def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]: self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION, out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH)) self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH)) @@ -96,7 +96,7 @@ def parse_outputs(self, outs: dict[str, np.ndarray], use_desired_curvature: bool out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH)) if 'lat_planner_solution' in outs: self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH)) - if use_desired_curvature and 'desired_curvature' in outs: + if 'desired_curvature' in outs: self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,)) for k in ['lead_prob', 'lane_lines_prob', 'meta']: self.parse_binary_crossentropy(k, outs) diff --git a/selfdrive/monitoring/dmonitoringd.py b/selfdrive/monitoring/dmonitoringd.py index 80af7b71d079c5..19b09c37e44171 100755 --- a/selfdrive/monitoring/dmonitoringd.py +++ b/selfdrive/monitoring/dmonitoringd.py @@ -17,6 +17,9 @@ def dmonitoringd_thread(): DM = DriverMonitoring(rhd_saved=params.get_bool("IsRhdDetected"), always_on=params.get_bool("AlwaysOnDM")) + # FrogPilot variables + driver_view_enabled = params.get_bool("IsDriverViewEnabled") + # 20Hz <- dmonitoringmodeld while True: sm.update() @@ -27,9 +30,11 @@ def dmonitoringd_thread(): valid = sm.all_checks() if valid: DM.run_step(sm) + elif driver_view_enabled: + DM.face_detected = sm['driverStateV2'].leftDriverData.faceProb > DM.settings._FACE_THRESHOLD or sm['driverStateV2'].rightDriverData.faceProb > DM.settings._FACE_THRESHOLD # publish - dat = DM.get_state_packet(valid=valid) + dat = DM.get_state_packet(valid=valid or driver_view_enabled) pm.send('driverMonitoringState', dat) # load live always-on toggle diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index a5ca809270e93b..211ea6d4d8d9c1 100644 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -256,13 +256,11 @@ void OffroadHome::hideEvent(QHideEvent *event) { void OffroadHome::refresh() { QString model = processModelName(uiState()->scene.model_name); - if (uiState()->scene.model_randomizer) { - model = "Mystery Model 👻"; - } - date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d")); version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + model); + date->setVisible(util::system_time_valid()); + bool updateAvailable = update_widget->refresh(); int alerts = alerts_widget->refresh(); diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 1332d87ec72928..4cb748c762ea78 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -118,8 +118,8 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) { }); // FrogPilot signals - connect(toggles["IsMetric"], &ToggleControl::toggleFlipped, [=]() { - updateMetric(); + connect(toggles["IsMetric"], &ToggleControl::toggleFlipped, [=](bool metric) { + updateMetric(metric); }); } diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index a862106c85cc3e..0072ea6a721c7a 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -40,7 +40,7 @@ class SettingsWindow : public QFrame { void closePanel(); void closeParentToggle(); void closeSubParentToggle(); - void updateMetric(); + void updateMetric(bool metric, bool bootRun=false); private: QPushButton *sidebar_alert_widget; @@ -90,7 +90,7 @@ class TogglesPanel : public ListWidget { signals: // FrogPilot signals - void updateMetric(); + void updateMetric(bool metric, bool bootRun=false); public slots: void expandToggleDescription(const QString ¶m); diff --git a/selfdrive/ui/qt/offroad/software_settings.cc b/selfdrive/ui/qt/offroad/software_settings.cc index a23bfae569d020..c045cb26d2292b 100644 --- a/selfdrive/ui/qt/offroad/software_settings.cc +++ b/selfdrive/ui/qt/offroad/software_settings.cc @@ -32,6 +32,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) { // automatic updates toggle ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"), tr("FrogPilot will automatically update itself and it's assets when you're offroad and connected to Wi-Fi."), ""); + connect(automaticUpdatesToggle, &ToggleControl::toggleFlipped, this, &updateFrogPilotToggles); addItem(automaticUpdatesToggle); // download update btn @@ -137,7 +138,7 @@ void SoftwarePanel::updateLabels() { fs_watch->addParam("UpdateAvailable"); if (!isVisible()) { - scene.keep_screen_on = false; + scene.downloading_update = false; return; } @@ -153,9 +154,9 @@ void SoftwarePanel::updateLabels() { if (updater_state != "idle") { downloadBtn->setEnabled(false); downloadBtn->setValue(updater_state); - scene.keep_screen_on = true; + scene.downloading_update = true; } else { - scene.keep_screen_on = false; + scene.downloading_update = false; if (failed) { downloadBtn->setText(tr("CHECK")); downloadBtn->setValue(tr("failed to check for update")); diff --git a/selfdrive/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc index c945081dd8e613..66421e015c4022 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.cc +++ b/selfdrive/ui/qt/onroad/annotated_camera.cc @@ -54,7 +54,9 @@ void AnnotatedCameraWidget::updateState(int alert_height, const UIState &s) { const auto cs = sm["controlsState"].getControlsState(); const auto car_state = sm["carState"].getCarState(); const auto nav_instruction = sm["navInstruction"].getNavInstruction(); - + brakeSignal = s.scene.brake_signal; + standstill = s.scene.standstill; + brakeLightOn = s.scene.brake_lights_on; // Handle older routes where vCruiseCluster is not set float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster(); setSpeed = cs_alive ? v_cruise : SET_SPEED_NA; @@ -76,6 +78,9 @@ void AnnotatedCameraWidget::updateState(int alert_height, const UIState &s) { speedLimit = nav_alive ? nav_instruction.getSpeedLimit() : 0.0; } speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH); + if (s.scene.speed_limit_controller && !showSLCOffset && !slcOverridden && speedLimit != 0) { + speedLimit += slcSpeedLimitOffset; + } has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || !useViennaSLCSign && !hideSpeedLimit; has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) || useViennaSLCSign && !hideSpeedLimit; @@ -400,8 +405,16 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { p.setFont(InterFont(66)); drawText(p, rect().center().x(), 290, QString("%1 seconds").arg(seconds)); } else { + //check if brake signal is active + bool isBraking = (brakeSignal && (brakeLightOn || standstill)); p.setFont(InterFont(176, QFont::Bold)); - drawText(p, rect().center().x(), 210, speedStr); + if (isBraking) { + p.setPen(QColor(255, 0, 0, 255)); // Red + } else { + p.setPen(QColor(255, 255, 255, 255)); // White + }; + + drawText(p, rect().center().x(), 210, speedStr, 255, true); p.setFont(InterFont(66)); drawText(p, rect().center().x(), 290, speedUnit, 200); } @@ -757,7 +770,7 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState .arg(qRound(lead_speed * speedConversionMetrics)) .arg(leadSpeedUnit); } else { - text = QString("%1 %2 (%3) | %4 %5 | %6 %7") + text = QString("%1 %2 (%3) | %4 %5 | %6%7") .arg(qRound(d_rel * distanceConversion)) .arg(leadDistanceUnit) .arg(QString("Desired: %1").arg(desiredFollow * distanceConversion)) @@ -1206,10 +1219,6 @@ void PedalIcons::updateState(const UIScene &scene) { accelerating = acceleration > 0.25f; decelerating = acceleration < -0.25f; - - if (accelerating || decelerating) { - update(); - } } void PedalIcons::paintEvent(QPaintEvent *event) { diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h index d070caf051e2d2..eaffd25fb009f9 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/qt/onroad/annotated_camera.h @@ -57,6 +57,9 @@ class AnnotatedCameraWidget : public CameraWidget { QString speedUnit; float setSpeed; float speedLimit; + bool brakeLightOn; + bool standstill; + bool brakeSignal = false; bool is_cruise_set = false; bool is_metric = false; bool dmActive = false; diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc index 4eb0b4861ceb2d..5e0dd51c8ac0be 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.cc +++ b/selfdrive/ui/qt/onroad/onroad_home.cc @@ -214,31 +214,28 @@ void OnroadWindow::paintEvent(QPaintEvent *event) { static float smoothedSteer = 0.0; smoothedSteer = 0.1 * std::abs(steer) + 0.9 * smoothedSteer; - if (std::abs(smoothedSteer - steer) < 0.01) { smoothedSteer = steer; } - int visibleHeight = rect.height() * smoothedSteer; - QLinearGradient gradient(rect.topLeft(), rect.bottomLeft()); gradient.setColorAt(0.0, bg_colors[STATUS_TRAFFIC_MODE_ACTIVE]); gradient.setColorAt(0.15, bg_colors[STATUS_EXPERIMENTAL_MODE_ACTIVE]); gradient.setColorAt(0.5, bg_colors[STATUS_CONDITIONAL_OVERRIDDEN]); gradient.setColorAt(0.85, bg_colors[STATUS_ENGAGED]); gradient.setColorAt(1.0, bg_colors[STATUS_ENGAGED]); - QBrush brush(gradient); - int fillWidth = UI_BORDER_SIZE; if (steeringAngleDeg != 0) { + int visibleHeight = rect.height() * smoothedSteer; QRect rectToFill, rectToHide; + if (steeringAngleDeg < 0) { - rectToFill = QRect(rect.x(), rect.y() + rect.height() - visibleHeight, fillWidth, visibleHeight); - rectToHide = QRect(rect.x(), rect.y(), fillWidth, rect.height() - visibleHeight); + rectToFill = QRect(rect.x(), rect.y() + rect.height() - visibleHeight, UI_BORDER_SIZE, visibleHeight); + rectToHide = QRect(rect.x(), rect.y(), UI_BORDER_SIZE, rect.height() - visibleHeight); } else { - rectToFill = QRect(rect.x() + rect.width() - fillWidth, rect.y() + rect.height() - visibleHeight, fillWidth, visibleHeight); - rectToHide = QRect(rect.x() + rect.width() - fillWidth, rect.y(), fillWidth, rect.height() - visibleHeight); + rectToFill = QRect(rect.x() + rect.width() - UI_BORDER_SIZE, rect.y() + rect.height() - visibleHeight, UI_BORDER_SIZE, visibleHeight); + rectToHide = QRect(rect.x() + rect.width() - UI_BORDER_SIZE, rect.y(), UI_BORDER_SIZE, rect.height() - visibleHeight); } p.fillRect(rectToFill, brush); p.fillRect(rectToHide, bgColor); diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index 396ad791e7e311..8343b4aaa3e96f 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -61,7 +61,7 @@ void Sidebar::updateIcons() { void Sidebar::updateIcon(QLabel *&label, QMovie *&gif, const QString &gifPath, const QRect &btnRect, const QString &pngPath, bool &isGif) { QString selectedGifPath = gifPath; - if (qrand() % 100 == 0 && btnRect == home_btn && isRandomEvents) { + if (util::random_int(1, 100) == 100 && btnRect == home_btn && isRandomEvents) { selectedGifPath = randomEventGifPath; } diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 5cd917ca930067..21241b7a71bd64 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -309,7 +309,7 @@ static void update_state(UIState *s) { scene.vtsc_controlling_curve = frogpilotPlan.getVtscControllingCurve(); scene.vtsc_speed = frogpilotPlan.getVtscSpeed(); if (frogpilotPlan.getTogglesUpdated() && sm.frame % UI_FREQ == 0) { - scene.frogpilot_toggles = QJsonDocument::fromJson(QString::fromStdString(s->params_memory.get("FrogPilotToggles", true)).toUtf8()).object(); + scene.frogpilot_toggles = QJsonDocument::fromJson(s->params_memory.get("FrogPilotToggles", true).c_str()).object(); ui_update_params(s); ui_update_theme(s); @@ -406,6 +406,9 @@ void ui_update_frogpilot_params(UIState *s) { scene.radarless_model = scene.frogpilot_toggles.value("radarless_model").toBool(); scene.random_events = scene.frogpilot_toggles.value("random_events").toBool(); scene.rainbow_path = scene.frogpilot_toggles.value("rainbow_path").toBool(); + + scene.brake_signal = scene.frogpilot_toggles.value("brake_signal").toBool(); + scene.road_edge_width = scene.frogpilot_toggles.value("road_edge_width").toDouble(); scene.road_name_ui = scene.frogpilot_toggles.value("road_name_ui").toBool(); scene.rotating_wheel = scene.frogpilot_toggles.value("rotating_wheel").toBool(); @@ -552,7 +555,7 @@ void UIState::update() { scene.force_onroad = params_memory.getBool("ForceOnroad"); scene.started_timer = scene.started || started_prev ? scene.started_timer + 1 : 0; - if (scene.keep_screen_on) { + if (scene.downloading_update || scene.frogpilot_panel_active) { device()->resetInteractiveTimeout(scene.screen_timeout, scene.screen_timeout_onroad); } } diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 8413e4b613a4d5..249ee54d99bd89 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -149,10 +149,12 @@ typedef struct UIScene { bool blind_spot_path; bool blind_spot_right; bool brake_lights_on; + bool brake_signal; bool cem_status; bool compass; bool conditional_experimental; bool cpu_metrics; + bool downloading_update; bool driver_camera_in_reverse; bool dynamic_path_width; bool dynamic_pedals_on_ui; @@ -161,6 +163,7 @@ typedef struct UIScene { bool experimental_mode_via_tap; bool fahrenheit; bool force_onroad; + bool frogpilot_panel_active; bool frogs_go_moo; bool full_map; bool gpu_metrics; @@ -173,7 +176,6 @@ typedef struct UIScene { bool hide_speed_limit; bool ip_metrics; bool jerk_metrics; - bool keep_screen_on; bool lateral_tuning_metrics; bool lead_metrics; bool left_curve; diff --git a/sync_chubbspilot.sh b/sync_chubbspilot.sh new file mode 100755 index 00000000000000..430bb683b10454 --- /dev/null +++ b/sync_chubbspilot.sh @@ -0,0 +1,349 @@ +#!/bin/bash + +set -x + +# Function to determine the local network range +get_network_range() { + local ip + ip=$(ipconfig getifaddr en0 2>/dev/null || ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1 | head -n1) + if [[ -z "$ip" ]]; then + echo "ERROR: Unable to determine local IP address." + exit 1 + fi + # Convert IP to subnet (e.g., 192.168.1.0/24) + local subnet + subnet=$(echo "$ip" | awk -F. '{print $1 "." $2 "." $3 ".0/24"}') + echo "$subnet" +} + +# Function to detect device IP using nmap +get_device_ip_nmap() { + local network_range + network_range=$(get_network_range) + echo "Scanning network range: $network_range" + # Scan for devices with SSH ports 22 or 8022 open + nmap -p 22,8022 --open -oG - "$network_range" | awk '/22\/open|8022\/open/{print $2}' +} + +# Automatically detect DEVICE_IP +detect_device_ip() { + local possible_ips + possible_ips=$(get_device_ip_nmap) + local device_ip="" + for ip in $possible_ips; do + if ssh -o ConnectTimeout=2 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$ip" 'exit' 2>/dev/null; then + device_ip="$ip" + break + fi + done + + if [[ -z "$device_ip" ]]; then + echo "ERROR: Failed to detect device IP via nmap." + exit 1 + fi + + echo "$device_ip" +} + +# Configuration +DEVICE_USER="comma" +DEVICE_IP=$(detect_device_ip) + +# Remove existing SSH keys for DEVICE_IP +ssh-keygen -R "$DEVICE_IP" > /dev/null 2>&1 + +echo "Detected Device IP: $DEVICE_IP" + +# Function to get the current git branch on the SSH device +get_device_branch() { + ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'cd /data/openpilot && git rev-parse --abbrev-ref HEAD' +} + +# Function to check if prebuilt exists +check_prebuilt() { + # Check for the 'prebuilt' file + PREBUILT_FILE=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'test -f /data/openpilot/prebuilt && echo "yes" || echo "no"') + + # Additionally, check if any .so files exist as an indicator + ESSENTIAL_SO=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'find /data/openpilot -type f -name "*.so" | grep -q . && echo "yes" || echo "no"') + + if [[ "$PREBUILT_FILE" == "yes" || "$ESSENTIAL_SO" == "yes" ]]; then + echo "yes" + else + echo "no" + fi +} + +# Function to perform compilation +compile_device() { + echo "Performing device compilation..." + + echo "Connecting to device at $DEVICE_IP..." + OUTPUT=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" << 'ENDSSH' + set +x # Disable debug mode for cleaner output + set -e + cd /data/openpilot + + # Setup environment + export PATH="/data/data/com.termux/files/usr/bin:$PATH" + export LD_LIBRARY_PATH="/usr/lib/aarch64-linux-gnu:/usr/local/lib:$LD_LIBRARY_PATH" + + # Clean and compile + echo "Cleaning previous compilation..." + if ! scons -c; then + echo "ERROR: Clean failed" + exit 1 + fi + + echo "Starting compilation..." + if ! scons -j$(nproc); then + echo "ERROR: Compilation failed" + exit 1 + fi + + # Wait for compilation to complete + while pgrep -f "scons" > /dev/null; do + echo "Waiting for compilation to finish..." + sleep 5 + done + + # Final check if compilation succeeded + if ! scons --check; then + echo "ERROR: Compilation failed during final check" + exit 1 + fi + + echo "Compilation completed successfully" + + # Add compiled files + git add -f . + git commit -m "compile" --allow-empty + + # Delete all .cc files except specified ones + find . -type f -name '*.cc' ! \( -name 'main.cc' -o -name 'map_renderer.cc' -o -name 'transform.cc' \) -delete + + # Delete all .h files except specified ones + find . -type f -name '*.h' ! \( -name 'sensor.h' -o -name 'version.h' -o -name 'map_renderer.h' -o -name 'ox03c10_registers.h' -o -name 'ar0231_cl.h' -o -name 'os04c10_cl.h' -o -name 'ox03c10_cl.h' -o -name 'os04c10_registers.h' -o -name 'ar0231_registers.h' \) -delete + + # Remove other unwanted file types + find . -name '*.o' -delete + find . -name '*.os' -delete + find . -name '*.pyc' -delete + find . -name 'moc_*' -delete + find . -name '__pycache__' -delete + find . -name '*.a' -delete + + # Remove build system artifacts + rm -rf .sconsign.dblite Jenkinsfile release/ + + # Remove generated directories + rm -rf selfdrive/ubloxd/generated cereal/gen/cpp + + # Remove remaining MPC-generated files + rm -rf selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/main_* + rm -rf selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/main_* + + # Additional Deletions + # Remove specific directories except .github/workflows and delete body/ + find .github -mindepth 1 ! -path './.github/workflows*' -delete + rm -rf .vscode .devcontainer debug \ + tools/cabana tools/camerastream tools/car_porting tools/joystick \ + tools/latencylogger tools/lib/tests tools/plotjuggler tools/profiling \ + tools/replay tools/scripts tools/serial tools/sim tools/ssh \ + tools/ubuntu_setup tools/webcam body/ + + # Remove Docker-related files + rm -f Dockerfile* .dockerignore + + # Remove test files and directories + find . -type f -iname '*test*' -delete + find . -type d -iname '*test*' -exec rm -rf {} + + + # Remove all files named LICENSE (case-insensitive) + find . -type f -iname 'license*' -delete + + # Remove teleoprtc files + find . -type f -iname '*teleoprtc*' -delete + + # Remove configuration files + rm -f .clang-tidy .editorconfig + + # Remove debug-related files and directories + find . -type f -iname '*debug*' -delete + find . -type d -iname '*debug*' -exec rm -rf {} + + + touch prebuilt + + # Amend with cleaned state + git add -f . + git commit --amend -m "Prebuilt" --allow-empty + + # Output hash + echo "HASH=$(git rev-parse HEAD)" +ENDSSH + ) + # Check SSH exit status + SSH_STATUS=$? + if [ $SSH_STATUS -ne 0 ]; then + echo "ERROR: Compilation SSH session failed with status $SSH_STATUS" >&2 + echo "Output was:" >&2 + echo "$OUTPUT" >&2 + exit 1 + fi + + # Extract hash + COMMIT_HASH=$(echo "$OUTPUT" | grep "^HASH=" | cut -d= -f2) + + if ! [[ $COMMIT_HASH =~ ^[0-9a-f]{40}$ ]]; then + echo "ERROR: Invalid commit hash: $COMMIT_HASH" >&2 + echo "Full output was:" >&2 + echo "$OUTPUT" >&2 + exit 1 + fi + + echo "$COMMIT_HASH" +} + +# Function to skip compilation and clean +skip_compile_device() { + echo "Proceeding without recompilation..." >&2 # Redirect to stderr + # Disable set -x for this function to prevent debug logs from polluting OUTPUT + set +x + OUTPUT=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" << 'ENDSSH' + set +x # Disable debug mode + set -e + cd /data/openpilot + + # Add compiled files + git add -f . + git commit -m "compile" --allow-empty + + # Delete all .cc files except specified ones + find . -type f -name '*.cc' ! \( -name 'main.cc' -o -name 'map_renderer.cc' -o -name 'transform.cc' \) -delete + + # Delete all .h files except specified ones + find . -type f -name '*.h' ! \( -name 'sensor.h' -o -name 'version.h' -o -name 'map_renderer.h' -o -name 'ox03c10_registers.h' -o -name 'ar0231_cl.h' -o -name 'os04c10_cl.h' -o -name 'ox03c10_cl.h' -o -name 'os04c10_registers.h' -o -name 'ar0231_registers.h' \) -delete + + # Remove other unwanted file types + find . -name '*.o' -delete + find . -name '*.os' -delete + find . -name '*.pyc' -delete + find . -name 'moc_*' -delete + find . -name '__pycache__' -delete + find . -name '*.a' -delete + + # Remove build system artifacts + rm -rf .sconsign.dblite Jenkinsfile release/ + + # Remove generated directories + rm -rf selfdrive/ubloxd/generated cereal/gen/cpp + + # Remove remaining MPC-generated files + rm -rf selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/main_* + rm -rf selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/main_* + + # Additional Deletions + # Remove specific directories except .github/workflows and delete body/ + find .github -mindepth 1 ! -path './.github/workflows*' -delete + rm -rf .vscode .devcontainer debug \ + tools/cabana tools/camerastream tools/car_porting tools/joystick \ + tools/latencylogger tools/lib/tests tools/plotjuggler tools/profiling \ + tools/replay tools/scripts tools/serial tools/sim tools/ssh \ + tools/ubuntu_setup tools/webcam body/ + + # Remove Docker-related files + rm -f Dockerfile* .dockerignore + + # Remove test files and directories + find . -type f -iname '*test*' -delete + find . -type d -iname '*test*' -exec rm -rf {} + + + # Remove all files named LICENSE (case-insensitive) + find . -type f -iname 'license*' -delete + + # Remove teleoprtc files + find . -type f -iname '*teleoprtc*' -delete + + # Remove configuration files + rm -f .clang-tidy .editorconfig + + # Remove debug-related files and directories + find . -type f -iname '*debug*' -delete + find . -type d -iname '*debug*' -exec rm -rf {} + + + touch prebuilt + + # Amend with cleaned state + git add -f . + git commit --amend -m "Prebuilt" --allow-empty + + # Output hash + echo "HASH=$(git rev-parse HEAD)" +ENDSSH + ) + set -x # Re-enable debug mode + + SSH_STATUS=$? + if [ $SSH_STATUS -ne 0 ]; then + echo "ERROR: Commit and clean SSH session failed with status $SSH_STATUS" >&2 + echo "Output was:" >&2 + echo "$OUTPUT" >&2 + exit 1 + fi + + # Extract hash + COMMIT_HASH=$(echo "$OUTPUT" | grep "^HASH=" | cut -d= -f2) + + if ! [[ $COMMIT_HASH =~ ^[0-9a-f]{40}$ ]]; then + echo "ERROR: Invalid commit hash: $COMMIT_HASH" >&2 + echo "Full output was:" >&2 + echo "$OUTPUT" >&2 + exit 1 + fi + + echo "$COMMIT_HASH" +} + +# Function to handle workspace operations +workspace_operations() { + local device_commit=$1 + echo "=== Starting workspace operations ===" + export GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" + + if ! git remote | grep -q "device"; then + git remote add device ssh://"${DEVICE_USER}"@"${DEVICE_IP}":/data/openpilot + fi + + git fetch device "$BRANCH_NAME" --force + + git checkout "$BRANCH_NAME" || { echo "ERROR: Failed to checkout branch '$BRANCH_NAME'"; exit 1; } + git cherry-pick --no-commit --strategy=recursive -X theirs "$device_commit" || { echo "ERROR: Cherry-pick failed"; exit 1; } + git add -f . + git commit -C "$device_commit" || { echo "ERROR: Commit failed"; exit 1; } + + # Force push to origin + git push --force origin "$BRANCH_NAME" || { echo "ERROR: Force push to origin failed"; exit 1; } +} + +# Main execution +echo "Starting automated sync process..." + +PREBUILT=$(check_prebuilt) + +if [[ "$PREBUILT" == "yes" ]]; then + # Automatically skip recompilation without user confirmation + COMMIT_HASH=$(skip_compile_device) || exit 1 +else + # Perform compilation if not prebuilt + COMMIT_HASH=$(compile_device) || exit 1 +fi + +# Set BRANCH_NAME based on the device's current branch +BRANCH_NAME=$(get_device_branch) +if [[ -z "$BRANCH_NAME" ]]; then + echo "ERROR: Failed to retrieve the device's current branch." + exit 1 +fi + +workspace_operations "$COMMIT_HASH" +echo "Sync process completed." diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py index 8e6ce36351c4e9..ea53c3327b9e34 100755 --- a/system/loggerd/uploader.py +++ b/system/loggerd/uploader.py @@ -27,7 +27,11 @@ UPLOAD_ATTR_NAME = 'user.upload' UPLOAD_ATTR_VALUE = b'1' -UPLOAD_QLOG_QCAM_MAX_SIZE = 5 * 1e6 # MB +MAX_UPLOAD_SIZES = { + "qlog": 25*1e6, # can't be too restrictive here since we use qlogs to find + # bugs, including ones that can cause massive log sizes + "qcam": 5*1e6, +} allow_sleep = bool(os.getenv("UPLOADER_SLEEP", "1")) force_wifi = os.getenv("FORCEWIFI") is not None @@ -174,7 +178,7 @@ def upload(self, name: str, key: str, fn: str, network_type: int, metered: bool) if sz == 0: # tag files of 0 size as uploaded success = True - elif name in self.immediate_priority and sz > UPLOAD_QLOG_QCAM_MAX_SIZE: + elif name in MAX_UPLOAD_SIZES and sz > MAX_UPLOAD_SIZES[name]: cloudlog.event("uploader_too_large", key=key, fn=fn, sz=sz) success = True else: diff --git a/system/manager/process.py b/system/manager/process.py index a1cde729530d14..be7f564e8046b0 100644 --- a/system/manager/process.py +++ b/system/manager/process.py @@ -88,7 +88,7 @@ def restart(self) -> None: self.stop(sig=signal.SIGKILL) self.start() - def check_watchdog(self, started: bool, params: Params) -> None: + def check_watchdog(self, started: bool) -> None: if self.watchdog_max_dt is None or self.proc is None: return @@ -285,7 +285,7 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None else: p.stop(block=False) - p.check_watchdog(started, params) + p.check_watchdog(started) for p in running: p.start() diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 40bd3000898063..c5061a8894646f 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -69,8 +69,8 @@ def run_new_modeld(started, params, CP: car.CarParams, classic_model, frogpilot_ NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar), NativeProcess("loggerd", "system/loggerd", ["./loggerd"], allow_logging), NativeProcess("modeld", "selfdrive/modeld", ["./modeld"], run_new_modeld), - NativeProcess("mapsd", "selfdrive/navd", ["./mapsd"], only_onroad), - PythonProcess("navmodeld", "selfdrive.classic_modeld.navmodeld", only_onroad), + NativeProcess("mapsd", "selfdrive/navd", ["./mapsd"], run_classic_modeld), + PythonProcess("navmodeld", "selfdrive.classic_modeld.navmodeld", run_classic_modeld), NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC), NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)), PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad), @@ -84,7 +84,7 @@ def run_new_modeld(started, params, CP: car.CarParams, classic_model, frogpilot_ PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)), PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI), #PythonProcess("ugpsd", "system.ugpsd", only_onroad, enabled=TICI), - PythonProcess("navd", "selfdrive.navd.navd", only_onroad), + PythonProcess("navd", "selfdrive.navd.navd", run_classic_modeld), PythonProcess("pandad", "selfdrive.pandad.pandad", always_run), PythonProcess("paramsd", "selfdrive.locationd.paramsd", only_onroad), NativeProcess("ubloxd", "system/ubloxd", ["./ubloxd"], ublox, enabled=TICI), @@ -106,6 +106,7 @@ def run_new_modeld(started, params, CP: car.CarParams, classic_model, frogpilot_ NativeProcess("classic_modeld", "selfdrive/classic_modeld", ["./classic_modeld"], run_classic_modeld), PythonProcess("fleet_manager", "selfdrive.frogpilot.fleetmanager.fleet_manager", always_run), PythonProcess("frogpilot_process", "selfdrive.frogpilot.frogpilot_process", always_run), + PythonProcess("mapd", "selfdrive.frogpilot.navigation.mapd", always_run), ] managed_processes = {p.name: p for p in procs} diff --git a/system/sentry.py b/system/sentry.py index 100211814e6948..4b4d257694766f 100644 --- a/system/sentry.py +++ b/system/sentry.py @@ -1,7 +1,6 @@ """Install exception handler for process crash.""" import os import sentry_sdk -import subprocess import traceback from datetime import datetime from enum import Enum @@ -35,12 +34,12 @@ def report_tombstone(fn: str, message: str, contents: str) -> None: def capture_exception(*args, **kwargs) -> None: exc_text = traceback.format_exc() - phrases_to_check = [ + errors_to_ignore = [ "already exists. To overwrite it, set 'overwrite' to True", "setup_quectel failed after retry", ] - if any(phrase in exc_text for phrase in phrases_to_check): + if any(error in exc_text for error in errors_to_ignore): return save_exception(exc_text) @@ -72,7 +71,7 @@ def capture_fingerprint(candidate, params, blocked=False): value = params_tracking.get_int(key) else: if isinstance(params.get(key), bytes): - value = params.get(key).decode('utf-8') + value = params.get(key, encoding='utf-8') else: value = params.get(key) or "0" @@ -93,7 +92,7 @@ def capture_fingerprint(candidate, params, blocked=False): scope.fingerprint = [params.get("DongleId", encoding='utf-8'), candidate] if blocked: - sentry_sdk.capture_message("Blocked user from using the development branch", level='error') + sentry_sdk.capture_message("Blocked user from using the development branch", level='warning') else: sentry_sdk.capture_message(f"Fingerprinted {candidate}", level='info') @@ -101,13 +100,20 @@ def capture_fingerprint(candidate, params, blocked=False): sentry_sdk.flush() +def capture_model(frogpilot_toggles): + sentry_sdk.capture_message(f"User using: {frogpilot_toggles.model_name} - Model Randomizer: {frogpilot_toggles.model_randomizer}", level='info') + + +def capture_user(channel): + sentry_sdk.capture_message(f"Logged user on: {channel}", level='info') + + def set_tag(key: str, value: str) -> None: sentry_sdk.set_tag(key, value) def save_exception(exc_text: str) -> None: - if not os.path.exists(CRASHES_DIR): - os.makedirs(CRASHES_DIR) + os.makedirs(CRASHES_DIR, exist_ok=True) files = [ os.path.join(CRASHES_DIR, datetime.now().strftime('%Y-%m-%d--%H-%M-%S.log')), @@ -137,7 +143,7 @@ def init(project: SentryProject) -> bool: short_branch = build_metadata.channel - if short_branch == "FrogPilot-Development": + if short_branch == "Chubbs": env = "Development" elif build_metadata.release_channel: env = "Release" diff --git a/system/updated/updated.py b/system/updated/updated.py old mode 100755 new mode 100644 index 40fd8b898e8f0a..fb9928d9cbd4ae --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -480,7 +480,7 @@ def main() -> None: # check for update params.put("UpdaterState", "checking...") updater.check_for_update() - params_memory.put_bool("ManualUpdateInitiated", False) + params_memory.remove("ManualUpdateInitiated") # download update last_fetch = read_time_from_param(params, "UpdaterLastFetchTime")