mirror of
https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot.git
synced 2026-09-08 14:53:42 +08:00
IQ.Pilot Release Commit @ 4b0cfbe
This commit is contained in:
@@ -116,7 +116,7 @@
|
||||
},
|
||||
"python/iqpilot_private/navassist/__init__.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "d07a56d54a8dfd8dd66b0a58a709cd13585de455b7eeecbb50616792757193e1",
|
||||
"sha256": "2ff824301dba9858009c5896a1539a702e7d83a5bfa401ced739afe56d6a6663",
|
||||
"size": 67640
|
||||
},
|
||||
"python/iqpilot_private/navassist/__init__.py": {
|
||||
@@ -126,32 +126,32 @@
|
||||
},
|
||||
"python/iqpilot_private/navassist/appsetup.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "d7ba6546b659c439dfd747ee80419117493db6bdfee5305965428896aa75e720",
|
||||
"size": 135328
|
||||
"sha256": "2f37ea823cbdd13183539d343853df77c74c63f10a45c1d0d2dfa6dd504a5e05",
|
||||
"size": 135392
|
||||
},
|
||||
"python/iqpilot_private/navassist/assets.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "45f9529ee808c55bd2e8965a6c72e5050dd8d12f47fd71e996a5aad161aa2e6b",
|
||||
"sha256": "d2d9637b01968c656d35a583b238030b13de1d0a942d1a4ba2c5895c25701399",
|
||||
"size": 201704
|
||||
},
|
||||
"python/iqpilot_private/navassist/container.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "16d2d214e9d4a793ead2b623fade799cf842e739b25cf74ab5ed60cb7c26f2f3",
|
||||
"sha256": "f5bca5aa39d29ae40e289bb4381b4e0e1596ae5203adc501aada8f0223ca5a21",
|
||||
"size": 134016
|
||||
},
|
||||
"python/iqpilot_private/navassist/daemon.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "f2d8fd727cb6002c174ea47ccd2945977143e5abe6d188732217474bcbd931bc",
|
||||
"size": 203048
|
||||
"sha256": "97c8ff983b101ae0f6f034f9a041cd410f7cdd656f6baf6067c1acb669767e74",
|
||||
"size": 203224
|
||||
},
|
||||
"python/iqpilot_private/navassist/guard.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "2aed237187dcfa009ebbd5f0f6beab7a5da4d89cd3b4e37968a2bb62f7fc6468",
|
||||
"sha256": "d264372e8f219533da1f9bdc0e5793e6c10c42937012138c304e9f8ed6751763",
|
||||
"size": 68240
|
||||
},
|
||||
"python/iqpilot_private/navassist/paths.cpython-312-aarch64-linux-gnu.so": {
|
||||
"mode": 493,
|
||||
"sha256": "0e63117bb686de08c73176138cc3851cbdadf491774b04e752995c059dcd281a",
|
||||
"sha256": "7bec3818357726584894cb137b4c6a07c811a8178d8aaf6e3ee77597273728bc",
|
||||
"size": 69656
|
||||
},
|
||||
"runtime": {
|
||||
@@ -163,12 +163,12 @@
|
||||
}
|
||||
},
|
||||
"signatures": {
|
||||
"python/iqpilot_private/navassist/__init__.cpython-312-aarch64-linux-gnu.so": "grzvl6Szgy0DhX0A2RHxMH+EZYP4GDUwRwrXCI278zqBo9A+xdZPxBSQ0FIxoDzMZVrAfrK5yOADYYyF1gFkDQ==",
|
||||
"python/iqpilot_private/navassist/appsetup.cpython-312-aarch64-linux-gnu.so": "e4WP9/AodSGn0e1aCmUfLvDCA36Z3lyLQXTAXeuMpFamR2uu7SRYCKBoW7gIMtrqZvNi3DNeDfmPaWdITSlDDA==",
|
||||
"python/iqpilot_private/navassist/assets.cpython-312-aarch64-linux-gnu.so": "K9jDY0eQxqtE7RKPmnnjJhOYogK4mkUXxucZmkY+K798BWThYxT+Q0q2D50eW9vc33fKH0lk4+L4w4P/qkTzDA==",
|
||||
"python/iqpilot_private/navassist/container.cpython-312-aarch64-linux-gnu.so": "qE6DWECE/wsj58n9ewIFAEts8xTCKjWGQ0p+7fZ3XKFJR7hvFt7fNTZAQm1sBSAd55697ow7kq7Y1wyicvBBDw==",
|
||||
"python/iqpilot_private/navassist/daemon.cpython-312-aarch64-linux-gnu.so": "h2yssDIlxVDJMaPW+K3TzGKwzrGcGDdQdOrZ75LmTS02UN34tmPWzv0j7WV2ombz5n8gu8+FcxYB+o1VapqkDA==",
|
||||
"python/iqpilot_private/navassist/guard.cpython-312-aarch64-linux-gnu.so": "kTPke8BA2muzet1xLQWUKnFtlVboJSZ6Ep6O79y2zQ8xMkEPum2fHiYZr8wkqu+2NMEB/j1HO0BvwJPdl/qOBg==",
|
||||
"python/iqpilot_private/navassist/paths.cpython-312-aarch64-linux-gnu.so": "Sd7DGPwD4DDrbqrnCiIwGgGAnTXU5MTS7rNdpq2rsixxvXhFzaaxQgMbGFi6CoylBAHeOwqi9uwjtY6aiaYkCQ=="
|
||||
"python/iqpilot_private/navassist/__init__.cpython-312-aarch64-linux-gnu.so": "pB2EuFRYCo2d0WQnYAziKZMtTF0myedrlt4jPt6baypR49sl15qOFiKnFW8iL/C91mC8yrb8mgRu9DEJNs07Bw==",
|
||||
"python/iqpilot_private/navassist/appsetup.cpython-312-aarch64-linux-gnu.so": "y/P4Ni0GEBmqZHULY14DX/Odh4ByFo/tDd8QlgHiAM22QJ0hXhPRwr6AqbHqNADfjYSEOrGS30Oz/aXcXlXBBw==",
|
||||
"python/iqpilot_private/navassist/assets.cpython-312-aarch64-linux-gnu.so": "TzTvaBh0Wzuesg5T1GP6QKX253masiMTyYtcKkz2Mptfc8LcAq7xbJmPP5RfviDXiv0tEFe6PfH/LEvD8Xv6AQ==",
|
||||
"python/iqpilot_private/navassist/container.cpython-312-aarch64-linux-gnu.so": "bzSB5YmGEMbPX6TJn9R+g3hvIWI9sQYUOrSWofpQTFNcoPcf2WcLJRM9Fl1LbIdRWtEx2lWPQwjAQ5pd6J+4BQ==",
|
||||
"python/iqpilot_private/navassist/daemon.cpython-312-aarch64-linux-gnu.so": "sVR22V1Mfp2b2anriiZ16zoLPUT+/WT6uTpcfTErx8PxX2Ox/gWwe4ifzQMkWezFIaWn2imT4CehxhQZCA8HDA==",
|
||||
"python/iqpilot_private/navassist/guard.cpython-312-aarch64-linux-gnu.so": "k9Fz2A9GIFbJE+LEcFSmPWaNfYi0y3ZtVyP6P9e9vgk7KiugIazn5q9hRwUSlbBAn92vx8vU875Zv3/sqpr4AA==",
|
||||
"python/iqpilot_private/navassist/paths.cpython-312-aarch64-linux-gnu.so": "PplaPn3UzgTejl36JQ0tKHq9qd2Z8YqE7dX6z0+tHWaITgPdMhV5bDOXjt4haJCvzdN/Z5moPkXHPyCgKB4SCQ=="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
+k8uY8dYPLT5xuTlqHHgo9YUJkOQM6KQxhPn4leJUi2t9DsHDEMBWJwdtyqzQi5Zes9eRuG9GmF7Hircxj88DQ==
|
||||
Vx6Vm8MyJE7uWp+sCDMp9K8NBG6lbJ2w49N8AXP8pAnM82G2NB1SWe4pTI4AqwoUle/n93Ydpdvwhv1kAztuCg==
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -845,6 +845,7 @@ class CarState(CarStateBase):
|
||||
pt_messages += [
|
||||
("Blinkmodi_01", math.nan), # From J519 BCM (is inactive when no lights active, 50Hz when active)
|
||||
("Kombi_02", math.nan), # Auxiliary-bus cluster odometer
|
||||
("LH_EPS_01", math.nan), # ALC key slot, absent on MLB racks that never send 0x32A
|
||||
]
|
||||
else:
|
||||
pt_messages += [("Kombi_02", math.nan)] # Auxiliary-bus cluster odometer
|
||||
|
||||
@@ -10,8 +10,8 @@ from iqdbc.car.volkswagen.carstate import CarState
|
||||
from iqdbc.car.structs import CarParams
|
||||
from iqdbc.car.volkswagen.interface import CarInterface
|
||||
from iqdbc.car.volkswagen.values import (CAR, FW_QUERY_CONFIG, MLB_ACC_COORDINATOR_MSGS, MLB_GEARBOX_MSGS, MLB_MSG_ACC_10,
|
||||
MLB_MSG_GATEWAY_05, MLB_MSG_GETRIEBE_01, MLB_MSG_LH_EPS_03, WMI, VolkswagenFlags,
|
||||
VolkswagenFlagsIQ, VolkswagenSafetyFlags)
|
||||
MLB_MSG_GATEWAY_05, MLB_MSG_GETRIEBE_01, MLB_MSG_LH_EPS_01, MLB_MSG_LH_EPS_03,
|
||||
WMI, VolkswagenFlags, VolkswagenFlagsIQ, VolkswagenSafetyFlags)
|
||||
from iqdbc.car.volkswagen.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
@@ -210,7 +210,6 @@ def _a4_mk4_frames(packer, reverse=False, eps_torque=None):
|
||||
packer.make_can_msg("Kombi_02", 1, {"KBI_Inhalt_Tank": 40, "KBI_Kilometerstand": 100000}),
|
||||
packer.make_can_msg("Airbag_02", 1, {"AB_Gurtschloss_FA": 3}),
|
||||
packer.make_can_msg("Gateway_05", 1, {"BCM1_Rueckfahrlicht_Schalter": int(reverse)}),
|
||||
packer.make_can_msg("LH_EPS_01", 1, {}),
|
||||
]
|
||||
if eps_torque is not None:
|
||||
msgs.append(packer.make_can_msg("LH_EPS_03", 1, {"EPS_Lenkmoment": abs(eps_torque),
|
||||
@@ -499,6 +498,32 @@ def test_mlb_manual_gateway_car_adds_only_the_reverse_switch_on_the_powertrain_b
|
||||
assert manual[Bus.aux] == automatic[Bus.aux] | {MLB_MSG_GATEWAY_05}
|
||||
|
||||
|
||||
def _read_like_the_alc_runtime(parser, name):
|
||||
# the closed-source ALC reads its key slot through parser.vl, which subscribes the message on first access
|
||||
if name not in parser.dat:
|
||||
parser.vl[name]
|
||||
return parser.dat.get(name, b"")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("build", (_a4_mk4_car, _q5_mk1_car), ids=("a4_mk4", "q5_mk1"))
|
||||
def test_mlb_alc_key_slot_read_keeps_can_valid_on_a_rack_that_never_sends_it(build):
|
||||
car = build()
|
||||
subscribed = _subscribed_addresses(car)
|
||||
never_sent = {bus: addrs - {MLB_MSG_LH_EPS_01} for bus, addrs in subscribed.items()}
|
||||
|
||||
packer = CANPacker("vw_mlb")
|
||||
|
||||
def build_frames():
|
||||
_read_like_the_alc_runtime(car.can_parsers[Bus.pt], "LH_EPS_01")
|
||||
return _frames_for(car, packer, never_sent)
|
||||
|
||||
ret = _run_past_aliveness_timeout(car, build_frames)
|
||||
|
||||
assert ret.canValid
|
||||
assert all(parser.can_valid for parser in car.can_parsers.values())
|
||||
assert not any(parser.bus_timeout for parser in car.can_parsers.values())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("build", (_a4_mk4_car, _q5_mk1_car), ids=("a4_mk4", "q5_mk1"))
|
||||
def test_mlb_carstate_subscriptions_settle_and_stay_alive(build):
|
||||
car = build()
|
||||
|
||||
@@ -339,6 +339,7 @@ class VolkswagenFlagsIQ(IntFlag):
|
||||
|
||||
RADAR_DISABLE_STATE = {"error": False}
|
||||
|
||||
MLB_MSG_LH_EPS_01 = 0x32A
|
||||
MLB_MSG_LH_EPS_03 = 0x9F
|
||||
MLB_MSG_GETRIEBE_01 = 0x82
|
||||
MLB_MSG_GETRIEBE_02 = 0x83
|
||||
|
||||
@@ -361,6 +361,10 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
|
||||
// iqpilot model params
|
||||
{"CameraOffset", {PERSISTENT, FLOAT, "0.0"}},
|
||||
// Dev gate for the Android nav stack. Off by default and only konn3kt dev-flagged devices
|
||||
// (or a developer, by hand) set it. navassist stays fully dormant unless this AND IQAndroidNav
|
||||
// are both set, so a non-dev forcing IQAndroidNav=1 does nothing — the container never starts.
|
||||
{"IQNavAssistDev", {PERSISTENT, BOOL, "0"}},
|
||||
{"IQAndroidNav", {PERSISTENT, BOOL, "0"}},
|
||||
{"IQAndroidNavStatus", {PERSISTENT, STRING}},
|
||||
// "waze" or "maps": exactly one runs, the other is disabled outright.
|
||||
|
||||
@@ -221,7 +221,6 @@ class BundleProcess(NativeProcess):
|
||||
def __init__(self, name, bundle, entry, should_run, enabled=True, sigkill=False, restart_if_crash=False):
|
||||
self.bundle = bundle
|
||||
self.entry = entry
|
||||
self.restart_if_crash = restart_if_crash
|
||||
runner_path = preferred_runner_path()
|
||||
runner_cmd = str(runner_path) if runner_path.is_absolute() else "./iqpilot_bundle_runner"
|
||||
runner_cwd = ".iqpilot/runtime_root" if runner_path.is_absolute() else "system/proprietary_runtime"
|
||||
@@ -238,6 +237,7 @@ class BundleProcess(NativeProcess):
|
||||
should_run=should_run,
|
||||
enabled=enabled,
|
||||
sigkill=sigkill,
|
||||
restart_if_crash=restart_if_crash,
|
||||
)
|
||||
|
||||
def start(self) -> None:
|
||||
|
||||
@@ -68,7 +68,7 @@ def always_run(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return True
|
||||
|
||||
def nav_assist(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return params.get_bool("IQAndroidNav")
|
||||
return params.get_bool("IQNavAssistDev") and params.get_bool("IQAndroidNav")
|
||||
|
||||
def only_onroad(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started
|
||||
|
||||
@@ -67,6 +67,11 @@ class TestManager:
|
||||
assert manager.update_modeld_watchdog(deadline, True, False, proc, 16.0) is None
|
||||
proc.restart.assert_not_called()
|
||||
|
||||
def test_bundle_process_keeps_restart_if_crash(self):
|
||||
proc = BundleProcess("test", "bundle", "entry", lambda *_: True, restart_if_crash=True)
|
||||
assert proc.restart_if_crash is True
|
||||
assert BundleProcess("test", "bundle", "entry", lambda *_: True).restart_if_crash is False
|
||||
|
||||
def test_bundle_process_stops_with_sigterm(self, mocker):
|
||||
proc = BundleProcess("test", "bundle", "entry", lambda *_: True)
|
||||
proc.proc = mocker.Mock(exitcode=None, pid=123)
|
||||
|
||||
@@ -28,105 +28,98 @@ def _speed_scale() -> float:
|
||||
return CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH
|
||||
|
||||
|
||||
_STRIP_WIDTH = 28
|
||||
_STRIP_INSET = 14
|
||||
_STRIP_CEILING = 0.85
|
||||
_STRIP_EMA = 5.0
|
||||
_STRIP_ARC_SEGMENTS = 24
|
||||
_STRIP_ACCEL = (33, 112, 115)
|
||||
_STRIP_ACCEL_NEON = (94, 232, 236)
|
||||
_STRIP_DECEL = (255, 0, 247)
|
||||
_STRIP_DECEL_NEON = (255, 145, 251)
|
||||
_STRIP_TIP_ALPHA = 235
|
||||
_STRIP_ROOT_ALPHA = 55
|
||||
_STRIP_CORE_DEPTH = 7.0
|
||||
_STRIP_CORE_LAYERS = 8
|
||||
_STRIP_CORE_ALPHA = 0.15
|
||||
_STRIP_CORE_FLOOR = 0.3
|
||||
_STRIP_CORE_TAIL = 22.0
|
||||
_STRIP_HALO_SPREAD = 11.0
|
||||
_STRIP_HALO_LAYERS = 11
|
||||
_STRIP_HALO_ALPHA = 0.095
|
||||
_STRIP_HALO_TAIL = 34.0
|
||||
_STRIP_NEON_FULL = 2.0
|
||||
_ACCEL_METER_INSET = 18.0
|
||||
_ACCEL_METER_WIDTH = 16.0
|
||||
_ACCEL_METER_BACKDROP_WIDTH = 42.0
|
||||
_ACCEL_METER_HEIGHT = 0.5
|
||||
_ACCEL_METER_MAX_HEIGHT = 520.0
|
||||
_ACCEL_METER_DEADBAND = 0.12
|
||||
_ACCEL_METER_FULL_SCALE = 3.0
|
||||
_ACCEL_METER_ATTACK_TAU = 0.08
|
||||
_ACCEL_METER_RELEASE_TAU = 0.35
|
||||
_ACCEL_METER_ACCEL_ROOT = (0, 178, 169)
|
||||
_ACCEL_METER_ACCEL_TIP = (54, 255, 232)
|
||||
_ACCEL_METER_BRAKE_ROOT = (196, 32, 139)
|
||||
_ACCEL_METER_BRAKE_TIP = (255, 86, 202)
|
||||
_ACCEL_METER_GLOW = ((8.0, 18), (5.0, 28), (2.5, 42))
|
||||
|
||||
|
||||
class IQAccelBar:
|
||||
def __init__(self):
|
||||
self._eased = 0.0
|
||||
|
||||
def _reach(self) -> float:
|
||||
mag = abs(self._eased)
|
||||
return 0.0 if mag == 0.0 else max(0.0, _STRIP_CEILING - 0.1 / mag)
|
||||
self._shown = 0.0
|
||||
self._last_frame = time.monotonic()
|
||||
|
||||
@staticmethod
|
||||
def _tint(fill, frac: float):
|
||||
return canvas.shade(*fill, int(_STRIP_TIP_ALPHA + (_STRIP_ROOT_ALPHA - _STRIP_TIP_ALPHA) * frac))
|
||||
def _normalized(acceleration: float) -> float:
|
||||
magnitude = abs(acceleration)
|
||||
if magnitude <= _ACCEL_METER_DEADBAND:
|
||||
return 0.0
|
||||
return float(np.clip(
|
||||
(magnitude - _ACCEL_METER_DEADBAND) / (_ACCEL_METER_FULL_SCALE - _ACCEL_METER_DEADBAND),
|
||||
0.0,
|
||||
1.0,
|
||||
))
|
||||
|
||||
@staticmethod
|
||||
def _halo(center, cap: float, up: bool, neon, heat: float, tail: float) -> None:
|
||||
step = int(255 * _STRIP_HALO_ALPHA * heat)
|
||||
if step < 1:
|
||||
return
|
||||
start, end = (180.0, 360.0) if up else (0.0, 180.0)
|
||||
tint = canvas.shade(*neon, step)
|
||||
faded = canvas.shade(*neon, 0)
|
||||
top, bottom = (tint, faded) if up else (faded, tint)
|
||||
for i in range(_STRIP_HALO_LAYERS):
|
||||
spread = _STRIP_HALO_SPREAD * (1.0 - i / _STRIP_HALO_LAYERS)
|
||||
if spread <= 0.0:
|
||||
continue
|
||||
canvas.annulus(center, cap, cap + spread, start, end, _STRIP_ARC_SEGMENTS, tint)
|
||||
if tail <= 0.0:
|
||||
continue
|
||||
run = min(tail, _STRIP_HALO_TAIL)
|
||||
y = center.y if up else center.y - run
|
||||
canvas.v_sweep(center.x - cap - spread, y, spread, run, top, bottom)
|
||||
canvas.v_sweep(center.x + cap, y, spread, run, top, bottom)
|
||||
def _next_value(shown: float, target: float, dt: float) -> float:
|
||||
growing = abs(target) > abs(shown) or shown * target < 0.0
|
||||
tau = _ACCEL_METER_ATTACK_TAU if growing else _ACCEL_METER_RELEASE_TAU
|
||||
alpha = 1.0 - math.exp(-max(0.0, dt) / tau)
|
||||
value = shown + (target - shown) * alpha
|
||||
return 0.0 if abs(value) < 0.005 and abs(target) <= _ACCEL_METER_DEADBAND else value
|
||||
|
||||
@staticmethod
|
||||
def _cap(center, cap: float, up: bool, fill, neon, heat: float, tail: float) -> None:
|
||||
start, end = (180.0, 360.0) if up else (0.0, 180.0)
|
||||
canvas.annulus(center, 0.0, cap, start, end, _STRIP_ARC_SEGMENTS, fill)
|
||||
IQAccelBar._halo(center, cap, up, neon, heat, tail)
|
||||
lit = _STRIP_CORE_FLOOR + (1.0 - _STRIP_CORE_FLOOR) * heat
|
||||
core = canvas.shade(*neon, max(1, int(255 * _STRIP_CORE_ALPHA * lit)))
|
||||
faded = canvas.shade(*neon, 0)
|
||||
top, bottom = (core, faded) if up else (faded, core)
|
||||
run = min(tail, _STRIP_CORE_TAIL)
|
||||
for i in range(_STRIP_CORE_LAYERS):
|
||||
depth = _STRIP_CORE_DEPTH * (1.0 - i / _STRIP_CORE_LAYERS)
|
||||
if depth <= 0.0:
|
||||
continue
|
||||
canvas.annulus(center, max(0.0, cap - depth), cap, start, end, _STRIP_ARC_SEGMENTS, core)
|
||||
if run <= 0.0:
|
||||
continue
|
||||
y = center.y if up else center.y - run
|
||||
canvas.v_sweep(center.x - cap, y, depth, run, top, bottom)
|
||||
canvas.v_sweep(center.x + cap - depth, y, depth, run, top, bottom)
|
||||
def _gradient(y: float, height: float, width: float, x: float, accelerating: bool, root, tip, alpha: int) -> None:
|
||||
root_color = canvas.shade(*root, max(1, int(alpha * 0.56)))
|
||||
tip_color = canvas.shade(*tip, alpha)
|
||||
top, bottom = (tip_color, root_color) if accelerating else (root_color, tip_color)
|
||||
canvas.v_sweep(x, y, width, height, top, bottom)
|
||||
|
||||
@staticmethod
|
||||
def _fill(x: float, y: float, width: float, height: float, accelerating: bool, root, tip) -> None:
|
||||
for spread, alpha in _ACCEL_METER_GLOW:
|
||||
IQAccelBar._gradient(y, height, width + spread * 2.0, x - spread, accelerating, root, tip, alpha)
|
||||
IQAccelBar._gradient(y, height, width, x, accelerating, root, tip, 235)
|
||||
center_x = x + width / 2.0
|
||||
tip_y = y if accelerating else y + height
|
||||
canvas.disc(center_x, tip_y, width / 2.0 + 1.0, canvas.shade(*tip, 242))
|
||||
canvas.v_sweep(x + width * 0.38, y, width * 0.24, height,
|
||||
canvas.shade(255, 255, 255, 92 if accelerating else 24),
|
||||
canvas.shade(255, 255, 255, 24 if accelerating else 92))
|
||||
|
||||
def render(self, rect, sm) -> None:
|
||||
if not ui_state.rocket_fuel:
|
||||
return
|
||||
self._eased += (sm['carState'].aEgo - self._eased) / _STRIP_EMA
|
||||
reach = self._reach() * rect.height / 2.0
|
||||
if reach <= 0.0:
|
||||
return
|
||||
accelerating = self._eased > 0.0
|
||||
mid = rect.y + rect.height / 2.0
|
||||
top = mid - reach if accelerating else mid
|
||||
x = rect.x + _STRIP_INSET
|
||||
cap = min(_STRIP_WIDTH / 2.0, reach / 2.0)
|
||||
fill, neon = (_STRIP_ACCEL, _STRIP_ACCEL_NEON) if accelerating else (_STRIP_DECEL, _STRIP_DECEL_NEON)
|
||||
near = cap / reach
|
||||
frac_top, frac_bottom = (near, 1.0 - near) if accelerating else (1.0 - near, near)
|
||||
heat = min(abs(self._eased) / _STRIP_NEON_FULL, 1.0)
|
||||
now = time.monotonic()
|
||||
dt = min(max(now - self._last_frame, 1.0 / 120.0), 0.1)
|
||||
self._last_frame = now
|
||||
target = float(np.clip(sm['carState'].aEgo, -_ACCEL_METER_FULL_SCALE, _ACCEL_METER_FULL_SCALE))
|
||||
self._shown = self._next_value(self._shown, target, dt)
|
||||
|
||||
canvas.v_sweep(x, top + cap, cap * 2.0, reach - cap * 2.0,
|
||||
self._tint(fill, frac_top), self._tint(fill, frac_bottom))
|
||||
tail = max(0.0, reach / 2.0 - cap)
|
||||
self._cap(canvas.Pt(x + cap, top + cap), cap, True, self._tint(fill, frac_top), neon, heat, tail)
|
||||
self._cap(canvas.Pt(x + cap, top + reach - cap), cap, False, self._tint(fill, frac_bottom), neon, heat, tail)
|
||||
track_height = min(rect.height * _ACCEL_METER_HEIGHT, _ACCEL_METER_MAX_HEIGHT)
|
||||
mid = rect.y + rect.height / 2.0
|
||||
track_y = mid - track_height / 2.0
|
||||
backdrop_x = rect.x + _ACCEL_METER_INSET
|
||||
track_x = backdrop_x + (_ACCEL_METER_BACKDROP_WIDTH - _ACCEL_METER_WIDTH) / 2.0
|
||||
canvas.panel(rl.Rectangle(backdrop_x, track_y - 10.0, _ACCEL_METER_BACKDROP_WIDTH, track_height + 20.0),
|
||||
0.48, 18, canvas.shade(0, 8, 13, 108))
|
||||
canvas.panel_outline(rl.Rectangle(backdrop_x, track_y - 10.0, _ACCEL_METER_BACKDROP_WIDTH, track_height + 20.0),
|
||||
0.48, 18, 1.0, canvas.shade(126, 245, 238, 34))
|
||||
canvas.panel(rl.Rectangle(track_x, track_y, _ACCEL_METER_WIDTH, track_height),
|
||||
0.5, 14, canvas.shade(210, 245, 242, 25))
|
||||
|
||||
normalized = self._normalized(self._shown)
|
||||
if normalized > 0.0:
|
||||
accelerating = self._shown > 0.0
|
||||
reach = normalized * (track_height / 2.0 - _ACCEL_METER_WIDTH / 2.0)
|
||||
fill_y = mid - reach if accelerating else mid
|
||||
root, tip = ((_ACCEL_METER_ACCEL_ROOT, _ACCEL_METER_ACCEL_TIP) if accelerating else
|
||||
(_ACCEL_METER_BRAKE_ROOT, _ACCEL_METER_BRAKE_TIP))
|
||||
self._fill(track_x, fill_y, _ACCEL_METER_WIDTH, reach, accelerating, root, tip)
|
||||
|
||||
canvas.slab(backdrop_x + 6.0, mid - 1.0, _ACCEL_METER_BACKDROP_WIDTH - 12.0, 2.0,
|
||||
canvas.shade(236, 255, 253, 205))
|
||||
canvas.disc(track_x + _ACCEL_METER_WIDTH / 2.0, mid, 3.0, canvas.shade(255, 255, 255, 238))
|
||||
|
||||
_BS_INSET = 20
|
||||
_BS_DROP = 100
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from iqpilot.ui.onroad.hud_overlays import IQAccelBar
|
||||
|
||||
|
||||
@pytest.mark.parametrize("acceleration", [-0.12, 0.0, 0.12])
|
||||
def test_acceleration_meter_deadband(acceleration):
|
||||
assert IQAccelBar._normalized(acceleration) == 0.0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("acceleration", [-4.0, -3.0, 3.0, 4.0])
|
||||
def test_acceleration_meter_clamps_full_scale(acceleration):
|
||||
assert IQAccelBar._normalized(acceleration) == 1.0
|
||||
|
||||
|
||||
def test_acceleration_meter_is_symmetric():
|
||||
assert IQAccelBar._normalized(-1.5) == IQAccelBar._normalized(1.5)
|
||||
|
||||
|
||||
def test_acceleration_meter_attack_is_faster_than_release():
|
||||
attack = IQAccelBar._next_value(0.0, 1.0, 0.05)
|
||||
release = IQAccelBar._next_value(1.0, 0.0, 0.05)
|
||||
assert attack > 1.0 - release
|
||||
|
||||
|
||||
def test_acceleration_meter_crosses_direction_smoothly():
|
||||
shown = IQAccelBar._next_value(0.8, -0.8, 0.05)
|
||||
assert -0.8 < shown < 0.8
|
||||
Reference in New Issue
Block a user