mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-11-04 01:43:43 +08:00
Merge branch 'feature/slc' into ui/slc-ui
This commit is contained in:
@@ -125,6 +125,8 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"ApiCache_DriveStats", PERSISTENT},
|
||||
{"AutoLaneChangeBsmDelay", PERSISTENT},
|
||||
{"AutoLaneChangeTimer", PERSISTENT},
|
||||
{"BlinkerMinLateralControlSpeed", PERSISTENT | BACKUP},
|
||||
{"BlinkerPauseLateralControl", PERSISTENT | BACKUP},
|
||||
{"CarParamsSP", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"CarParamsSPCache", CLEAR_ON_MANAGER_START},
|
||||
{"CarParamsSPPersistent", PERSISTENT},
|
||||
|
||||
@@ -79,7 +79,8 @@ Networking::Networking(QWidget* parent, bool show_advanced) : QFrame(parent) {
|
||||
}
|
||||
|
||||
void Networking::setPrimeType(PrimeState::Type type) {
|
||||
an->setGsmVisible(type == PrimeState::PRIME_TYPE_NONE || type == PrimeState::PRIME_TYPE_LITE);
|
||||
an->setGsmVisible(type == PrimeState::PRIME_TYPE_NONE || type == PrimeState::PRIME_TYPE_UNKNOWN || \
|
||||
type == PrimeState::PRIME_TYPE_PURPLE || type == PrimeState::PRIME_TYPE_LITE);
|
||||
wifi->ipv4_forward = (type == PrimeState::PRIME_TYPE_NONE || type == PrimeState::PRIME_TYPE_LITE);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ qt_src = [
|
||||
]
|
||||
|
||||
lateral_panel_qt_src = [
|
||||
"sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.cc",
|
||||
"sunnypilot/qt/offroad/settings/lateral/lane_change_settings.cc",
|
||||
"sunnypilot/qt/offroad/settings/lateral/mads_settings.cc",
|
||||
"sunnypilot/qt/offroad/settings/lateral/neural_network_lateral_control.cc",
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
*
|
||||
* This file is part of sunnypilot and is licensed under the MIT License.
|
||||
* See the LICENSE.md file in the root directory for more details.
|
||||
*/
|
||||
|
||||
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.h"
|
||||
|
||||
BlinkerPauseLateralSettings::BlinkerPauseLateralSettings(const QString ¶m, const QString &title, const QString &description, const QString &icon, QWidget *parent)
|
||||
: ExpandableToggleRow(param, title, description, icon, parent) {
|
||||
|
||||
pauseLateralSpeed = new OptionControlSP("BlinkerMinLateralControlSpeed", "", "", "", {0, 255}, 5);
|
||||
connect(pauseLateralSpeed, &OptionControlSP::updateLabels, this, &BlinkerPauseLateralSettings::refresh);
|
||||
addItem(pauseLateralSpeed);
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void BlinkerPauseLateralSettings::refresh() {
|
||||
const QString option = QString::fromStdString(params.get("BlinkerMinLateralControlSpeed"));
|
||||
const bool is_metric = params.getBool("IsMetric");
|
||||
const QString unit = is_metric ? "km/h" : "mph";
|
||||
|
||||
pauseLateralSpeed->setLabel(option + " " + unit);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
*
|
||||
* This file is part of sunnypilot and is licensed under the MIT License.
|
||||
* See the LICENSE.md file in the root directory for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "selfdrive/ui/sunnypilot/ui.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/widgets/expandable_row.h"
|
||||
|
||||
class BlinkerPauseLateralSettings : public ExpandableToggleRow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BlinkerPauseLateralSettings(const QString ¶m, const QString &title, const QString &description, const QString &icon, QWidget *parent = nullptr);
|
||||
void refresh();
|
||||
|
||||
private:
|
||||
Params params;
|
||||
OptionControlSP *pauseLateralSpeed;
|
||||
};
|
||||
@@ -64,6 +64,17 @@ LateralPanel::LateralPanel(SettingsWindowSP *parent) : QFrame(parent) {
|
||||
list->addItem(vertical_space(0));
|
||||
list->addItem(horizontal_line());
|
||||
|
||||
// Blinker Pause Lateral Control
|
||||
blinkerPauseLateralSettings = new BlinkerPauseLateralSettings(
|
||||
"BlinkerPauseLateralControl",
|
||||
tr("Pause Lateral Control with Blinker"),
|
||||
tr("Pause lateral control with blinker when traveling below the desired speed selected."),
|
||||
"",
|
||||
this);
|
||||
list->addItem(blinkerPauseLateralSettings);
|
||||
|
||||
list->addItem(horizontal_line());
|
||||
|
||||
// Neural Network Lateral Control
|
||||
nnlcToggle = new NeuralNetworkLateralControl();
|
||||
list->addItem(nnlcToggle);
|
||||
@@ -139,5 +150,7 @@ void LateralPanel::updateToggles(bool _offroad) {
|
||||
|
||||
madsSettingsButton->setEnabled(madsToggle->isToggled());
|
||||
|
||||
blinkerPauseLateralSettings->refresh();
|
||||
|
||||
offroad = _offroad;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "selfdrive/ui/sunnypilot/ui.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/blinker_pause_lateral_settings.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/mads_settings.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/neural_network_lateral_control.h"
|
||||
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral/lane_change_settings.h"
|
||||
@@ -43,6 +44,7 @@ private:
|
||||
PushButtonSP *laneChangeSettingsButton;
|
||||
LaneChangeSettings *laneChangeWidget = nullptr;
|
||||
NeuralNetworkLateralControl *nnlcToggle = nullptr;
|
||||
BlinkerPauseLateralSettings *blinkerPauseLateralSettings = nullptr;
|
||||
|
||||
const QString MADS_BASE_DESC = tr("Enables independent engagements of Automatic Lane Centering (ALC) and Adaptive Cruise Control (ACC).");
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ from opendbc.car import structs
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.sunnypilot.selfdrive.controls.lib.param_store import ParamStore
|
||||
from openpilot.sunnypilot.selfdrive.controls.lib.blinker_pause_lateral import BlinkerPauseLateral
|
||||
|
||||
|
||||
class ControlsExt:
|
||||
def __init__(self, CP: structs.CarParams, params: Params):
|
||||
self.CP = CP
|
||||
self.params = params
|
||||
self.blinker_pause_lateral = BlinkerPauseLateral()
|
||||
self.param_store = ParamStore(self.CP)
|
||||
self.get_params_sp()
|
||||
|
||||
@@ -29,11 +31,13 @@ class ControlsExt:
|
||||
|
||||
def get_params_sp(self) -> None:
|
||||
self.param_store.update(self.params)
|
||||
self.blinker_pause_lateral.get_params()
|
||||
|
||||
def get_lat_active(self, sm: messaging.SubMaster) -> bool:
|
||||
if self.blinker_pause_lateral.update(sm['carState']):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_lat_active(sm: messaging.SubMaster) -> bool:
|
||||
ss_sp = sm['selfdriveStateSP']
|
||||
|
||||
if ss_sp.mads.available:
|
||||
return bool(ss_sp.mads.active)
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
from cereal import car
|
||||
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.common.params import Params
|
||||
|
||||
|
||||
class BlinkerPauseLateral:
|
||||
def __init__(self):
|
||||
self.params = Params()
|
||||
|
||||
self.enabled = self.params.get_bool("BlinkerPauseLateralControl")
|
||||
self.is_metric = self.params.get_bool("IsMetric")
|
||||
self.min_speed = 0
|
||||
|
||||
def get_params(self) -> None:
|
||||
self.enabled = self.params.get_bool("BlinkerPauseLateralControl")
|
||||
self.is_metric = self.params.get_bool("IsMetric")
|
||||
self.min_speed = int(self.params.get("BlinkerMinLateralControlSpeed", encoding='utf8'))
|
||||
|
||||
def update(self, CS: car.CarState) -> bool:
|
||||
if not self.enabled:
|
||||
return False
|
||||
|
||||
one_blinker = CS.leftBlinker != CS.rightBlinker
|
||||
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
|
||||
min_speed_ms = self.min_speed * speed_factor
|
||||
|
||||
return bool(one_blinker and CS.vEgo < min_speed_ms)
|
||||
+66
-42
@@ -1,3 +1,4 @@
|
||||
from collections.abc import Callable
|
||||
import numpy as np
|
||||
import time
|
||||
|
||||
@@ -20,10 +21,14 @@ ENABLED_STATES = (SpeedLimitControlState.preActive, SpeedLimitControlState.tempI
|
||||
|
||||
|
||||
class SpeedLimitController:
|
||||
_speed_limit: float
|
||||
_distance: float
|
||||
_source: Source
|
||||
|
||||
def __init__(self, CP):
|
||||
self._params = Params()
|
||||
self._CP = CP
|
||||
self._policy = Policy(int(self._params.get("SpeedLimitControlPolicy", encoding='utf8')))
|
||||
self._policy = self._read_policy_param()
|
||||
self._resolver = SpeedLimitResolver(self._policy)
|
||||
self._last_params_update = 0.0
|
||||
self._last_op_enabled_time = 0.0
|
||||
@@ -48,12 +53,12 @@ class SpeedLimitController:
|
||||
self._gas_pressed = False
|
||||
self._pcm_cruise_op_long = CP.openpilotLongitudinalControl and CP.pcmCruise
|
||||
|
||||
self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8'))
|
||||
self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8'))
|
||||
self._warning_type = int(self._params.get("SpeedLimitWarningType", encoding='utf8'))
|
||||
self._warning_offset_type = int(self._params.get("SpeedLimitWarningOffsetType", encoding='utf8'))
|
||||
self._warning_offset_value = float(self._params.get("SpeedLimitWarningValueOffset", encoding='utf8'))
|
||||
self._engage_type = np.clip(int(self._params.get("SpeedLimitEngageType", encoding='utf8')), Engage.auto, Engage.user_confirm)
|
||||
self._offset_type = OffsetType(self._read_int_param("SpeedLimitOffsetType"))
|
||||
self._offset_value = self._read_int_param("SpeedLimitValueOffset")
|
||||
self._warning_type = self._read_int_param("SpeedLimitWarningType")
|
||||
self._warning_offset_type = OffsetType(self._read_int_param("SpeedLimitWarningOffsetType"))
|
||||
self._warning_offset_value = self._read_int_param("SpeedLimitWarningValueOffset")
|
||||
self._engage_type = self._read_engage_type_param()
|
||||
self._brake_pressed = False
|
||||
self._brake_pressed_prev = False
|
||||
self._current_time = 0.
|
||||
@@ -61,7 +66,7 @@ class SpeedLimitController:
|
||||
self._v_cruise_prev_rounded = 0.
|
||||
self._speed_limit_offsetted_rounded = 0.
|
||||
self._speed_limit_warning_offsetted_rounded = 0.
|
||||
self._ms_to_local = CV.MS_TO_KPH if self._is_metric else CV.MS_TO_MPH
|
||||
self._speed_factor = CV.MS_TO_KPH if self._is_metric else CV.MS_TO_MPH
|
||||
|
||||
# Mapping functions to state transitions
|
||||
self.state_transition_strategy = {
|
||||
@@ -114,23 +119,11 @@ class SpeedLimitController:
|
||||
|
||||
@property
|
||||
def speed_limit_offset(self) -> float:
|
||||
if self._offset_type == OffsetType.default:
|
||||
return float(np.interp(self._speed_limit, LIMIT_PERC_OFFSET_BP, LIMIT_PERC_OFFSET_V) * self._speed_limit)
|
||||
elif self._offset_type == OffsetType.fixed:
|
||||
return self._offset_value * (CV.KPH_TO_MS if self._is_metric else CV.MPH_TO_MS)
|
||||
elif self._offset_type == OffsetType.percentage:
|
||||
return self._offset_value * 0.01 * self._speed_limit
|
||||
return 0.
|
||||
return self._get_offset(self._offset_type, self._offset_value)
|
||||
|
||||
@property
|
||||
def speed_limit_warning_offset(self) -> float:
|
||||
if self._warning_offset_type == OffsetType.default:
|
||||
return float(np.interp(self._speed_limit, LIMIT_PERC_OFFSET_BP, LIMIT_PERC_OFFSET_V) * self._speed_limit)
|
||||
elif self._warning_offset_type == OffsetType.fixed:
|
||||
return self._warning_offset_value * (CV.KPH_TO_MS if self._is_metric else CV.MPH_TO_MS)
|
||||
elif self._warning_offset_type == OffsetType.percentage:
|
||||
return self._warning_offset_value * 0.01 * self._speed_limit
|
||||
return 0.
|
||||
return self._get_offset(self._warning_offset_type, self._warning_offset_value)
|
||||
|
||||
@property
|
||||
def speed_limit(self) -> float:
|
||||
@@ -144,26 +137,57 @@ class SpeedLimitController:
|
||||
def source(self) -> Source:
|
||||
return self._source
|
||||
|
||||
def _get_offset(self, offset_type: OffsetType, offset_value: int) -> float:
|
||||
if offset_type == OffsetType.default:
|
||||
return float(np.interp(self._speed_limit, LIMIT_PERC_OFFSET_BP, LIMIT_PERC_OFFSET_V) * self._speed_limit)
|
||||
elif offset_type == OffsetType.fixed:
|
||||
return offset_value * (CV.KPH_TO_MS if self._is_metric else CV.MPH_TO_MS)
|
||||
elif offset_type == OffsetType.percentage:
|
||||
return offset_value * 0.01 * self._speed_limit
|
||||
else:
|
||||
raise NotImplementedError("Offset not supported")
|
||||
|
||||
def _update_v_cruise_setpoint_prev(self) -> None:
|
||||
self._v_cruise_setpoint_prev = self._v_cruise_setpoint
|
||||
|
||||
def _update_params(self) -> None:
|
||||
if self._current_time > self._last_params_update + PARAMS_UPDATE_PERIOD:
|
||||
self._is_enabled = self._params.get_bool("SpeedLimitControl")
|
||||
self._offset_type = int(self._params.get("SpeedLimitOffsetType", encoding='utf8'))
|
||||
self._offset_value = float(self._params.get("SpeedLimitValueOffset", encoding='utf8'))
|
||||
self._warning_type = int(self._params.get("SpeedLimitWarningType", encoding='utf8'))
|
||||
self._warning_offset_type = int(self._params.get("SpeedLimitWarningOffsetType", encoding='utf8'))
|
||||
self._warning_offset_value = float(self._params.get("SpeedLimitWarningValueOffset", encoding='utf8'))
|
||||
self._policy = Policy(int(self._params.get("SpeedLimitControlPolicy", encoding='utf8')))
|
||||
self._offset_type = OffsetType(self._read_int_param("SpeedLimitOffsetType"))
|
||||
self._offset_value = self._read_int_param("SpeedLimitValueOffset")
|
||||
self._warning_type = self._read_int_param("SpeedLimitWarningType")
|
||||
self._warning_offset_type = OffsetType(self._read_int_param("SpeedLimitWarningOffsetType"))
|
||||
self._warning_offset_value = self._read_int_param("SpeedLimitWarningValueOffset")
|
||||
self._policy = self._read_policy_param()
|
||||
self._is_metric = self._params.get_bool("IsMetric")
|
||||
self._ms_to_local = CV.MS_TO_KPH if self._is_metric else CV.MS_TO_MPH
|
||||
self._speed_factor = CV.MS_TO_KPH if self._is_metric else CV.MS_TO_MPH
|
||||
self._resolver.change_policy(self._policy)
|
||||
self._engage_type = Engage.auto if self._pcm_cruise_op_long else \
|
||||
np.clip(int(self._params.get("SpeedLimitEngageType", encoding='utf8')), Engage.auto, Engage.user_confirm)
|
||||
debug(f'Updated Speed limit params. enabled: {self._is_enabled}, with offset: {self._offset_type}')
|
||||
self._engage_type = self._read_engage_type_param()
|
||||
|
||||
self._last_params_update = self._current_time
|
||||
|
||||
def _read_policy_param(self) -> Policy:
|
||||
try:
|
||||
return Policy(int(self._params.get("SpeedLimitControlPolicy", encoding='utf8')))
|
||||
except (ValueError, TypeError):
|
||||
return Policy.car_state_priority
|
||||
|
||||
def _read_engage_type_param(self) -> Engage:
|
||||
if self._pcm_cruise_op_long:
|
||||
return Engage.auto
|
||||
|
||||
return Engage(self._read_int_param("SpeedLimitEngageType", validator=lambda x: np.clip(x, Engage.auto, Engage.user_confirm)))
|
||||
|
||||
def _read_int_param(self, key: str, default: int = 0, validator: Callable[[int], int] = None) -> int:
|
||||
try:
|
||||
val = int(self._params.get(key, encoding='utf8'))
|
||||
|
||||
if validator is not None:
|
||||
return validator(val)
|
||||
return val
|
||||
except (ValueError, TypeError):
|
||||
return default
|
||||
|
||||
def _update_calculations(self) -> None:
|
||||
# Update current velocity offset (error)
|
||||
self._v_offset = self.speed_limit_offseted - self._v_ego
|
||||
@@ -184,11 +208,11 @@ class SpeedLimitController:
|
||||
self._op_enabled_prev = self._op_enabled
|
||||
self._brake_pressed_prev = self._brake_pressed
|
||||
|
||||
self._v_cruise_rounded = int(round(self._v_cruise_setpoint * self._ms_to_local))
|
||||
self._v_cruise_prev_rounded = int(round(self._v_cruise_setpoint_prev * self._ms_to_local))
|
||||
self._speed_limit_offsetted_rounded = 0 if self._speed_limit == 0 else int(round((self._speed_limit + self.speed_limit_offset) * self._ms_to_local))
|
||||
self._v_cruise_rounded = int(round(self._v_cruise_setpoint * self._speed_factor))
|
||||
self._v_cruise_prev_rounded = int(round(self._v_cruise_setpoint_prev * self._speed_factor))
|
||||
self._speed_limit_offsetted_rounded = 0 if self._speed_limit == 0 else int(round((self._speed_limit + self.speed_limit_offset) * self._speed_factor))
|
||||
self._speed_limit_warning_offsetted_rounded = 0 if self._speed_limit == 0 else \
|
||||
int(round((self._speed_limit + self.speed_limit_warning_offset) * self._ms_to_local))
|
||||
int(round((self._speed_limit + self.speed_limit_warning_offset) * self._speed_factor))
|
||||
|
||||
def transition_state_from_inactive(self) -> None:
|
||||
""" Make state transition from inactive state """
|
||||
@@ -272,27 +296,27 @@ class SpeedLimitController:
|
||||
|
||||
def get_current_acceleration_as_target(self) -> float:
|
||||
""" When state is inactive or tempInactive, preserve current acceleration """
|
||||
return self._a_ego
|
||||
return float(self._a_ego)
|
||||
|
||||
def get_adapting_state_target_acceleration(self) -> float:
|
||||
""" In adapting state, calculate target acceleration based on speed limit and current velocity """
|
||||
if self.distance > 0:
|
||||
return (self.speed_limit_offseted ** 2 - self._v_ego ** 2) / (2. * self.distance)
|
||||
return float((self.speed_limit_offseted ** 2 - self._v_ego ** 2) / (2. * self.distance))
|
||||
|
||||
return self._v_offset / ModelConstants.T_IDXS[CONTROL_N]
|
||||
return float(self._v_offset / ModelConstants.T_IDXS[CONTROL_N])
|
||||
|
||||
def get_active_state_target_acceleration(self) -> float:
|
||||
""" In active state, aim to keep speed constant around control time horizon """
|
||||
return self._v_offset / ModelConstants.T_IDXS[CONTROL_N]
|
||||
return float(self._v_offset / ModelConstants.T_IDXS[CONTROL_N])
|
||||
|
||||
def _update_events(self, events_sp: EventsSP) -> None:
|
||||
if self._speed_limit > 0 and self._warning_type == 2 and \
|
||||
self._speed_limit_warning_offsetted_rounded < int(round(self._v_ego * self._ms_to_local)):
|
||||
self._speed_limit_warning_offsetted_rounded < int(round(self._v_ego * self._speed_factor)):
|
||||
events_sp.add(EventNameSP.speedLimitPreActive)
|
||||
|
||||
if not self.is_active:
|
||||
if self._state == SpeedLimitControlState.preActive and self._state_prev != SpeedLimitControlState.preActive and \
|
||||
self._v_cruise_rounded != self._speed_limit_offsetted_rounded:
|
||||
self._v_cruise_rounded != self._speed_limit_offsetted_rounded:
|
||||
events_sp.add(EventNameSP.speedLimitPreActive)
|
||||
else:
|
||||
if self._engage_type == Engage.user_confirm:
|
||||
|
||||
@@ -7,9 +7,12 @@ from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_controller.helpers
|
||||
|
||||
|
||||
class SpeedLimitResolver:
|
||||
_limit_solutions: dict[Source, float] # Store for speed limit solutions from different sources
|
||||
_distance_solutions: dict[Source, float] # Store for distance to current speed limit start for different sources
|
||||
|
||||
def __init__(self, policy: Policy):
|
||||
self._limit_solutions = {} # Store for speed limit solutions from different sources
|
||||
self._distance_solutions = {} # Store for distance to current speed limit start for different sources
|
||||
self._limit_solutions = {}
|
||||
self._distance_solutions = {}
|
||||
|
||||
self._policy = policy
|
||||
self._policy_to_sources_map = {
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
|
||||
|
||||
This file is part of sunnypilot and is licensed under the MIT License.
|
||||
See the LICENSE.md file in the root directory for more details.
|
||||
"""
|
||||
from cereal import car
|
||||
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.sunnypilot.selfdrive.controls.lib.blinker_pause_lateral import BlinkerPauseLateral
|
||||
|
||||
|
||||
class TestBlinkerPauseLateral:
|
||||
|
||||
def setup_method(self):
|
||||
self.blinker_pause_lateral = BlinkerPauseLateral()
|
||||
self._reset_states()
|
||||
|
||||
def _reset_states(self):
|
||||
self.blinker_pause_lateral.enabled = True
|
||||
self.blinker_pause_lateral.is_metric = False
|
||||
self.blinker_pause_lateral.min_speed = 20 # MPH
|
||||
|
||||
self.CS = car.CarState.new_message()
|
||||
self.CS.vEgo = 0
|
||||
self.CS.leftBlinker = False
|
||||
self.CS.rightBlinker = False
|
||||
|
||||
def _test_should_blinker_pause_lateral(self, expected_results) -> None:
|
||||
for left in (True, False):
|
||||
for right in (True, False):
|
||||
self.CS.leftBlinker = left
|
||||
self.CS.rightBlinker = right
|
||||
|
||||
result = self.blinker_pause_lateral.update(self.CS)
|
||||
assert result == expected_results[(left, right)]
|
||||
|
||||
def test_below_min_speed_blinker(self):
|
||||
self.CS.vEgo = 4.5 # ~10 MPH
|
||||
|
||||
expected_results = {
|
||||
(False, False): False,
|
||||
(True, False): True,
|
||||
(False, True): True,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
def test_above_min_speed_blinker(self):
|
||||
self.CS.vEgo = 13.4 # ~30 MPH
|
||||
|
||||
expected_results = {
|
||||
(False, False): False,
|
||||
(True, False): False,
|
||||
(False, True): False,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
def test_just_below_min_speed(self):
|
||||
self.CS.vEgo = (20 * CV.MPH_TO_MS) - 0.01
|
||||
|
||||
expected_results = {
|
||||
(False, False): False,
|
||||
(True, False): True,
|
||||
(False, True): True,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
def test_disabled(self):
|
||||
self.blinker_pause_lateral.enabled = False
|
||||
self.CS.vEgo = 4.5 # ~10 MPH
|
||||
|
||||
expected_results = {
|
||||
(False, False): False,
|
||||
(True, False): False,
|
||||
(False, True): False,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
def test_metric_units_below_min_speed(self):
|
||||
self.blinker_pause_lateral.is_metric = True
|
||||
self.CS.vEgo = 5.0 # ~18 km/h
|
||||
|
||||
expected_results = {
|
||||
(False, False): False,
|
||||
(True, False): True,
|
||||
(False, True): True,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
def test_metric_units_above_threshold(self):
|
||||
self.blinker_pause_lateral.is_metric = True
|
||||
self.CS.vEgo = 6.0 # ~21.6 km/h
|
||||
|
||||
expected_results = {
|
||||
(False, False): False,
|
||||
(True, False): False,
|
||||
(False, True): False,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
def test_change_min_speed_threshold(self):
|
||||
self.blinker_pause_lateral.min_speed = 30 # MPH
|
||||
|
||||
# below min speed
|
||||
self.CS.vEgo = 11.2 # ~25 MPH
|
||||
|
||||
expected_results = {
|
||||
(False, False): False,
|
||||
(True, False): True,
|
||||
(False, True): True,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
|
||||
# above min speed
|
||||
self.CS.vEgo = 15.6 # ~35 MPH
|
||||
|
||||
expected_results = {
|
||||
(False, False): False,
|
||||
(True, False): False,
|
||||
(False, True): False,
|
||||
(True, True): False
|
||||
}
|
||||
self._test_should_blinker_pause_lateral(expected_results)
|
||||
@@ -46,6 +46,8 @@ def manager_init() -> None:
|
||||
sunnypilot_default_params: list[tuple[str, str | bytes]] = [
|
||||
("AutoLaneChangeTimer", "0"),
|
||||
("AutoLaneChangeBsmDelay", "0"),
|
||||
("BlinkerMinLateralControlSpeed", "20"), # MPH or km/h
|
||||
("BlinkerPauseLateralControl", "0"),
|
||||
("DynamicExperimentalControl", "0"),
|
||||
("HyundaiLongitudinalTuning", "0"),
|
||||
("Mads", "1"),
|
||||
|
||||
Reference in New Issue
Block a user