1
0
forked from IQ.Lvbs/IQ.Pilot

IQ.Pilot Release Commit @ 5bc9cd3

This commit is contained in:
IQ.Lvbs history cleanup
2026-08-22 23:42:42 -05:00
commit d037016281
4504 changed files with 1129827 additions and 0 deletions

View File

@@ -0,0 +1,186 @@
#!/usr/bin/env python3
import argparse
import os
import pathlib
import subprocess
import shutil
import sys
import time
import importlib
from openpilot.common.params import Params
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.selfdrive.test.helpers import with_processes
TEST_DIR = pathlib.Path(__file__).parent
OUTPUT_DIR = TEST_DIR / "nav_demo_report"
UI_DELAY = float(os.getenv("IQPILOT_NAV_DEMO_UI_DELAY", "0.75"))
VERSION = "0.10.1 / nav-ui-demo / 7864838 / Mar 09"
SCENE_SETTLE_S = float(os.getenv("IQPILOT_NAV_DEMO_SCENE_SETTLE_S", "0.04"))
SEED_REPEATS = int(os.getenv("IQPILOT_NAV_DEMO_SEED_REPEATS", "2"))
SEED_DELAY_S = float(os.getenv("IQPILOT_NAV_DEMO_SEED_DELAY_S", "0.02"))
SEED_REFRESH_EVERY = int(os.getenv("IQPILOT_NAV_DEMO_SEED_REFRESH_EVERY", "24"))
NAV_REPEATS = int(os.getenv("IQPILOT_NAV_DEMO_NAV_REPEATS", "2"))
NAV_DELAY_S = float(os.getenv("IQPILOT_NAV_DEMO_NAV_DELAY_S", "0.02"))
SEED_PUBLISH_DURATION_S = SEED_REPEATS * SEED_DELAY_S
NAV_SCENE_DURATION_S = NAV_REPEATS * NAV_DELAY_S
NAV_SCENES = ()
NAV_TIMELINE = ()
build_ui_pubmaster = None
publish_nav_scene = None
publish_onroad_seed = None
seed_ui_test_params = None
class NavDemoCapture:
def __init__(self, output_dir: pathlib.Path):
os.environ["SCALE"] = os.getenv("SCALE", "1")
os.environ["BIG"] = "1"
os.environ["RECORD"] = "1"
os.environ["RECORD_OUTPUT"] = str(output_dir / "nav_demo")
sys.modules["mouseinfo"] = False
self.output_dir = output_dir
self.pm = build_ui_pubmaster()
self.frames = []
self._image_lib = None
self.video_path = self.output_dir / "nav_demo.mp4"
def _load_image_lib(self):
if self._image_lib is not None:
return self._image_lib
try:
self._image_lib = importlib.import_module("PIL.Image")
except ModuleNotFoundError:
self._image_lib = None
return self._image_lib
def setup(self):
publish_onroad_seed(self.pm)
time.sleep(UI_DELAY)
@with_processes(["ui"])
def run(self):
self.setup()
for idx, scene in enumerate(NAV_TIMELINE):
if idx % SEED_REFRESH_EVERY == 0:
publish_onroad_seed(self.pm, repeats=SEED_REPEATS, delay=SEED_DELAY_S)
publish_nav_scene(self.pm, scene, repeats=NAV_REPEATS, delay=NAV_DELAY_S)
time.sleep(SCENE_SETTLE_S)
def extract_video_stills(self) -> list[pathlib.Path]:
if not self.video_path.exists():
return []
extracted = []
current_ts = UI_DELAY
for idx, scene in enumerate(NAV_TIMELINE):
if idx % SEED_REFRESH_EVERY == 0:
current_ts += SEED_PUBLISH_DURATION_S
capture_ts = current_ts + (NAV_SCENE_DURATION_S * 0.6)
capture_name = scene.get("capture_name")
if capture_name:
output_path = self.output_dir / f"{capture_name}.png"
subprocess.run(
[
"ffmpeg",
"-y",
"-loglevel", "error",
"-ss", f"{capture_ts:.2f}",
"-i", str(self.video_path),
"-frames:v", "1",
str(output_path),
],
check=True,
)
extracted.append(output_path)
current_ts += NAV_SCENE_DURATION_S + SCENE_SETTLE_S
return extracted
def write_gif(self) -> pathlib.Path | None:
if not self.frames:
for scene in NAV_SCENES:
image_path = self.output_dir / f"{scene['name']}.png"
if image_path.exists():
self.frames.append(self._load_image_lib().open(image_path).copy())
if not self.frames:
return None
if self._load_image_lib() is None:
return None
gif_path = self.output_dir / "nav_demo.gif"
self.frames[0].save(
gif_path,
save_all=True,
append_images=self.frames[1:],
duration=900,
loop=0,
)
return gif_path
def main():
parser = argparse.ArgumentParser(description="Run BIG raylib UI with a hard-coded nav route and capture screenshots/GIF.")
parser.add_argument("--output-dir", type=pathlib.Path, default=OUTPUT_DIR, help="Directory for screenshots and gif.")
parser.add_argument("--no-gif", action="store_true", help="Skip animated GIF creation.")
parser.add_argument("--mapbox-token", default="", help="Mapbox token for the demo prefix. Falls back to MAPBOX_TOKEN if omitted.")
parser.add_argument("--no-mapbox", action="store_true", help="Disable Mapbox for the demo and use only cached/offline tiles.")
parser.add_argument("--force-local-offline", action="store_true", help="Bypass both live and cached Mapbox so only the local offline provider can render.")
parser.add_argument("--offline-mbtiles", type=pathlib.Path, default=None, help="Path to a local raster MBTiles file for offline rendering.")
parser.add_argument("--offline-tile-root", type=pathlib.Path, default=None, help="Path to a local XYZ raster tile directory for offline rendering.")
parser.add_argument("--fixture", type=pathlib.Path, default=None, help="Route fixture JSON to drive the nav demo.")
parser.add_argument("--osmaps-mode", choices=("online", "offline", "both"), default=None,
help="Set the OnlineOSMaps/OfflineOSMaps params (the production source selection) for the demo prefix.")
args = parser.parse_args()
if args.output_dir.exists():
shutil.rmtree(args.output_dir)
args.output_dir.mkdir(parents=True)
mapbox_token = args.mapbox_token
if not mapbox_token and not args.no_mapbox:
existing = Params().get("MapboxToken")
if isinstance(existing, bytes):
existing = existing.decode("utf-8")
mapbox_token = existing or ""
with OpenpilotPrefix():
if args.fixture is not None:
os.environ["IQPILOT_NAV_DEMO_FIXTURE"] = str(args.fixture)
if args.force_local_offline:
os.environ["IQPILOT_DISABLE_MAPBOX_PROVIDER"] = "1"
os.environ["IQPILOT_DISABLE_MAPBOX_CACHE"] = "1"
elif args.no_mapbox:
os.environ.pop("IQPILOT_DISABLE_MAPBOX_PROVIDER", None)
os.environ.pop("IQPILOT_DISABLE_MAPBOX_CACHE", None)
if args.offline_mbtiles is not None:
os.environ["IQPILOT_OFFLINE_MBTILES"] = str(args.offline_mbtiles)
if args.offline_tile_root is not None:
os.environ["IQPILOT_OFFLINE_TILE_ROOT"] = str(args.offline_tile_root)
global NAV_SCENES, NAV_TIMELINE, build_ui_pubmaster, publish_nav_scene, publish_onroad_seed, seed_ui_test_params
nav_demo_common = importlib.import_module("openpilot.selfdrive.ui.tests.test_ui.nav_demo_common")
NAV_SCENES = nav_demo_common.NAV_SCENES
NAV_TIMELINE = nav_demo_common.NAV_TIMELINE
build_ui_pubmaster = nav_demo_common.build_ui_pubmaster
publish_nav_scene = nav_demo_common.publish_nav_scene
publish_onroad_seed = nav_demo_common.publish_onroad_seed
seed_ui_test_params = nav_demo_common.seed_ui_test_params
seed_ui_test_params(Params(), VERSION, mapbox_token=mapbox_token)
if args.osmaps_mode is not None:
demo_params = Params()
demo_params.put_bool("OnlineOSMaps", args.osmaps_mode in ("online", "both"))
demo_params.put_bool("OfflineOSMaps", args.osmaps_mode in ("offline", "both"))
demo = NavDemoCapture(args.output_dir)
demo.run()
demo.extract_video_stills()
gif_path = None if args.no_gif else demo.write_gif()
print(f"Screenshots written to: {args.output_dir}")
if demo.video_path.exists():
print(f"Recorded preview written to: {demo.video_path}")
if gif_path is not None:
print(f"Animated preview written to: {gif_path}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,705 @@
#!/usr/bin/env python3
import json
import os
import time
from math import atan2, cos, radians, sqrt
from pathlib import Path
from cereal import car, custom, log, messaging
from cereal.messaging import PubMaster
from openpilot.common.params import Params
from openpilot.system.hardware import HARDWARE
from openpilot.system.updated.updated import parse_release_notes
from openpilot.system.version import terms_version, training_version
from openpilot.common.basedir import BASEDIR
DEFAULT_DEMO_FIXTURE_PATH = Path(__file__).with_name("nav_demo_fixture_bolingbrook_mapbox.json")
DEMO_ROUTE_STEP_M_MIN = 12.0
TARGET_TIMELINE_SCENES = 180
def _load_demo_fixture() -> dict:
fixture_path = Path(os.getenv("IQPILOT_NAV_DEMO_FIXTURE", str(DEFAULT_DEMO_FIXTURE_PATH)))
return json.loads(fixture_path.read_text())
def _lerp(a: float, b: float, t: float) -> float:
return a + (b - a) * t
def _segment_length_m(a: tuple[float, float], b: tuple[float, float]) -> float:
lat_scale = 111_320.0
lon_scale = 111_320.0 * cos(radians((a[0] + b[0]) * 0.5))
dx = (b[1] - a[1]) * lon_scale
dy = (b[0] - a[0]) * lat_scale
return sqrt(dx * dx + dy * dy)
def _route_length_m(points: list[tuple[float, float]]) -> float:
return sum(_segment_length_m(points[idx], points[idx + 1]) for idx in range(len(points) - 1))
def _cumulative_distances(points: list[tuple[float, float]]) -> list[float]:
out = [0.0]
for idx in range(len(points) - 1):
out.append(out[-1] + _segment_length_m(points[idx], points[idx + 1]))
return out
def _interpolate_along(points: list[tuple[float, float]], distance_m: float) -> tuple[float, float]:
if len(points) < 2:
return points[0]
remaining = max(distance_m, 0.0)
for idx in range(len(points) - 1):
a, b = points[idx], points[idx + 1]
seg_len = _segment_length_m(a, b)
if remaining <= seg_len:
t = 0.0 if seg_len < 1e-3 else remaining / seg_len
return _lerp(a[0], b[0], t), _lerp(a[1], b[1], t)
remaining -= seg_len
return points[-1]
def _bearing_between(a: tuple[float, float], b: tuple[float, float]) -> float:
lon_scale = cos(radians((a[0] + b[0]) * 0.5))
dx = (b[1] - a[1]) * lon_scale
dy = b[0] - a[0]
return (90.0 - (180.0 / 3.141592653589793) * atan2(dy, dx)) % 360.0
def _slice_route_ahead(points: list[tuple[float, float]], current_idx: int, lookbehind: int = 1) -> list[tuple[float, float]]:
start = max(current_idx - lookbehind, 0)
return points[start:]
def _decode_polyline6(polyline: str) -> list[tuple[float, float]]:
if not polyline:
return []
points = []
index = 0
lat = 0
lon = 0
while index < len(polyline):
result = 0
shift = 0
while True:
byte = ord(polyline[index]) - 63
index += 1
result |= (byte & 0x1F) << shift
shift += 5
if byte < 0x20:
break
lat += ~(result >> 1) if result & 1 else (result >> 1)
result = 0
shift = 0
while True:
byte = ord(polyline[index]) - 63
index += 1
result |= (byte & 0x1F) << shift
shift += 5
if byte < 0x20:
break
lon += ~(result >> 1) if result & 1 else (result >> 1)
points.append((lat / 1_000_000.0, lon / 1_000_000.0))
return points
def _modifier_to_direction(modifier: str | None) -> int:
modifier = (modifier or "").lower()
if "left" in modifier:
return custom.NavDirection.left
if "right" in modifier:
return custom.NavDirection.right
return custom.NavDirection.none
def _modifier_to_turn_direction(modifier: str | None) -> int:
modifier = (modifier or "").lower()
if "left" in modifier:
return custom.IQTurnSignalDirection.turnLeft
if "right" in modifier:
return custom.IQTurnSignalDirection.turnRight
return custom.IQTurnSignalDirection.none
def _valhalla_modifier_from_type(type_code: int) -> str:
mapping = {
9: "slight_right",
10: "right",
11: "sharp_right",
12: "uturn_right",
13: "uturn_left",
14: "sharp_left",
15: "left",
16: "slight_left",
17: "straight",
18: "right",
19: "left",
20: "straight",
21: "roundabout",
22: "roundabout",
24: "right",
25: "left",
26: "straight",
27: "straight",
31: "straight",
32: "right",
33: "left",
36: "straight",
}
return mapping.get(type_code, "straight")
def _normalize_mapbox_fixture(data: dict) -> dict:
route = data["routes"][0]
leg = route["legs"][0]
points = [(lat, lon) for lon, lat in route["geometry"]["coordinates"]]
waypoint_entries = data.get("waypoints", [{}, {}])
start_waypoint = waypoint_entries[0] if waypoint_entries else {}
destination_location = data.get("waypoints", [{}, {}])[-1].get("location", route["geometry"]["coordinates"][-1])
destination = (float(destination_location[1]), float(destination_location[0]))
steps = []
previous_name = os.getenv("IQPILOT_NAV_DEMO_START_NAME", "185 Brandon Ct")
for step in leg["steps"]:
maneuver = step["maneuver"]
name = step["name"] or previous_name
if step["name"]:
previous_name = step["name"]
steps.append({
"name": name,
"banner_name": step["name"],
"location": (float(maneuver["location"][1]), float(maneuver["location"][0])),
"raw_type": maneuver.get("type", "none"),
"modifier": maneuver.get("modifier", "straight") or "straight",
"description": maneuver.get("instruction") or name or "Continue",
"distance": float(step["distance"]),
"duration": float(step["duration"]),
})
return {
"provider": "mapbox",
"route_points": points,
"duration_s": float(route["duration"]),
"distance_m": float(route["distance"]),
"steps": steps,
"start_name": os.getenv("IQPILOT_NAV_DEMO_START_NAME", start_waypoint.get("name") or "Start"),
"destination_name": os.getenv("IQPILOT_NAV_DEMO_DESTINATION_NAME", waypoint_entries[-1].get("name") or "Destination"),
"destination": destination,
}
def _normalize_valhalla_fixture(data: dict) -> dict:
trip = data["trip"]
leg = trip["legs"][0]
points = _decode_polyline6(leg["shape"])
start_loc = trip["locations"][0]
destination_loc = trip["locations"][-1]
destination = (float(destination_loc["lat"]), float(destination_loc["lon"]))
steps = []
previous_name = os.getenv("IQPILOT_NAV_DEMO_START_NAME", "185 Brandon Ct")
for maneuver in leg["maneuvers"]:
type_code = int(maneuver.get("type", 8) or 8)
modifier = _valhalla_modifier_from_type(type_code)
begin_idx = min(max(int(maneuver.get("begin_shape_index", 0) or 0), 0), len(points) - 1)
street_names = maneuver.get("street_names") or []
banner_name = street_names[0] if street_names else ""
name = banner_name or previous_name
if banner_name:
previous_name = banner_name
steps.append({
"name": name,
"banner_name": banner_name,
"location": points[begin_idx],
"raw_type": f"valhalla:{type_code}",
"modifier": modifier,
"description": maneuver.get("instruction") or name or "Continue",
"distance": float(maneuver.get("length", 0.0) or 0.0) * 1000.0,
"duration": float(maneuver.get("time", 0.0) or 0.0),
"type_code": type_code,
})
return {
"provider": "valhalla",
"route_points": points,
"duration_s": float(trip["summary"]["time"]),
"distance_m": float(trip["summary"]["length"]) * 1000.0,
"steps": steps,
"start_name": os.getenv("IQPILOT_NAV_DEMO_START_NAME", start_loc.get("name") or "Start"),
"destination_name": os.getenv("IQPILOT_NAV_DEMO_DESTINATION_NAME", destination_loc.get("name") or "Destination"),
"destination": destination,
}
def _normalize_demo_fixture(data: dict) -> dict:
if "routes" in data:
return _normalize_mapbox_fixture(data)
if "trip" in data:
return _normalize_valhalla_fixture(data)
raise ValueError("Unsupported nav demo fixture format")
def _map_step_type(step_type: str, modifier: str | None, type_code: int | None = None) -> int:
if type_code is not None:
if type_code in {4, 5, 6}:
return custom.IQNavState.ManeuverType.arrive
if type_code in {21, 22, 23, 37}:
return custom.IQNavState.ManeuverType.roundabout
if type_code in {18, 19, 24, 25}:
return custom.IQNavState.ManeuverType.exit
if type_code in {17, 20, 26, 27}:
return custom.IQNavState.ManeuverType.merge
if type_code in {1, 2, 3, 7, 8, 31, 36}:
return custom.IQNavState.ManeuverType.continueStraight
return custom.IQNavState.ManeuverType.turn
step_type = step_type or "none"
modifier = (modifier or "").lower()
if step_type == "arrive":
return custom.IQNavState.ManeuverType.arrive
if step_type in {"off ramp"}:
return custom.IQNavState.ManeuverType.exit
if step_type in {"merge", "on ramp"}:
return custom.IQNavState.ManeuverType.merge
if step_type in {"fork"}:
return custom.IQNavState.ManeuverType.fork
if step_type in {"roundabout", "rotary", "roundabout turn"}:
return custom.IQNavState.ManeuverType.roundabout
if step_type in {"continue", "new name", "depart", "notification"}:
return custom.IQNavState.ManeuverType.continueStraight
if step_type in {"turn", "end of road"}:
return custom.IQNavState.ManeuverType.turn
if modifier == "straight":
return custom.IQNavState.ManeuverType.continueStraight
return custom.IQNavState.ManeuverType.turn
def _nearest_route_index(points: list[tuple[float, float]], target: tuple[float, float]) -> int:
best_idx = 0
best_distance = None
for idx, point in enumerate(points):
d = _segment_length_m(point, target)
if best_distance is None or d < best_distance:
best_distance = d
best_idx = idx
return best_idx
DEMO_FIXTURE = _load_demo_fixture()
DEMO_CONTEXT = _normalize_demo_fixture(DEMO_FIXTURE)
DEMO_START_NAME = DEMO_CONTEXT["start_name"]
DEMO_DESTINATION_NAME = DEMO_CONTEXT["destination_name"]
DEMO_DESTINATION = DEMO_CONTEXT["destination"]
DEMO_ROUTE_POINTS = DEMO_CONTEXT["route_points"]
ROUTE_DISTANCES = _cumulative_distances(DEMO_ROUTE_POINTS)
ROUTE_TOTAL_DISTANCE_M = ROUTE_DISTANCES[-1]
DEMO_DURATION_S = float(DEMO_CONTEXT["duration_s"])
DEMO_ROUTE_STEP_M = max(DEMO_ROUTE_STEP_M_MIN, ROUTE_TOTAL_DISTANCE_M / TARGET_TIMELINE_SCENES)
DEMO_STEPS = []
previous_name = DEMO_START_NAME
for idx, step in enumerate(DEMO_CONTEXT["steps"]):
maneuver_location = step["location"]
route_index = _nearest_route_index(DEMO_ROUTE_POINTS, maneuver_location)
route_distance = ROUTE_DISTANCES[route_index]
step_name = step["name"] or previous_name
if step["name"]:
previous_name = step["name"]
step_type = _map_step_type(step.get("raw_type", "none"), step.get("modifier"), step.get("type_code"))
direction = _modifier_to_direction(step.get("modifier"))
step_speed = max(float(step["distance"]) / max(float(step["duration"]), 1.0), 3.5)
DEMO_STEPS.append({
"index": idx,
"name": step_name,
"banner_name": step.get("banner_name", ""),
"route_index": route_index,
"route_distance": route_distance,
"type": step_type,
"raw_type": step.get("raw_type", "none"),
"modifier": step.get("modifier", "straight") or "straight",
"direction": direction,
"description": step.get("description") or step_name or "Continue",
"distance": float(step["distance"]),
"duration": float(step["duration"]),
"speed": step_speed,
"location": maneuver_location,
})
DEMO_NAV_STEPS = tuple(step for step in DEMO_STEPS if step["raw_type"] not in {"depart", "valhalla:1"})
DEMO_DESTINATION_ROUTE_POINT = DEMO_STEPS[-1]["location"]
def _find_route_index_for_distance(distance_m: float) -> int:
for idx, route_distance in enumerate(ROUTE_DISTANCES):
if route_distance >= distance_m:
return idx
return len(ROUTE_DISTANCES) - 1
def _find_upcoming_steps(distance_m: float) -> tuple[dict | None, dict | None]:
upcoming = [step for step in DEMO_NAV_STEPS if step["route_distance"] > distance_m + 1e-3]
first = upcoming[0] if upcoming else None
second = upcoming[1] if len(upcoming) > 1 else None
return first, second
def _current_road_name(distance_m: float) -> str:
current = DEMO_START_NAME
for step in DEMO_STEPS:
if step["route_distance"] <= distance_m and step["banner_name"]:
current = step["banner_name"]
return current
def _phase_for_step(step: dict | None, distance_to_next: float) -> int:
if step is None or step["type"] == custom.IQNavState.ManeuverType.arrive:
return custom.IQNavState.ManeuverPhase.none
if step["type"] in (custom.IQNavState.ManeuverType.exit, custom.IQNavState.ManeuverType.merge, custom.IQNavState.ManeuverType.fork):
if distance_to_next <= 90.0:
return custom.IQNavState.ManeuverPhase.highwayCommit
if distance_to_next <= 260.0:
return custom.IQNavState.ManeuverPhase.highwayPrepare
return custom.IQNavState.ManeuverPhase.none
if distance_to_next <= 45.0:
return custom.IQNavState.ManeuverPhase.turnActive
if distance_to_next <= 180.0:
return custom.IQNavState.ManeuverPhase.turnPrepare
return custom.IQNavState.ManeuverPhase.none
def _zoom_for_distance(distance_to_next: float, maneuver_type: int) -> float:
if maneuver_type == custom.IQNavState.ManeuverType.arrive:
return 17.2
if maneuver_type in (custom.IQNavState.ManeuverType.exit, custom.IQNavState.ManeuverType.merge, custom.IQNavState.ManeuverType.fork):
if distance_to_next <= 120.0:
return 16.9
return 16.3
if distance_to_next <= 70.0:
return 17.1
if distance_to_next <= 160.0:
return 16.8
return 16.4
def _speed_target_for_step(current_step: dict | None, next_step: dict | None, distance_to_next: float) -> float:
current_speed = current_step["speed"] if current_step is not None else 12.0
next_speed = next_step["speed"] if next_step is not None else current_speed
if next_step is not None and next_step["type"] == custom.IQNavState.ManeuverType.arrive:
if distance_to_next <= 25.0:
return 3.0
if distance_to_next <= 80.0:
return 4.5
if distance_to_next <= 35.0:
return max(next_speed * 0.85, 4.5)
if distance_to_next <= 140.0:
return max(min(current_speed, next_speed + 1.5), 6.0)
return max(current_speed, 7.0)
def _capture_distance_targets() -> dict[str, float]:
captures = {}
selected_steps = [step for step in DEMO_NAV_STEPS if step["type"] != custom.IQNavState.ManeuverType.continueStraight]
for idx, step in enumerate(selected_steps[:4], start=1):
captures[f"nav_step_{idx:02d}"] = max(step["route_distance"] - min(120.0, max(step["distance"] * 0.35, 45.0)), 0.0)
captures["nav_arrival"] = max(DEMO_NAV_STEPS[-1]["route_distance"] - 35.0, 0.0)
return captures
def _timeline_distances() -> list[float]:
base = [idx * DEMO_ROUTE_STEP_M for idx in range(int(ROUTE_TOTAL_DISTANCE_M // DEMO_ROUTE_STEP_M) + 1)]
points = set(base)
for step in DEMO_NAV_STEPS:
for offset in (260.0, 180.0, 120.0, 80.0, 50.0, 25.0):
if step["type"] == custom.IQNavState.ManeuverType.arrive and offset > 120.0:
continue
points.add(max(step["route_distance"] - offset, 0.0))
points.add(ROUTE_TOTAL_DISTANCE_M - 15.0)
points.add(ROUTE_TOTAL_DISTANCE_M - 5.0)
return sorted(d for d in points if 0.0 <= d <= ROUTE_TOTAL_DISTANCE_M)
def _make_timeline_scene(distance_m: float) -> dict:
current_lat, current_lon = _interpolate_along(DEMO_ROUTE_POINTS, distance_m)
next_lat, next_lon = _interpolate_along(DEMO_ROUTE_POINTS, min(distance_m + 18.0, ROUTE_TOTAL_DISTANCE_M))
route_idx = _find_route_index_for_distance(distance_m)
bearing = _bearing_between((current_lat, current_lon), (next_lat, next_lon))
current_step_idx = max(0, max((idx for idx, step in enumerate(DEMO_STEPS) if step["route_distance"] <= distance_m), default=0))
current_step = DEMO_STEPS[current_step_idx]
next_step, second_step = _find_upcoming_steps(distance_m)
if next_step is None:
next_step = DEMO_NAV_STEPS[-1]
next_distance = max(next_step["route_distance"] - distance_m, 0.0)
remaining_distance = max(ROUTE_TOTAL_DISTANCE_M - distance_m, 0.0)
remaining_time = max(DEMO_DURATION_S * (remaining_distance / max(ROUTE_TOTAL_DISTANCE_M, 1.0)), 10.0)
scene = {
"name": f"nav_timeline_{int(distance_m):04d}",
"capture_name": "",
"road_name": _current_road_name(distance_m),
"distance_m": next_distance,
"time_remaining": remaining_time,
"distance_remaining": remaining_distance,
"speed_limit": max(current_step["speed"], 8.0),
"speed_limit_ahead": max(next_step["speed"], 6.0),
"speed_limit_ahead_distance": max(min(next_distance, 220.0), 0.0),
"phase": _phase_for_step(next_step, next_distance),
"direction": next_step["direction"],
"next_type": next_step["type"],
"next_modifier": next_step["modifier"],
"next_description": next_step["description"],
"second_type": second_step["type"] if second_step is not None else custom.IQNavState.ManeuverType.arrive,
"second_direction": second_step["direction"] if second_step is not None else custom.NavDirection.none,
"second_modifier": second_step["modifier"] if second_step is not None else "straight",
"second_distance": max(second_step["route_distance"] - distance_m, 0.0) if second_step is not None else 0.0,
"second_valid": second_step is not None,
"provider": custom.IQNavState.LongitudinalProvider.route,
"route_speed_target": _speed_target_for_step(current_step, next_step, next_distance),
"current_latitude": current_lat,
"current_longitude": current_lon,
"bearing_deg": bearing,
"zoom_hint": _zoom_for_distance(next_distance, next_step["type"]),
"destination_latitude": DEMO_DESTINATION[0],
"destination_longitude": DEMO_DESTINATION[1],
"destination_name": DEMO_DESTINATION_NAME,
"route_points": _slice_route_ahead(DEMO_ROUTE_POINTS, route_idx, lookbehind=1),
"next_maneuver_latitude": next_step["location"][0],
"next_maneuver_longitude": next_step["location"][1],
}
return scene
CAPTURE_TARGETS = _capture_distance_targets()
_timeline_scenes = [_make_timeline_scene(distance_m) for distance_m in _timeline_distances()]
for capture_name, target_distance in CAPTURE_TARGETS.items():
best_scene = min(
_timeline_scenes,
key=lambda scene: abs((ROUTE_TOTAL_DISTANCE_M - scene["distance_remaining"]) - target_distance),
)
best_scene["capture_name"] = capture_name
NAV_TIMELINE = tuple(_timeline_scenes)
NAV_SCENES = tuple(scene for scene in NAV_TIMELINE if scene.get("capture_name"))
def seed_ui_test_params(params: Params, version: str, mapbox_token: str = "") -> None:
params.put("DongleId", "123456789012345")
params.put("UpdaterCurrentDescription", version)
params.put("UpdaterNewDescription", version)
params.put("UpdaterCurrentReleaseNotes", parse_release_notes(BASEDIR))
params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR))
params.put("HasAcceptedTerms", terms_version)
params.put("CompletedTrainingVersion", training_version)
params.put_bool("OnScreenNavigation", True)
cp = car.CarParams(notCar=True, wheelbase=2.7, steerRatio=15.0)
cp.openpilotLongitudinalControl = True
cp_bytes = cp.to_bytes()
params.put("CarParamsPersistent", cp_bytes)
params.put("CarParams", cp_bytes)
token = mapbox_token or os.getenv("MAPBOX_TOKEN", "")
if token:
params.put("MapboxToken", token)
def build_ui_pubmaster() -> PubMaster:
return PubMaster([
"deviceState",
"pandaStates",
"driverStateV2",
"selfdriveState",
"carState",
"carControl",
"controlsState",
"iqPlan",
"iqLiveData",
"iqNavState",
"iqNavRenderState",
"gpsLocationExternal",
])
def publish_onroad_seed(pm: PubMaster, repeats: int = 8, delay: float = 0.05) -> None:
device_state = messaging.new_message("deviceState")
device_state.deviceState.started = True
device_state.deviceState.networkType = log.DeviceState.NetworkType.wifi
device_state.deviceState.deviceType = HARDWARE.get_device_type()
panda_states = messaging.new_message("pandaStates", 1)
panda_states.pandaStates[0].pandaType = log.PandaState.PandaType.dos
panda_states.pandaStates[0].ignitionLine = True
driver_state = messaging.new_message("driverStateV2")
driver_state.driverStateV2.leftDriverData.faceOrientation = [0.0, 0.0, 0.0]
selfdrive_state = messaging.new_message("selfdriveState")
selfdrive_state.selfdriveState.enabled = True
selfdrive_state.selfdriveState.state = log.SelfdriveState.OpenpilotState.enabled
car_state = messaging.new_message("carState")
car_state.carState.vEgo = 22.0
car_state.carState.aEgo = -0.2
car_state.carState.vCruise = 72.0
car_state.carState.vCruiseCluster = 72.0
car_control = messaging.new_message("carControl")
car_control.carControl.enabled = True
car_control.carControl.latActive = True
car_control.carControl.cruiseControl.override = False
controls_state = messaging.new_message("controlsState")
controls_state.controlsState.vCruiseDEPRECATED = 72.0
controls_state.controlsState.vCruiseClusterDEPRECATED = 72.0
controls_state.controlsState.curvature = 0.0
gps = messaging.new_message("gpsLocationExternal")
gps.gpsLocationExternal.flags = 1
gps.gpsLocationExternal.hasFix = True
gps.gpsLocationExternal.verticalAccuracy = 1.0
gps.gpsLocationExternal.speedAccuracy = 0.5
gps.gpsLocationExternal.bearingAccuracyDeg = 1.0
gps.gpsLocationExternal.vNED = [0.0, 0.0, 0.0]
gps.gpsLocationExternal.latitude = DEMO_ROUTE_POINTS[0][0]
gps.gpsLocationExternal.longitude = DEMO_ROUTE_POINTS[0][1]
gps.gpsLocationExternal.altitude = 181.0
gps.gpsLocationExternal.speed = 22.0
gps.gpsLocationExternal.bearingDeg = _bearing_between(DEMO_ROUTE_POINTS[0], DEMO_ROUTE_POINTS[1])
gps.gpsLocationExternal.unixTimestampMillis = int(time.time() * 1000)
for _ in range(repeats):
pm.send("deviceState", device_state)
pm.send("pandaStates", panda_states)
pm.send("driverStateV2", driver_state)
pm.send("selfdriveState", selfdrive_state)
pm.send("carState", car_state)
pm.send("carControl", car_control)
pm.send("controlsState", controls_state)
pm.send("gpsLocationExternal", gps)
time.sleep(delay)
def publish_nav_scene(pm: PubMaster, scene: dict, repeats: int = 6, delay: float = 0.05) -> None:
iq_plan = messaging.new_message("iqPlan")
iq_plan.iqPlan.longitudinalPlanSource = custom.IQPlan.LongitudinalPlanSource.nav
iq_plan.iqPlan.vTarget = float(scene["route_speed_target"])
iq_plan.iqPlan.aTarget = -0.7
resolver = iq_plan.iqPlan.speedLimit.resolver
resolver.speedLimit = float(scene["speed_limit"])
resolver.speedLimitLast = float(scene["speed_limit"])
resolver.speedLimitFinal = float(scene["speed_limit"])
resolver.speedLimitFinalLast = float(scene["speed_limit"])
resolver.speedLimitValid = True
resolver.speedLimitLastValid = True
resolver.speedLimitOffset = 0.0
resolver.distToSpeedLimit = 0.0
resolver.source = custom.IQPlan.SpeedLimit.Source.map
assist = iq_plan.iqPlan.speedLimit.assist
assist.enabled = False
assist.active = False
assist.state = custom.IQPlan.SpeedLimit.AssistState.disabled
assist.vTarget = 255.0
assist.aTarget = 0.0
nav_summary = iq_plan.iqPlan.iqNavState.nav
nav_summary.engaged = True
nav_summary.provider = scene["provider"]
nav_summary.state = custom.IQNavState.LongitudinalState.active
nav_summary.speedTarget = float(scene["route_speed_target"])
nav_summary.accelTarget = -0.7
nav_summary.valid = True
iq_live_data = messaging.new_message("iqLiveData")
iq_live_data.iqLiveData.speedLimitValid = True
iq_live_data.iqLiveData.speedLimit = float(scene["speed_limit"])
iq_live_data.iqLiveData.speedLimitAheadValid = True
iq_live_data.iqLiveData.speedLimitAhead = float(scene["speed_limit_ahead"])
iq_live_data.iqLiveData.speedLimitAheadDistance = float(scene["speed_limit_ahead_distance"])
iq_live_data.iqLiveData.roadName = scene["road_name"]
iq_nav_state = messaging.new_message("iqNavState")
nav_state = iq_nav_state.iqNavState
nav_state.active = True
nav_state.destinationValid = True
nav_state.destinationLatitude = float(scene["destination_latitude"])
nav_state.destinationLongitude = float(scene["destination_longitude"])
nav_state.destinationName = scene.get("destination_name", "Navigation destination")
nav_state.distanceRemaining = float(scene["distance_remaining"])
nav_state.timeRemaining = float(scene["time_remaining"])
nav_state.nextManeuverValid = True
nav_state.nextManeuverDistance = float(scene["distance_m"])
nav_state.nextManeuverType = scene["next_type"]
nav_state.nextManeuverDirection = scene["direction"]
nav_state.nextManeuverModifier = scene["next_modifier"]
nav_state.nextManeuverDescription = scene["next_description"]
nav_state.secondNextManeuverValid = scene.get("second_valid", True)
nav_state.secondNextManeuverType = scene["second_type"]
nav_state.secondNextManeuverDirection = scene["second_direction"]
nav_state.secondNextManeuverDistance = float(scene["second_distance"])
nav_state.secondNextManeuverModifier = scene.get("second_modifier", "")
nav_state.longitudinalProvider = scene["provider"]
nav_state.longitudinalState = custom.IQNavState.LongitudinalState.active
nav_state.longitudinalEngaged = True
nav_state.speedTarget = float(scene["route_speed_target"])
nav_state.accelTarget = -0.7
nav_state.valid = True
nav_state.targetSpeed = float(scene["route_speed_target"])
nav_state.targetSpeedValid = True
nav_state.maneuverPhase = scene["phase"]
nav_state.maneuverDirection = scene["direction"]
nav_state.navSpeedTargetActive = True
if scene["phase"] in (custom.IQNavState.ManeuverPhase.highwayPrepare, custom.IQNavState.ManeuverPhase.highwayCommit):
nav_state.shouldSendLanePositioning = True
nav_state.lanePositioningDirection = custom.IQTurnSignalDirection.turnRight if scene["direction"] == custom.NavDirection.right else custom.IQTurnSignalDirection.turnLeft
nav_state.command = custom.IQNavState.Command.laneChange
nav_state.commandDirection = scene["direction"]
nav_state.commandIndex = 1
elif scene["phase"] in (custom.IQNavState.ManeuverPhase.turnPrepare, custom.IQNavState.ManeuverPhase.turnActive):
nav_state.shouldSendTurnDesire = True
nav_state.turnDesireDirection = custom.IQTurnSignalDirection.turnLeft if scene["direction"] == custom.NavDirection.left else custom.IQTurnSignalDirection.turnRight
iq_nav_render = messaging.new_message("iqNavRenderState")
render_state = iq_nav_render.iqNavRenderState
render_state.active = True
render_state.currentLatitude = float(scene["current_latitude"])
render_state.currentLongitude = float(scene["current_longitude"])
render_state.bearingDeg = float(scene["bearing_deg"])
render_state.zoomHint = float(scene["zoom_hint"])
route_points = scene["route_points"]
render_state.init("routePolyline", len(route_points))
render_state.init("routePolylineSimplified", len(route_points))
for idx, (lat, lon) in enumerate(route_points):
render_state.routePolyline[idx].latitude = lat
render_state.routePolyline[idx].longitude = lon
render_state.routePolylineSimplified[idx].latitude = lat
render_state.routePolylineSimplified[idx].longitude = lon
render_state.nextManeuverLatitude = float(scene["next_maneuver_latitude"])
render_state.nextManeuverLongitude = float(scene["next_maneuver_longitude"])
render_state.nextManeuverType = scene["next_type"]
render_state.nextManeuverDirection = scene["direction"]
render_state.nextManeuverDistance = float(scene["distance_m"])
render_state.destinationLatitude = float(scene["destination_latitude"])
render_state.destinationLongitude = float(scene["destination_longitude"])
gps = messaging.new_message("gpsLocationExternal")
gps.gpsLocationExternal.flags = 1
gps.gpsLocationExternal.hasFix = True
gps.gpsLocationExternal.verticalAccuracy = 1.0
gps.gpsLocationExternal.speedAccuracy = 0.5
gps.gpsLocationExternal.bearingAccuracyDeg = 1.0
gps.gpsLocationExternal.vNED = [0.0, 0.0, 0.0]
gps.gpsLocationExternal.latitude = float(scene["current_latitude"])
gps.gpsLocationExternal.longitude = float(scene["current_longitude"])
gps.gpsLocationExternal.altitude = 181.0
gps.gpsLocationExternal.speed = max(float(scene["route_speed_target"]), 4.5)
gps.gpsLocationExternal.bearingDeg = float(scene["bearing_deg"])
gps.gpsLocationExternal.unixTimestampMillis = int(time.time() * 1000)
for _ in range(repeats):
pm.send("iqPlan", iq_plan)
pm.send("iqLiveData", iq_live_data)
pm.send("iqNavState", iq_nav_state)
pm.send("iqNavRenderState", iq_nav_render)
pm.send("gpsLocationExternal", gps)
time.sleep(delay)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,404 @@
{
"trip": {
"locations": [
{
"type": "break",
"lat": 41.690091,
"lon": -88.078806,
"name": "185 Brandon Ct, Bolingbrook, IL 60440",
"side_of_street": "left",
"original_index": 0
},
{
"type": "break",
"lat": 41.908129,
"lon": -88.115063,
"name": "505 E North Ave, Carol Stream, IL 60188",
"original_index": 1
}
],
"legs": [
{
"maneuvers": [
{
"type": 3,
"instruction": "Drive east on Cinnamon Court.",
"verbal_succinct_transition_instruction": "Drive east. Then, in 300 feet, Turn left to stay on Cinnamon Court.",
"verbal_pre_transition_instruction": "Drive east on Cinnamon Court. Then, in 300 feet, Turn left to stay on Cinnamon Court.",
"verbal_post_transition_instruction": "Continue for 300 feet.",
"street_names": [
"Cinnamon Court"
],
"bearing_after": 86,
"time": 9.98,
"length": 0.0515,
"cost": 15.202,
"begin_shape_index": 0,
"end_shape_index": 2,
"verbal_multi_cue": true,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left to stay on Cinnamon Court.",
"verbal_transition_alert_instruction": "Turn left to stay on Cinnamon Court.",
"verbal_succinct_transition_instruction": "Turn left.",
"verbal_pre_transition_instruction": "Turn left to stay on Cinnamon Court.",
"verbal_post_transition_instruction": "Continue for 400 feet.",
"street_names": [
"Cinnamon Court"
],
"bearing_before": 89,
"bearing_after": 354,
"time": 17.532,
"length": 0.0764,
"cost": 24.474,
"begin_shape_index": 2,
"end_shape_index": 5,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 10,
"instruction": "Turn right onto Lily Cache Lane.",
"verbal_transition_alert_instruction": "Turn right onto Lily Cache Lane.",
"verbal_succinct_transition_instruction": "Turn right.",
"verbal_pre_transition_instruction": "Turn right onto Lily Cache Lane.",
"verbal_post_transition_instruction": "Continue for a half mile.",
"street_names": [
"Lily Cache Lane"
],
"bearing_before": 356,
"bearing_after": 88,
"time": 62.436,
"length": 0.5014,
"cost": 151.84,
"begin_shape_index": 5,
"end_shape_index": 18,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 10,
"instruction": "Turn right onto South Bolingbrook Drive/IL 53.",
"verbal_transition_alert_instruction": "Turn right onto South Bolingbrook Drive.",
"verbal_succinct_transition_instruction": "Turn right.",
"verbal_pre_transition_instruction": "Turn right onto South Bolingbrook Drive, IL 53.",
"verbal_post_transition_instruction": "Continue for a half mile.",
"street_names": [
"South Bolingbrook Drive",
"IL 53"
],
"bearing_before": 89,
"bearing_after": 179,
"time": 52.006,
"length": 0.4473,
"cost": 129.587,
"begin_shape_index": 18,
"end_shape_index": 34,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 19,
"instruction": "Turn left to take the I 55 North ramp toward Chicago.",
"verbal_transition_alert_instruction": "Turn left to take the I 55 North ramp.",
"verbal_pre_transition_instruction": "Turn left to take the I 55 North ramp toward Chicago.",
"verbal_post_transition_instruction": "Continue for 1.5 miles.",
"street_names": [
"I 55 North",
"Adlai Stevenson Expressway"
],
"bearing_before": 177,
"bearing_after": 66,
"time": 99.799,
"length": 1.4676,
"cost": 180.0,
"begin_shape_index": 34,
"end_shape_index": 55,
"highway": true,
"sign": {
"exit_branch_elements": [
{
"text": "I 55 North",
"consecutive_count": 1
}
],
"exit_toward_elements": [
{
"text": "Chicago"
}
]
},
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 20,
"instruction": "Take exit 269 on the right onto I 355 Toll toward Northwest Suburbs.",
"verbal_transition_alert_instruction": "Take exit 269 on the right.",
"verbal_pre_transition_instruction": "Take exit 269 on the right onto I 355 Toll toward Northwest Suburbs.",
"bearing_before": 55,
"bearing_after": 60,
"time": 56.476,
"length": 0.5294,
"cost": 60.572,
"begin_shape_index": 55,
"end_shape_index": 63,
"toll": true,
"sign": {
"exit_number_elements": [
{
"text": "269"
}
],
"exit_branch_elements": [
{
"text": "I 355 Toll"
}
],
"exit_toward_elements": [
{
"text": "Northwest Suburbs",
"consecutive_count": 1
},
{
"text": "Southwest Suburbs"
}
]
},
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 24,
"instruction": "Keep left to take I 355 North toward Northwest Suburbs.",
"verbal_transition_alert_instruction": "Keep left to take I 355 North.",
"verbal_pre_transition_instruction": "Keep left to take I 355 North toward Northwest Suburbs.",
"verbal_post_transition_instruction": "Continue for 16 miles.",
"street_names": [
"I 355 North",
"Veterans Memorial Tollway"
],
"bearing_before": 58,
"bearing_after": 58,
"time": 940.064,
"length": 15.5504,
"cost": 1049.088,
"begin_shape_index": 63,
"end_shape_index": 353,
"toll": true,
"highway": true,
"sign": {
"exit_branch_elements": [
{
"text": "I 355 North",
"consecutive_count": 1
}
],
"exit_toward_elements": [
{
"text": "Northwest Suburbs",
"consecutive_count": 1
}
]
},
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 20,
"instruction": "Take exit 27 on the right onto IL 64 toward North Avenue.",
"verbal_transition_alert_instruction": "Take exit 27 on the right.",
"verbal_pre_transition_instruction": "Take exit 27 on the right onto IL 64 toward North Avenue.",
"bearing_before": 358,
"bearing_after": 3,
"time": 27.824,
"length": 0.4268,
"cost": 29.178,
"begin_shape_index": 353,
"end_shape_index": 365,
"toll": true,
"sign": {
"exit_number_elements": [
{
"text": "27"
}
],
"exit_branch_elements": [
{
"text": "IL 64",
"consecutive_count": 2
}
],
"exit_toward_elements": [
{
"text": "North Avenue"
}
]
},
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 24,
"instruction": "Keep left to take IL 64 toward Glendale Heights/Carol Stream.",
"verbal_transition_alert_instruction": "Keep left to take IL 64.",
"verbal_pre_transition_instruction": "Keep left to take IL 64 toward Glendale Heights, Carol Stream. Then Turn left onto North Avenue.",
"bearing_before": 9,
"bearing_after": 1,
"time": 5.008,
"length": 0.0813,
"cost": 24.782,
"begin_shape_index": 365,
"end_shape_index": 369,
"toll": true,
"sign": {
"exit_branch_elements": [
{
"text": "IL 64",
"consecutive_count": 2
}
],
"exit_toward_elements": [
{
"text": "Glendale Heights"
},
{
"text": "Carol Stream"
}
]
},
"verbal_multi_cue": true,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left onto North Avenue/IL 64.",
"verbal_transition_alert_instruction": "Turn left onto North Avenue.",
"verbal_succinct_transition_instruction": "Turn left.",
"verbal_pre_transition_instruction": "Turn left onto North Avenue, IL 64.",
"verbal_post_transition_instruction": "Continue for 3 miles.",
"street_names": [
"North Avenue",
"IL 64"
],
"bearing_before": 13,
"bearing_after": 266,
"time": 287.201,
"length": 3.3088,
"cost": 330.452,
"begin_shape_index": 369,
"end_shape_index": 476,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 10,
"instruction": "Turn right onto North Schmale Road.",
"verbal_transition_alert_instruction": "Turn right onto North Schmale Road.",
"verbal_succinct_transition_instruction": "Turn right.",
"verbal_pre_transition_instruction": "Turn right onto North Schmale Road.",
"verbal_post_transition_instruction": "Continue for a half mile.",
"street_names": [
"North Schmale Road"
],
"bearing_before": 268,
"bearing_after": 2,
"time": 46.619,
"length": 0.5163,
"cost": 104.948,
"begin_shape_index": 476,
"end_shape_index": 507,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left onto Kehoe Boulevard.",
"verbal_transition_alert_instruction": "Turn left onto Kehoe Boulevard.",
"verbal_succinct_transition_instruction": "Turn left.",
"verbal_pre_transition_instruction": "Turn left onto Kehoe Boulevard.",
"verbal_post_transition_instruction": "Continue for a half mile.",
"street_names": [
"Kehoe Boulevard"
],
"bearing_before": 0,
"bearing_after": 270,
"time": 146.003,
"length": 0.7431,
"cost": 260.047,
"begin_shape_index": 507,
"end_shape_index": 533,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left.",
"verbal_transition_alert_instruction": "Turn left.",
"verbal_succinct_transition_instruction": "Turn left.",
"verbal_pre_transition_instruction": "Turn left.",
"verbal_post_transition_instruction": "Continue for 400 feet.",
"bearing_before": 269,
"bearing_after": 180,
"time": 21.653,
"length": 0.0677,
"cost": 113.812,
"begin_shape_index": 533,
"end_shape_index": 538,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 4,
"instruction": "You have arrived at 505 E North Ave, Carol Stream, IL 60188.",
"verbal_transition_alert_instruction": "You will arrive at 505 E North Ave, Carol Stream, IL 60188.",
"verbal_pre_transition_instruction": "You have arrived at 505 E North Ave, Carol Stream, IL 60188.",
"bearing_before": 150,
"time": 0.0,
"length": 0.0,
"cost": 0.0,
"begin_shape_index": 538,
"end_shape_index": 538,
"travel_mode": "drive",
"travel_type": "car"
}
],
"summary": {
"has_time_restrictions": false,
"has_toll": true,
"has_highway": true,
"has_ferry": false,
"min_lat": 41.684661,
"min_lon": -88.115648,
"max_lat": 41.909558,
"max_lon": -88.027976,
"time": 1772.608,
"length": 23.7686,
"cost": 2473.988
},
"shape": "mpponAb{{~fDe@}TWog@aRnAwh@jBwFT]ie@c@cj@]k^c@ae@]qa@q@u|@Yql@Sud@OcZnA_L_@efA[u{@o@ueBde@c@dKKjJQpq@mAjS_@nS_@vl@o@rEGpOO~SaAja@cArf@YjPK`GC`IQtr@aBv@mE[gIuBsIiD_PoIiWgJcSsGuJmMwPaPuP_WyWkYoh@_RiXwVil@{p@g~Agj@orAeZis@wqAo`DagCqdGsn@{zA{dBiiEmqBowE}z@{gCal@{cBm[c}@oq@}lBaO}b@wJwX_Reh@qOwb@imAugDcv@gtBkk@qgBqTue@sFcJcE{FgE{EgJkIwGaFaHqDgGiCkHqBuFiAcG_A}Ge@_FS_HDiFN}JhAoHdBwJfCiJbEkIhFoKpIs`@r^ex@x{@w^f^cTpP{GbE}OxIkLlEaRbEs]xEyn@jJuiBpUct@bNa}B~WogCbYqeCjXojAzMe~BbPofBbHm\\rAqiHnGkzCd@ymDzIimDzAafBbC}m@f@kdH~G_dCtAccHzIyrAzAiaAvA_iBlAqjAZ}fAdCmpAlKefA|ZmmAre@asA`{@spDpiDiS~Rii@~h@saCv|BgxCdtCk}@`{@yrA~nAkRrRwY|YoaA`~@mrCzlCa^n[qb@hZke@rZ_^zQ}IxFwQ|Io^hOil@`T{gAx\\mz@~SyfBp]kdA~Mwq@hImp@hEazAjIwr@dBkdBdCcJJ_\\j@o`AbAeiA|AkXb@uRFmOJ{i@Ci_ApEem@}FytBmReqBod@cSqFcRyF_\\oKk\\_M}\\yM{ZsO_\\yQmZoSsYcUcY_XmR}Q}QkSsL{NmLqPmLoPeL{PuKyQqK_TuIsRmH_R_JmWeImYsGiZaC}MkBaN_ByLqAyLmAwLeAcNq@yKk@iNe@_MUaNMkMBaM@yLPmM\\sL^gNhAqZ~LyrCrUgnFzCk}@y@ob@kEcjAyUwpBkQ}x@eZe}@wo@itA_q@y~@qv@{p@ks@y`@geAoVcS_D{RoBsTu@uTC}}@zB_dAb@eMP_yAdCal@fAg}AjC{DFuSh@wq@Yeo@xEq`B~AinAlB}|@~@oxAhCc{@`BwgAvAmrAxAodAvBusAnBu~@bBmfA`Bgm@zAwi@lAqqAbC_c@l@ms@k@knBtFgQ~Au[nDgZnGgSdFgUlIgSnJiYrNcNxHyPdMsP~MeQzP{U|VwR`XkY`e@gSva@iMrXsMd\\oY|t@a\\nv@kQv]gOlW}NzUaSrXqSvXoL~MqMlPcG`GiJpJcHrGwMrLqXjUsPxLsZpRsU~MyH`EoV|KmUvJoR|G{RhGoPbEmUxF}j@rJaKjAyE\\cLnAiXdBwT~@qY`@qf@a@eUiAu[sBiWcDqZyEce@eJcs@yPo_AmVch@eOyb@{N_Cu@wZeKyi@kQqZwLmk@gUkn@yWqaAgd@{a@oTwbBm~@ilA{u@wjA}v@oa@{Wo]yQkn@{WuXeJe_@gKgc@mI_a@mFmYiC}_@yAwU_@kZNqWj@m^`Bug@jGya@bIeQdEse@lNi]zMsf@fUij@jZ}bBj_A_o@l]ou@za@kZ|Mcl@jSog@bKsf@nF}]vAu}@hAah@n@u^l@oi@p@gk@`@_b@n@crBxB}_@h@sg@{Aaj@Ric@d@qq@Cik@o@a[Y}^_C_^}DoHyAy[{EqTgDaQ{Byd@c@sMZoDHkN{C\\zPTjLj@pz@Rb[`@|]~C~lA^r]J|KJbIb@jd@l@rp@F~JDjLB|Fd@leAZxs@FzMd@|c@N~Zr@hyAF`FHxGtA~jAJ`Fp@x^VvMPvJBfCb@|i@r@z|@RjVJ`OFzIJdP|@reA@tF~@zh@vAdiATdQJzHRbPRrORp\\h@ps@FlMB|IxA~qBVxUDzENvLv@p}@HjIFpHLvIjA~|@ThSn@vk@fB~bB@d@t@fs@n@zl@@p@vA|tARfRNrRP`UFjH^ld@xAbdB`@jZBzAxAdeAf@pk@n@`v@xA~cBjAluAP|MJlKJvLPjP@lBj@bj@j@vi@HzHZxUPtO\\tXx@rr@v@~o@z@pcAVnYhAzrA|@hdAvBziBj@xd@^zb@d@nl@RhTjBh{AhEfsD`@`a@jAplAFnF^~\\^l\\f@fe@p@br@aDpKk@hBaArB}@hAkFhF{R_@}n@uAgErCqJ]cEKu[{@_LWy^}@gTg@y@A{LUwEOuL]oESgJo@_PyBmLsCga@qJqLkCiG}@aEa@eIa@yGSmYG_L@qi@K?hI?l@?r]?lo@?dQ?~fA?h`@?pX?rgA?pe@?rE?|e@Brg@BlPEjdA?js@?vR@bM@dlBG`b@@ti@Bxa@?fSAjO?hV@pHdMBpCvA~E|BxRBzKyH"
}
],
"summary": {
"has_time_restrictions": false,
"has_toll": true,
"has_highway": true,
"has_ferry": false,
"min_lat": 41.684661,
"min_lon": -88.115648,
"max_lat": 41.909558,
"max_lon": -88.027976,
"time": 1772.608,
"length": 23.7686,
"cost": 2473.988
},
"status_message": "Found route between points",
"status": 0,
"units": "miles",
"language": "en-US"
}
}

View File

@@ -0,0 +1,206 @@
{
"trip": {
"locations": [
{
"type": "break",
"lat": 41.701966,
"lon": -88.086597,
"original_index": 0
},
{
"type": "break",
"lat": 41.704616,
"lon": -88.067058,
"original_index": 1
}
],
"legs": [
{
"maneuvers": [
{
"type": 1,
"instruction": "Drive north on Brandon Court.",
"verbal_succinct_transition_instruction": "Drive north. Then Turn left onto Blair Lane.",
"verbal_pre_transition_instruction": "Drive north on Brandon Court. Then Turn left onto Blair Lane.",
"verbal_post_transition_instruction": "Continue for 80 meters.",
"street_names": [
"Brandon Court"
],
"bearing_after": 359,
"time": 10.107,
"length": 0.084,
"cost": 10.846,
"begin_shape_index": 0,
"end_shape_index": 2,
"verbal_multi_cue": true,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left onto Blair Lane.",
"verbal_transition_alert_instruction": "Turn left onto Blair Lane.",
"verbal_succinct_transition_instruction": "Turn left. Then Turn right onto North Schmidt Road.",
"verbal_pre_transition_instruction": "Turn left onto Blair Lane. Then Turn right onto North Schmidt Road.",
"verbal_post_transition_instruction": "Continue for 90 meters.",
"street_names": [
"Blair Lane"
],
"bearing_before": 359,
"bearing_after": 269,
"time": 12.751,
"length": 0.09,
"cost": 20.96,
"begin_shape_index": 2,
"end_shape_index": 4,
"verbal_multi_cue": true,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 10,
"instruction": "Turn right onto North Schmidt Road.",
"verbal_transition_alert_instruction": "Turn right onto North Schmidt Road.",
"verbal_succinct_transition_instruction": "Turn right.",
"verbal_pre_transition_instruction": "Turn right onto North Schmidt Road.",
"verbal_post_transition_instruction": "Continue for 500 meters.",
"street_names": [
"North Schmidt Road"
],
"bearing_before": 269,
"bearing_after": 358,
"time": 43.917,
"length": 0.533,
"cost": 77.944,
"begin_shape_index": 4,
"end_shape_index": 17,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 10,
"instruction": "Turn right onto West Boughton Road/CH 67.",
"verbal_transition_alert_instruction": "Turn right onto West Boughton Road.",
"verbal_succinct_transition_instruction": "Turn right.",
"verbal_pre_transition_instruction": "Turn right onto West Boughton Road, CH 67.",
"verbal_post_transition_instruction": "Continue for 1.5 kilometers.",
"street_names": [
"West Boughton Road",
"CH 67"
],
"bearing_before": 359,
"bearing_after": 80,
"time": 104.058,
"length": 1.628,
"cost": 143.853,
"begin_shape_index": 17,
"end_shape_index": 48,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 10,
"instruction": "Turn right onto North Bolingbrook Drive/IL 53.",
"verbal_transition_alert_instruction": "Turn right onto North Bolingbrook Drive.",
"verbal_succinct_transition_instruction": "Turn right.",
"verbal_pre_transition_instruction": "Turn right onto North Bolingbrook Drive, IL 53.",
"verbal_post_transition_instruction": "Continue for 700 meters.",
"street_names": [
"North Bolingbrook Drive",
"IL 53"
],
"bearing_before": 67,
"bearing_after": 178,
"time": 51.743,
"length": 0.723,
"cost": 70.552,
"begin_shape_index": 48,
"end_shape_index": 62,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left.",
"verbal_transition_alert_instruction": "Turn left.",
"verbal_succinct_transition_instruction": "Turn left.",
"verbal_pre_transition_instruction": "Turn left.",
"verbal_post_transition_instruction": "Continue for 100 meters.",
"bearing_before": 178,
"bearing_after": 89,
"time": 24.024,
"length": 0.126,
"cost": 146.206,
"begin_shape_index": 62,
"end_shape_index": 66,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left.",
"verbal_transition_alert_instruction": "Turn left.",
"verbal_succinct_transition_instruction": "Turn left. Then You will arrive at your destination.",
"verbal_pre_transition_instruction": "Turn left. Then You will arrive at your destination.",
"verbal_post_transition_instruction": "Continue for 30 meters.",
"bearing_before": 88,
"bearing_after": 359,
"time": 7.643,
"length": 0.031,
"cost": 14.839,
"begin_shape_index": 66,
"end_shape_index": 68,
"verbal_multi_cue": true,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 4,
"instruction": "You have arrived at your destination.",
"verbal_transition_alert_instruction": "You will arrive at your destination.",
"verbal_pre_transition_instruction": "You have arrived at your destination.",
"bearing_before": 359,
"time": 0.0,
"length": 0.0,
"cost": 0.0,
"begin_shape_index": 68,
"end_shape_index": 68,
"travel_mode": "drive",
"travel_type": "car"
}
],
"summary": {
"has_time_restrictions": false,
"has_toll": false,
"has_highway": false,
"has_ferry": false,
"min_lat": 41.701966,
"min_lon": -88.087852,
"max_lat": 41.710795,
"max_lon": -88.06705,
"time": 254.245,
"length": 3.216,
"cost": 485.201
},
"shape": "{chpnAhck_gDki@d@yCBR|z@@rFyc@n@yn@j@yo@`AgCDwDDoo@h@gEDaZTue@`@sLJiDDkIFsEDg@wHoBsYyD_k@uBq[gCo`@iB}XQoCWqDaBaWkBeXkBmZsCy_@{D_i@gFsw@gSe~CcEer@e\\mxEyDy^gGge@wIqf@}Nsz@eGm]cA_GaDwQaBqJy@_F{I}g@sB}KqByKqLwi@qE{S~S_@hQ[jS]dIOlJQpHMvo@o@f^]bmAsApSW~j@s@pY_@jMQvr@{@G}GKyUUo_@W_^{IHcFD"
}
],
"summary": {
"has_time_restrictions": false,
"has_toll": false,
"has_highway": false,
"has_ferry": false,
"min_lat": 41.701966,
"min_lon": -88.087852,
"max_lat": 41.710795,
"max_lon": -88.06705,
"time": 254.245,
"length": 3.216,
"cost": 485.201
},
"status_message": "Found route between points",
"status": 0,
"units": "kilometers",
"language": "en-US"
}
}

View File

@@ -0,0 +1,148 @@
{
"trip": {
"locations": [
{
"type": "break",
"lat": 41.701966,
"lon": -88.086597,
"original_index": 0
},
{
"type": "break",
"lat": 41.699213,
"lon": -88.102372,
"original_index": 1
}
],
"legs": [
{
"maneuvers": [
{
"type": 1,
"instruction": "Drive north on Brandon Court.",
"verbal_succinct_transition_instruction": "Drive north. Then Turn left onto Blair Lane.",
"verbal_pre_transition_instruction": "Drive north on Brandon Court. Then Turn left onto Blair Lane.",
"verbal_post_transition_instruction": "Continue for 80 meters.",
"street_names": [
"Brandon Court"
],
"bearing_after": 359,
"time": 13.766,
"length": 0.084,
"cost": 14.791,
"begin_shape_index": 0,
"end_shape_index": 2,
"verbal_multi_cue": true,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left onto Blair Lane.",
"verbal_transition_alert_instruction": "Turn left onto Blair Lane.",
"verbal_succinct_transition_instruction": "Turn left.",
"verbal_pre_transition_instruction": "Turn left onto Blair Lane.",
"verbal_post_transition_instruction": "Continue for 90 meters.",
"street_names": [
"Blair Lane"
],
"bearing_before": 359,
"bearing_after": 269,
"time": 20.128,
"length": 0.09,
"cost": 32.463,
"begin_shape_index": 2,
"end_shape_index": 4,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 15,
"instruction": "Turn left onto North Schmidt Road.",
"verbal_transition_alert_instruction": "Turn left onto North Schmidt Road.",
"verbal_succinct_transition_instruction": "Turn left.",
"verbal_pre_transition_instruction": "Turn left onto North Schmidt Road.",
"verbal_post_transition_instruction": "Continue for 500 meters.",
"street_names": [
"North Schmidt Road"
],
"bearing_before": 269,
"bearing_after": 179,
"time": 48.162,
"length": 0.522,
"cost": 72.474,
"begin_shape_index": 4,
"end_shape_index": 11,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 10,
"instruction": "Turn right onto West Briarcliff Road.",
"verbal_transition_alert_instruction": "Turn right onto West Briarcliff Road.",
"verbal_succinct_transition_instruction": "Turn right.",
"verbal_pre_transition_instruction": "Turn right onto West Briarcliff Road.",
"verbal_post_transition_instruction": "Continue for 1.5 kilometers.",
"street_names": [
"West Briarcliff Road"
],
"bearing_before": 178,
"bearing_after": 267,
"time": 211.369,
"length": 1.278,
"cost": 252.521,
"begin_shape_index": 11,
"end_shape_index": 39,
"travel_mode": "drive",
"travel_type": "car"
},
{
"type": 4,
"instruction": "You have arrived at your destination.",
"verbal_transition_alert_instruction": "You will arrive at your destination.",
"verbal_pre_transition_instruction": "You have arrived at your destination.",
"bearing_before": 274,
"time": 0.0,
"length": 0.0,
"cost": 0.0,
"begin_shape_index": 39,
"end_shape_index": 39,
"travel_mode": "drive",
"travel_type": "car"
}
],
"summary": {
"has_time_restrictions": false,
"has_toll": false,
"has_highway": false,
"has_ferry": false,
"min_lat": 41.697911,
"min_lon": -88.102372,
"max_lat": 41.702721,
"max_lon": -88.086596,
"time": 293.426,
"length": 1.974,
"cost": 372.251
},
"shape": "{chpnAhck_gDki@d@yCBR|z@@rF~vB_C|DCzGElfAq@r_@i@|PWbc@s@HxGhC~zBjA|aAuKf|@cGrl@K`IxE|jEj@rx@B~LNhm@x@bgALjKCfLiAdQwBbOaD|McDzIiElHiIbLcMtPmD~F_EnJoDlNeE|Vu@bF]nEe@`K@lD"
}
],
"summary": {
"has_time_restrictions": false,
"has_toll": false,
"has_highway": false,
"has_ferry": false,
"min_lat": 41.697911,
"min_lon": -88.102372,
"max_lat": 41.702721,
"max_lon": -88.086596,
"time": 293.426,
"length": 1.974,
"cost": 372.251
},
"status_message": "Found route between points",
"status": 0,
"units": "kilometers",
"language": "en-US"
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""
Simple script to print mouse coordinates on Ubuntu.
Run with: python print_mouse_coords.py
Press Ctrl+C to exit.
"""
from pynput import mouse
print("Mouse coordinate printer - Press Ctrl+C to exit")
print("Click to set the top left origin")
origin: tuple[int, int] | None = None
clicks: list[tuple[int, int]] = []
def on_click(x, y, button, pressed):
global origin, clicks
if pressed: # Only on mouse down, not up
if origin is None:
origin = (x, y)
print(f"Origin set to: {x},{y}")
else:
rel_x = x - origin[0]
rel_y = y - origin[1]
clicks.append((rel_x, rel_y))
print(f"Clicks: {clicks}")
if __name__ == "__main__":
try:
# Start mouse listener
with mouse.Listener(on_click=on_click) as listener:
listener.join()
except KeyboardInterrupt:
print("\nExiting...")

View File

@@ -0,0 +1,392 @@
#!/usr/bin/env python3
import os
import sys
import shutil
import time
import pathlib
from collections import namedtuple
import pyautogui
import pywinctl
from cereal import car, log
from cereal import messaging
from cereal.messaging import PubMaster
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.selfdrive.test.helpers import with_processes
from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
from openpilot.system.updated.updated import parse_release_notes
from openpilot.system.version import terms_version, training_version
from openpilot.selfdrive.ui.tests.test_ui.nav_demo_common import (
NAV_SCENES,
build_ui_pubmaster,
publish_nav_scene,
)
AlertSize = log.SelfdriveState.AlertSize
AlertStatus = log.SelfdriveState.AlertStatus
TEST_DIR = pathlib.Path(__file__).parent
TEST_OUTPUT_DIR = TEST_DIR / "raylib_report"
SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots"
UI_DELAY = 0.5
BRANCH_NAME = "this-is-a-really-super-mega-ultra-max-extreme-ultimate-long-branch-name"
VERSION = f"0.10.1 / {BRANCH_NAME} / 7864838 / Oct 03"
# Offroad alerts to test
OFFROAD_ALERTS = ['Offroad_IsTakingSnapshot']
def put_update_params(params: Params):
params.put("UpdaterCurrentReleaseNotes", parse_release_notes(BASEDIR))
params.put("UpdaterNewReleaseNotes", parse_release_notes(BASEDIR))
params.put("UpdaterTargetBranch", BRANCH_NAME)
def setup_homescreen(click, pm: PubMaster, scroll=None):
pass
def setup_homescreen_update_available(click, pm: PubMaster, scroll=None):
params = Params()
params.put_bool("UpdateAvailable", True)
put_update_params(params)
setup_offroad_alert(click, pm)
def setup_settings(click, pm: PubMaster, scroll=None):
click(100, 100)
def close_settings(click, pm: PubMaster, scroll=None):
click(140, 120)
def setup_settings_network(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
click(278, 450)
def setup_settings_network_advanced(click, pm: PubMaster, scroll=None):
setup_settings_network(click, pm, scroll=scroll)
click(1880, 100)
def setup_settings_toggles(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
click(278, 620)
def setup_settings_software(click, pm: PubMaster, scroll=None):
put_update_params(Params())
setup_settings(click, pm)
click(278, 730)
def setup_settings_software_download(click, pm: PubMaster, scroll=None):
params = Params()
# setup_settings_software but with "DOWNLOAD" button to test long text
params.put("UpdaterState", "idle")
params.put_bool("UpdaterFetchAvailable", True)
setup_settings_software(click, pm)
def setup_settings_software_release_notes(click, pm: PubMaster, scroll=None):
setup_settings_software(click, pm, scroll=scroll)
click(588, 110) # expand description for current version
def setup_settings_software_branch_switcher(click, pm: PubMaster, scroll=None):
setup_settings_software(click, pm, scroll=scroll)
params = Params()
params.put("UpdaterAvailableBranches", f"master,nightly,release,{BRANCH_NAME}")
params.put("GitBranch", BRANCH_NAME) # should be on top
params.put("UpdaterTargetBranch", "nightly") # should be selected
click(1984, 449)
def setup_settings_developer(click, pm: PubMaster, scroll=None):
CP = car.CarParams()
CP.alphaLongitudinalAvailable = True # show alpha long control toggle
Params().put("CarParamsPersistent", CP.to_bytes())
setup_settings(click, pm)
scroll(-20, 278, 950)
click(278, 950)
def setup_keyboard(click, pm: PubMaster, scroll=None):
setup_settings_developer(click, pm, scroll=scroll)
click(1930, 470)
def setup_pair_device(click, pm: PubMaster, scroll=None):
click(1950, 800)
def setup_offroad_alert(click, pm: PubMaster, scroll=None):
put_update_params(Params())
set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C')
set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal')
for alert in OFFROAD_ALERTS:
set_offroad_alert(alert, True)
setup_settings(click, pm)
close_settings(click, pm)
def setup_confirmation_dialog(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
click(1985, 791) # reset calibration
def setup_experimental_mode_description(click, pm: PubMaster, scroll=None):
setup_settings_toggles(click, pm)
click(1200, 280) # expand description for experimental mode
def setup_openpilot_long_confirmation_dialog(click, pm: PubMaster, scroll=None):
setup_settings_developer(click, pm, scroll=scroll)
click(650, 960) # toggle IQ.Pilot longitudinal control
def setup_settings_models(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
click(278, 840)
def setup_settings_steering(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
click(278, 950)
def setup_settings_cruise(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-4, 278, 950)
click(278, 860)
def setup_settings_visuals(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-20, 278, 950)
click(278, 330)
def setup_settings_display(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-20, 278, 950)
click(278, 420)
def setup_settings_osm(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-20, 278, 950)
click(278, 520)
def setup_settings_trips(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-20, 278, 950)
click(278, 630)
def setup_settings_vehicle(click, pm: PubMaster, scroll=None):
setup_settings(click, pm)
scroll(-20, 278, 950)
click(278, 750)
def setup_onroad(click, pm: PubMaster, scroll=None):
ds = messaging.new_message('deviceState')
ds.deviceState.started = True
ps = messaging.new_message('pandaStates', 1)
ps.pandaStates[0].pandaType = log.PandaState.PandaType.dos
ps.pandaStates[0].ignitionLine = True
driverState = messaging.new_message('driverStateV2')
driverState.driverStateV2.leftDriverData.faceOrientation = [0, 0, 0]
for _ in range(5):
pm.send('deviceState', ds)
pm.send('pandaStates', ps)
pm.send('driverStateV2', driverState)
ds.clear_write_flag()
ps.clear_write_flag()
driverState.clear_write_flag()
time.sleep(0.05)
def setup_onroad_nav_demo(click, pm: PubMaster, scroll=None):
setup_onroad(click, pm)
publish_nav_scene(pm, NAV_SCENES[1])
def setup_onroad_sidebar(click, pm: PubMaster, scroll=None):
setup_onroad(click, pm)
click(100, 100) # open sidebar
def setup_onroad_alert(click, pm: PubMaster, size: log.SelfdriveState.AlertSize, text1: str, text2: str, status: log.SelfdriveState.AlertStatus):
setup_onroad(click, pm)
alert = messaging.new_message('selfdriveState')
ss = alert.selfdriveState
ss.alertSize = size
ss.alertText1 = text1
ss.alertText2 = text2
ss.alertStatus = status
for _ in range(5):
pm.send('selfdriveState', alert)
alert.clear_write_flag()
time.sleep(0.05)
def setup_onroad_small_alert(click, pm: PubMaster, scroll=None):
setup_onroad_alert(click, pm, AlertSize.small, "Small Alert", "This is a small alert", AlertStatus.normal)
def setup_onroad_medium_alert(click, pm: PubMaster, scroll=None):
setup_onroad_alert(click, pm, AlertSize.mid, "Medium Alert", "This is a medium alert", AlertStatus.userPrompt)
def setup_onroad_full_alert(click, pm: PubMaster, scroll=None):
setup_onroad_alert(click, pm, AlertSize.full, "DISENGAGE IMMEDIATELY", "Driver Distracted", AlertStatus.critical)
def setup_onroad_full_alert_multiline(click, pm: PubMaster, scroll=None):
setup_onroad_alert(click, pm, AlertSize.full, "Reverse\nGear", "", AlertStatus.normal)
def setup_onroad_full_alert_long_text(click, pm: PubMaster, scroll=None):
setup_onroad_alert(click, pm, AlertSize.full, "TAKE CONTROL IMMEDIATELY", "Calibration Invalid: Remount Device & Recalibrate", AlertStatus.userPrompt)
CASES = {
"homescreen": setup_homescreen,
"homescreen_paired": setup_homescreen,
"homescreen_prime": setup_homescreen,
"homescreen_update_available": setup_homescreen_update_available,
"homescreen_unifont": setup_homescreen,
"settings_device": setup_settings,
"settings_network": setup_settings_network,
"settings_network_advanced": setup_settings_network_advanced,
"settings_toggles": setup_settings_toggles,
"settings_software": setup_settings_software,
"settings_software_download": setup_settings_software_download,
"settings_software_release_notes": setup_settings_software_release_notes,
"settings_software_branch_switcher": setup_settings_software_branch_switcher,
"settings_developer": setup_settings_developer,
"keyboard": setup_keyboard,
"pair_device": setup_pair_device,
"offroad_alert": setup_offroad_alert,
"confirmation_dialog": setup_confirmation_dialog,
"experimental_mode_description": setup_experimental_mode_description,
"openpilot_long_confirmation_dialog": setup_openpilot_long_confirmation_dialog,
"onroad": setup_onroad,
"onroad_nav_demo": setup_onroad_nav_demo,
"onroad_sidebar": setup_onroad_sidebar,
"onroad_small_alert": setup_onroad_small_alert,
"onroad_medium_alert": setup_onroad_medium_alert,
"onroad_full_alert": setup_onroad_full_alert,
"onroad_full_alert_multiline": setup_onroad_full_alert_multiline,
"onroad_full_alert_long_text": setup_onroad_full_alert_long_text,
}
# IQ.Pilot cases
CASES.update({
"settings_models": setup_settings_models,
"settings_steering": setup_settings_steering,
"settings_cruise": setup_settings_cruise,
"settings_visuals": setup_settings_visuals,
"settings_display": setup_settings_display,
"settings_osm": setup_settings_osm,
"settings_trips": setup_settings_trips,
"settings_vehicle": setup_settings_vehicle,
})
class TestUI:
def __init__(self):
os.environ["SCALE"] = os.getenv("SCALE", "1")
os.environ["BIG"] = "1"
sys.modules["mouseinfo"] = False
def setup(self):
# Seed minimal offroad state
self.pm = build_ui_pubmaster()
ds = messaging.new_message('deviceState')
ds.deviceState.networkType = log.DeviceState.NetworkType.wifi
for _ in range(5):
self.pm.send('deviceState', ds)
ds.clear_write_flag()
time.sleep(0.05)
time.sleep(0.5)
try:
self.ui = pywinctl.getWindowsWithTitle("UI")[0]
except Exception as e:
print(f"failed to find ui window, assuming that it's in the top left (for Xvfb) {e}")
self.ui = namedtuple("bb", ["left", "top", "width", "height"])(0, 0, 2160, 1080)
def screenshot(self, name: str):
full_screenshot = pyautogui.screenshot()
cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height))
cropped.save(SCREENSHOTS_DIR / f"{name}.png")
def click(self, x: int, y: int, *args, **kwargs):
pyautogui.mouseDown(self.ui.left + x, self.ui.top + y, *args, **kwargs)
time.sleep(0.01)
pyautogui.mouseUp(self.ui.left + x, self.ui.top + y, *args, **kwargs)
def scroll(self, clicks: int, x, y, *args, **kwargs):
if clicks == 0:
return
click = -1 if clicks < 0 else 1 # -1 = down, 1 = up
for _ in range(abs(clicks)):
pyautogui.scroll(click, self.ui.left + x, self.ui.top + y, *args, **kwargs) # scroll for individual clicks since we need to delay between clicks
time.sleep(0.01) # small delay between scroll clicks to work properly
time.sleep(2) # wait for scroll to fully settle
@with_processes(["ui"])
def test_ui(self, name, setup_case):
self.setup()
time.sleep(UI_DELAY) # wait for UI to start
setup_case(self.click, self.pm, self.scroll)
self.screenshot(name)
def create_screenshots():
if TEST_OUTPUT_DIR.exists():
shutil.rmtree(TEST_OUTPUT_DIR)
SCREENSHOTS_DIR.mkdir(parents=True)
t = TestUI()
for name, setup in CASES.items():
with OpenpilotPrefix():
params = Params()
params.put("DongleId", "123456789012345")
# Set branch name
params.put("UpdaterCurrentDescription", VERSION)
params.put("UpdaterNewDescription", VERSION)
# Set terms and training version (to skip onboarding)
params.put("HasAcceptedTerms", terms_version)
params.put("CompletedTrainingVersion", training_version)
# PrimeState uses PRIME_TYPE env var (not Params('PrimeType')) to avoid clobbering stock/Connect state.
os.environ.pop("PRIME_TYPE", None)
if name == "homescreen_paired":
os.environ["PRIME_TYPE"] = "0" # NONE
elif name == "homescreen_prime":
os.environ["PRIME_TYPE"] = "2" # LITE
elif name == "homescreen_unifont":
params.put("LanguageSetting", "zh-CHT") # Traditional Chinese
t.test_ui(name, setup)
if __name__ == "__main__":
create_screenshots()

View File

@@ -0,0 +1,34 @@
<html>
<style>
.column {
float: left;
width: 50%;
padding: 5px;
}
.row::after {
content: "";
clear: both;
display: table;
}
.image {
width: 100%;
}
</style>
{% for name, (image, ref_image) in cases.items() %}
<h1>{{name}}</h1>
<div class="row">
<div class="column">
<img class="image" src="{{ image }}" />
</div>
</div>
<br>
{% endfor %}
</html>

View File

@@ -0,0 +1,20 @@
from collections import deque
from openpilot.system.ui.lib.scroll_panel2 import weighted_velocity
def test_weighted_velocity_empty():
assert weighted_velocity(deque()) == 0.0
def test_weighted_velocity_single():
assert weighted_velocity(deque([120.0])) == 120.0
def test_weighted_velocity_two_samples():
assert weighted_velocity(deque([100.0, 200.0])) == 130.0
def test_weighted_velocity_three_samples_biases_older():
velocity = weighted_velocity(deque([300.0, 180.0, 20.0]))
assert velocity == 244.0